Members
WDispatchAi
- Description:
AI供應商分派
- Source:
AI供應商分派
Example
詳見dispatchAi、dispatchAiFallback、dispatchAiWkf、dispatchOpencode、dispatchClaude、dispatchCodex、dispatchAntigravity、dispatchApiOpenaiCompat範例
adapters
- Description:
各AI供應商種類(kind)對CLI轉接器函數之對照表
本對照表為kind之唯一來源,dispatchAi以其鍵值分派,WDispatchAi以其鍵名產生KINDS, 新增供應商時僅須於此加入一個鍵值對即可
- Source:
各AI供應商種類(kind)對CLI轉接器函數之對照表
本對照表為kind之唯一來源,dispatchAi以其鍵值分派,WDispatchAi以其鍵名產生KINDS, 新增供應商時僅須於此加入一個鍵值對即可
Example
import adapters from './src/adapters.mjs'
console.log(Object.keys(adapters))
// => ['opencode', 'claude', 'codex', 'antigravity', 'api-openai-compat']
Methods
buildChain(providers, spec) → {Object}
- Description:
依providers定義表把「名稱規格」展開成dispatchAiFallback的providers陣列
- Source:
Example
import { buildChain } from './src/wkf/callAiWithFallback.mjs'
let providers = { a: { kind: 'claude' }, b: { kind: 'codex' } }
console.log(buildChain(providers, { use: 'a', fallback: ['b', 'c'] }))
// => { chain: [ { id: 'a', kind: 'claude' }, { id: 'b', kind: 'codex' } ], missing: [ 'c' ] }
Parameters:
| Name | Type | Description |
|---|---|---|
providers |
Object | 輸入定義表物件(名稱 → 條目) |
spec |
Object | 輸入名額規格物件{ use, fallback } |
Returns:
回傳物件,內含chain(條目物件陣列,id一律用名稱)與missing(查無定義之名稱字串陣列)
- Type
- Object
buildValidator(rule) → {function|null}
- Description:
建立驗證函式(規則語法同execCli之validate)
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
rule |
String | function | 輸入驗證規則字串('nonempty'、'json'、'min:100', 逗號可串接)或自訂函式 |
Returns:
回傳驗證函式,無有效規則回傳null
- Type
- function | null
(async) callAiWithFallback(prompt, optopt) → {Promise}
- Description:
呼叫一個AI名額(主模型+自帶遞補鏈),回覆經parse+check驗證後回傳結構化結果
特點: spec.use為主模型名稱、spec.fallback為遞補名稱陣列,依序展開為遞補鏈,名稱查無定義即回報錯誤(fail fast); parse+check接進遞補層之validate——非法回覆視為該家失敗而自動換下一家,不把壞結果帶回來; 預設掛防寫檔前綴(promptPrefix傳空字串可關閉); 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need cli in system PATH
import callAiWithFallback from './src/wkf/callAiWithFallback.mjs'
let providers = {
'deepseek': { kind: 'opencode', model: 'opencode/deepseek-v4-flash-free', provider: 'opencode', keys: ['sk-xxx'] },
'sonnet': { kind: 'claude', model: 'sonnet' },
}
let test = async () => {
let r = await callAiWithFallback('只回覆JSON: {"a":1}', {
providers,
spec: { use: 'deepseek', fallback: ['sonnet'] },
check: (j) => j.a === 1,
})
console.log(r.ok, r.json, r.providerId)
// => true { a: 1 } 'deepseek'
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否取得可用結果布林值)、json(解析後物件,rawText模式下為文字)、providerId(實際使用之名稱)、keyIndex、keyId、ms(總耗時毫秒)、tried(遞補嘗試歷程陣列)、error(錯誤訊息字串),本函數不會reject
- Type
- Promise
(async) callOnce(url, headers, body, timeoutMs, validator) → {Promise}
- Description:
單次HTTP呼叫(內部使用, 不含重試邏輯)
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
url |
String | 輸入完整端點網址字串 |
headers |
Object | 輸入請求標頭物件 |
body |
Object | 輸入請求本體物件 |
timeoutMs |
Number | 輸入逾時毫秒 |
validator |
function | null | 輸入驗證函式 |
Returns:
回傳Promise,resolve回傳結果物件
- Type
- Promise
defaultIntegratePrompt(candidates, optopt) → {String}
- Description:
預設整合提示詞模板:把成功候選JSON併入整合任務
- Source:
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
candidates |
Array | 輸入成功候選物件陣列 |
||
opt |
Object |
<optional> |
{}
|
輸入設定物件(取schema作為輸出格式示意),預設{} |
Returns:
回傳整合提示詞字串
- Type
- String
(async) dispatchAi(kind, prompt, optopt) → {Promise}
- Description:
依供應商種類(kind)分派至對應之CLI轉接器
三種供應商的差異(2026-08-08於本機實測確認): opencode支援逐次注入金鑰(OPENCODE_AUTH_CONTENT),故可多把金鑰輪替; claude與codex則沿用CLI既有登入狀態,無逐次金鑰概念。 故「輪替」的單位是「供應商條目」而非單純的金鑰:一個條目即一組(kind, model, 可選的key/provider), 輪到誰就用誰的CLI與模型
- Source:
Example
//need claude, codex or opencode cli in system PATH
import dispatchAi from './src/dispatchAi.mjs'
let test = async () => {
let r = await dispatchAi('claude', '請只回覆兩個字:完成', { model: 'sonnet' })
console.log(r.ok, r.stdout.trim())
// => true '完成'
let re = await dispatchAi('gemini', 'abc')
console.log(re.ok, re.error)
// => false 'unknown ai kind: "gemini" (available: opencode, claude, codex)'
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
kind |
String | 輸入供應商種類字串,可選'opencode'、'claude'、'codex' |
||
prompt |
String | 輸入提示詞字串,一律以stdin傳入子進程 |
||
opt |
Object |
<optional> |
{}
|
輸入設定物件,原樣轉傳對應轉接器,各轉接器可用設定詳見dispatchOpencode、dispatchClaude、dispatchCodex,預設{} |
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
- Type
- Promise
(async) dispatchAiFallback(prompt, optopt) → {Promise}
- Description:
依供應商清單順序自動遞補調用AI,組內多金鑰以游標輪替
特點: providers陣列順序即優先序,排前面的先用; 條目本身即該次調用之opt(除id與keys外原樣透傳對應轉接器),與dispatchAi「條目直接當opt」同一約定; 條目給予keys(多把金鑰)時以游標輪替,某把失敗自動換下一把,全數失敗才遞補下一組; 與金鑰無關之失敗(逾時/執行檔不存在/參數錯誤/輸出未過驗證/未知kind)直接整組跳過,不逐把空耗; 跨次執行僅記憶游標(經store注入持久化),不設金鑰停用清單——額度視窗形態多樣(5小時滾動/逐時/逐日), 停用會把已恢復的金鑰閒置,而重探的代價僅一次快速失敗; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need opencode, claude, codex cli in system PATH
import dispatchAiFallback from './src/dispatchAiFallback.mjs'
let test = async () => {
let r = await dispatchAiFallback('請只回覆兩個字:完成', {
providers: [
{
id: 'deepseek',
kind: 'opencode',
model: 'opencode/deepseek-v4-flash-free',
provider: 'opencode',
keys: ['sk-aaa', 'sk-bbb'], //多把金鑰, 某把失敗自動換下一把
timeoutMs: 180000,
},
{ id: 'claude', kind: 'claude', model: 'sonnet' }, //deepseek全敗時遞補
{ id: 'codex', kind: 'codex', model: 'gpt-5.6-luna', sandbox: 'read-only' },
],
budgetMs: 600000,
onEvent: (ev) => console.log(ev.type, ev.providerId, ev.keyIndex),
})
console.log(r.ok, r.providerId, r.keyIndex, r.tried.length)
// => true 'deepseek' 0 1
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串,一律以stdin傳入子進程 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,除execCli既有欄位(ok、stdout、stderr、code、error、durationMs、attempts、pid)外,追加providerId(實際使用之群組)、keyIndex(實際使用之金鑰索引,無keys時為null)、kind、model、tried(全部嘗試歷程陣列,成功時亦回傳),本函數不會reject
- Type
- Promise
dispatchAiWkf(opt) → {Object}
- Description:
建立AI工作流執行環境(工廠),注入provider定義表與共用預設後回傳綁定版API
特點: providers為名稱對dispatchAiFallback條目之定義表,之後各工作流以名稱宣告主模型與遞補鏈; defaults為共用呼叫設定,各工作流之callOpt與名額規格可逐項覆寫; 回傳之各函數皆不reject;本工廠為同步函數,providers無效時throw(設定錯誤應於啟動期即失敗)
- Source:
Example
//need cli in system PATH
import dispatchAiWkf from './src/dispatchAiWkf.mjs'
let wkf = dispatchAiWkf({
providers: {
'deepseek': { kind: 'opencode', model: 'opencode/deepseek-v4-flash-free', provider: 'opencode', keys: ['sk-xxx'] },
'sonnet': { kind: 'claude', model: 'sonnet' },
'luna': { kind: 'codex', model: 'gpt-5.6-luna' },
},
defaults: { timeoutMs: 300000 },
})
let test = async () => {
//單一名額: 主模型+遞補鏈
let r1 = await wkf.callAi('只回覆JSON: {"a":1}', { spec: { use: 'deepseek', fallback: ['sonnet'] }, check: (j) => j.a === 1 })
console.log(r1.ok, r1.json)
// => true { a: 1 }
//Fanout工作流: 多開執行+單點整合
let r2 = await wkf.runFanout({
task: '分析並只回覆JSON: {"essence":"..."}',
agents: [{ use: 'deepseek', fallback: ['sonnet'] }, { use: 'sonnet' }],
integrate: { use: 'luna' },
check: (j) => !!j.essence,
})
console.log(r2.ok, r2.integrated)
// => true true
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
opt |
Object | 輸入設定物件 Properties
|
Returns:
回傳綁定版API物件,內含callAi(單一名額呼叫)、runFanout(多開+整合)、runRolePipeline(串行角色鏈)、runFanoutPipeline(多開+整合+角色鏈)、providers(定義表原樣)
- Type
- Object
(async) dispatchAntigravity(prompt, optopt) → {Promise}
- Description:
以Google Antigravity CLI(agy)呼叫AI模型
特點: prompt作為
--print旗標之值傳遞而非stdin(agy介面如此,塞stdin會進互動模式卡住), 故prompt受命令列長度上限約束,超過30000字元回傳錯誤結果物件; model須為agy models第一欄之slug(如gemini-3.6-flash-low),注意agy錯誤訊息列出的是顯示名稱而非slug; 帶檔位之slug(-high/-medium/-low結尾)與effort同時給定且檔位不一致時agy回conflicts錯誤(一致則放行), effort需agy>=1.1.11,建議搭配不帶檔位之基礎slug(如gemini-3.1-pro)使用; 預設帶--dangerously-skip-permissions令非互動print模式不卡權限確認,可給予skipPermissions為false保留權限閘門; agy自身之--print-timeout未給時由timeoutMs推導並預留30秒緩衝,令CLI先於外層逾時; 沿用agy既有OAuth登入狀態(首次須於桌面互動模式完成登入); 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need agy cli in system PATH, and OAuth login completed
import dispatchAntigravity from './src/dispatchAntigravity.mjs'
let test = async () => {
let r = await dispatchAntigravity('請只回覆兩個字:完成', { model: 'gemini-3.6-flash-low' })
console.log(r.ok, r.stdout.trim())
// => true 完成
//基礎slug搭配effort(不可用帶檔位之slug併用effort)
let r2 = await dispatchAntigravity('請只回覆兩個字:完成', { model: 'gemini-3.1-pro', effort: 'low' })
console.log(r2.ok)
// => true
let re = await dispatchAntigravity('')
console.log(re.ok, re.error)
// => false 'prompt must be a non-empty string'
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串,作為 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
- Type
- Promise
(async) dispatchApiOpenaiCompat(prompt, optopt) → {Promise}
- Description:
以fetch直呼OpenAI相容API(chat/completions)呼叫AI模型
特點: 免安裝CLI、免預先登入,給baseURL+key+model即可呼叫(如OpenCode Zen、Agnes等OpenAI相容閘道); prompt走HTTP body,無命令列長度限制; 錯誤依HTTP狀態碼分流:4xx(429除外)為客戶端錯誤不重試,429/5xx/網路錯誤/逾時依maxRetries線性退避重試; 結果結構與逾時/驗證失敗之error字樣對齊execCli,可直接作為dispatchAi與dispatchAiFallback之kind('api-openai-compat')使用; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need network, no cli required
import dispatchApiOpenaiCompat from './src/dispatchApiOpenaiCompat.mjs'
let test = async () => {
//OpenCode Zen(即opencode CLI之自家閘道), 模型名不帶opencode/前綴
let r1 = await dispatchApiOpenaiCompat('請只回覆兩個字:完成', {
baseURL: 'https://opencode.ai/zen/v1',
key: 'sk-xxxxxx',
model: 'deepseek-v4-flash-free',
})
console.log(r1.ok, r1.stdout.trim())
// => true 完成
//Agnes
let r2 = await dispatchApiOpenaiCompat('請只回覆兩個字:完成', {
baseURL: 'https://apihub.agnes-ai.com/v1',
key: 'sk-xxxxxx',
model: 'agnes-2.0-flash',
})
console.log(r2.ok, r2.stdout.trim())
// => true 完成
let re = await dispatchApiOpenaiCompat('abc', { baseURL: 'https://opencode.ai/zen/v1', key: 'sk-bad', model: 'deepseek-v4-flash-free' })
console.log(re.ok, re.code, re.error)
// => false 401 HTTP 401
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串,作為user訊息置於HTTP body |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(回覆內容字串)、stderr(失敗時之原始回應本體)、code(HTTP狀態碼,網路錯誤與逾時為null)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
- Type
- Promise
(async) dispatchClaude(prompt, optopt) → {Promise}
- Description:
以Claude Code CLI呼叫Claude模型
特點: prompt一律走stdin而非位置參數,因摘要內文可達數萬字,當命令列參數會spawn ENAMETOOLONG; 沿用Claude Code既有登入狀態,無逐次注入API key之概念,故無key參數; 未給model時不帶
--model旗標,由CLI自行決定使用模型; 預設帶--dangerously-skip-permissions令非互動之-p模式不因權限確認而卡住, 惟prompt含不可信內容(例如待摘要之網頁)時該內容之指示亦將無權限閘門執行, 可給予skipPermissions為false保留CLI權限閘門; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need claude cli in system PATH
import dispatchClaude from './src/dispatchClaude.mjs'
let test = async () => {
let r = await dispatchClaude('請只回覆兩個字:完成', { model: 'sonnet', timeoutMs: 120000 })
console.log(r.ok, r.stdout.trim())
// => true '完成'
let re = await dispatchClaude('')
console.log(re.ok, re.error)
// => false 'prompt must be a non-empty string'
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串,一律以stdin傳入子進程 |
|||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
- Type
- Promise
(async) dispatchCodex(prompt, optopt) → {Promise}
- Description:
以OpenAI Codex CLI呼叫GPT模型
特點: prompt一律走stdin而非位置參數,因摘要內文可達數萬字,當命令列參數會spawn ENAMETOOLONG; 固定帶
--skip-git-repo-check,令非git倉庫之工作目錄亦可執行; 沿用Codex CLI既有登入狀態,無逐次注入API key之概念,故無key參數; 未給model時不帶-m旗標,由CLI自行決定使用模型; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need codex cli in system PATH
import dispatchCodex from './src/dispatchCodex.mjs'
let test = async () => {
let r = await dispatchCodex('請只回覆兩個字:完成', { model: 'gpt-5.6-luna', sandbox: 'read-only' })
console.log(r.ok, r.stdout.includes('完成'))
// => true true
let re = await dispatchCodex('abc', { exe: 'codex-not-exist' })
console.log(re.ok, re.error.includes('ENOENT'))
// => false true
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串,一律以stdin傳入子進程 |
|||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
- Type
- Promise
(async) dispatchOpencode(prompt, optopt) → {Promise}
- Description:
以opencode CLI呼叫AI模型
特點: prompt一律走stdin而非位置參數,因摘要內文可達數萬字,當命令列參數會spawn ENAMETOOLONG, 而opencode run未帶位置message時即從stdin讀取; 同時給予key與provider時,以OPENCODE_AUTH_CONTENT環境變數逐次注入金鑰, 該注入僅作用於當次子進程且不改寫auth.json,故可多把金鑰輪替並與其他程序並行; 未給key或provider時沿用CLI既有登入狀態; 使用opencode未內建之第三方provider時,須另以config給予其provider定義; 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
- Source:
Example
//need opencode cli in system PATH
import dispatchOpencode from './src/dispatchOpencode.mjs'
let test = async () => {
//沿用CLI既有登入狀態
let r1 = await dispatchOpencode('請只回覆兩個字:完成', { model: 'opencode/deepseek-v4-flash-free' })
console.log(r1.ok, r1.stdout.includes('完成'))
// => true true
//逐次注入金鑰, key與provider與model須為同一組
let r2 = await dispatchOpencode('請只回覆兩個字:完成', {
model: 'opencode/deepseek-v4-flash-free',
provider: 'opencode',
key: 'sk-xxxxxx',
})
console.log(r2.ok)
// => true
//opencode未內建之第三方provider, 須另以config給予其定義
let r3 = await dispatchOpencode('請只回覆兩個字:完成', {
model: 'agnes-ai/agnes-2.0-flash',
provider: 'agnes-ai',
key: 'sk-xxxxxx',
config: {
provider: {
'agnes-ai': {
npm: '@ai-sdk/openai-compatible',
name: 'Agnes',
options: { baseURL: 'https://apihub.agnes-ai.com/v1' },
models: { 'agnes-2.0-flash': { name: 'Agnes 2.0 Flash' } },
},
},
},
})
console.log(r3.ok)
// => true
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
prompt |
String | 輸入提示詞字串,一律以stdin傳入子進程 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
- Type
- Promise
extractJsonLoose(text) → {Object|Array|null}
- Description:
從文字中抽取第一個完整的JSON物件或陣列
特點: 先去除ANSI色碼與code fence標記後嘗試整段解析(最常見情境之最快路徑); 整段非法時自第一個
{或[起以括號配對(跳過字串與跳脫)取得第一個完整片段再解析; 僅接受物件與陣列,純量(字串/數字/布林)回傳null; 括號未閉合(輸出被截斷)或片段非法一律回傳null,不throw
- Source:
Example
import extractJsonLoose from './src/wkf/extractJsonLoose.mjs'
console.log(extractJsonLoose('{"a":1}'))
// => { a: 1 }
console.log(extractJsonLoose('說明文字\n```json\n{"a":1}\n```\n後記'))
// => { a: 1 }
console.log(extractJsonLoose('{"a":1')) //截斷
// => null
console.log(extractJsonLoose('純文字回覆'))
// => null
Parameters:
| Name | Type | Description |
|---|---|---|
text |
String | 輸入AI回覆文字字串 |
Returns:
回傳解析成功之物件或陣列,失敗回傳null
- Type
- Object | Array | null
getCliArgs(…args) → {Array}
- Description:
將各段命令列參數展平為字串陣列,並濾除非有效字串
各轉接器之參數為「固定旗標」加「可選旗標」加「額外旗標」之組合, 其中可選旗標於未給值時須整段消失(例如未給model就不可出現懸空的
--model), 故呼叫端須以「整段陣列給或不給」表達,本函數僅負責展平與濾除非有效字串,不判斷旗標配對; 過濾之必要在於Nodejs之spawn要求各參數必為字串,混入undefined或數字會直接拋出TypeError, 破壞本套件「不reject、僅以結果物件回報」之約定
- Source:
Example
import getCliArgs from './src/getCliArgs.mjs'
console.log(getCliArgs('-p', ['--model', 'sonnet']))
// => ['-p', '--model', 'sonnet']
console.log(getCliArgs('-p', null, 123, ['', '--verbose']))
// => ['-p', '--verbose']
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
args |
String | Array |
<repeatable> |
輸入參數字串或參數字串陣列,可給多個 |
Returns:
回傳展平且濾除非有效字串後之參數字串陣列
- Type
- Array
getErrorResult(error) → {Object}
- Description:
產生與execCli同結構之錯誤結果物件
本套件各dispatch函數一律不reject,參數檢核失敗時即以本函數回傳錯誤結果物件, 其欄位與wsemi之execCli回傳結構一致,故呼叫端可用同一套欄位判斷成敗, 無須區分「參數錯誤」與「CLI執行失敗」兩種來源
- Source:
Example
import getErrorResult from './src/getErrorResult.mjs'
console.log(getErrorResult('prompt must be a non-empty string'))
// => { ok: false, stdout: '', stderr: '', code: null, error: 'prompt must be a non-empty string', durationMs: 0, attempts: 0 }
console.log(getErrorResult(null).error)
// => 'unknown error'
Parameters:
| Name | Type | Description |
|---|---|---|
error |
String | 輸入錯誤訊息字串 |
Returns:
回傳結果物件,內含ok(布林值,恆為false)、stdout(空字串)、stderr(空字串)、code(null)、error(錯誤訊息字串)、durationMs(0)、attempts(0)
- Type
- Object
isKeyIndependentFail(r) → {Boolean}
- Description:
判斷失敗結果是否與「哪一把金鑰」無關(換組內金鑰必然再敗, 應整組跳過)
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
r |
Object | 輸入dispatchAi失敗結果物件 |
Returns:
回傳是否應整組跳過之布林值
- Type
- Boolean
(async) runFanout(optopt) → {Promise}
- Description:
執行Fanout工作流:多開執行與單點整合
特點: 前段各名額並行執行同一任務,各名額可指定主模型(use)與自帶遞補鏈(fallback); 後段為單一整合名額,同樣可帶遞補鏈; 個別名額失敗不中斷整輪,成功候選未達minCandidates時以首位候選為成果(integrated:false)不硬整合; 成功候選完整保留於回傳(部分接受、便於接續重試整合); 本函數不會reject
- Source:
Example
//need cli in system PATH
import runFanout from './src/wkf/runFanout.mjs'
let providers = {
'deepseek': { kind: 'opencode', model: 'opencode/deepseek-v4-flash-free', provider: 'opencode', keys: ['sk-xxx'] },
'sonnet': { kind: 'claude', model: 'sonnet' },
}
let test = async () => {
let r = await runFanout({
providers,
task: '分析並只回覆JSON: {"essence":"..."}',
agents: [
{ use: 'deepseek', fallback: ['sonnet'] },
{ use: 'sonnet' },
],
integrate: { use: 'sonnet' },
check: (j) => !!j.essence,
})
console.log(r.ok, r.integrated, r.candidates.length)
// => true true 2
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(布林值)、result(工作流成果)、integrated(是否經過整合布林值)、agents(各名額完整結果陣列)、candidates(成功候選陣列)、integrateDetail(整合呼叫完整結果)、totalMs(總耗時毫秒)、error(錯誤訊息字串),本函數不會reject
- Type
- Promise
(async) runFanoutPipeline(optopt) → {Promise}
- Description:
執行FanoutPipeline工作流(Fanout+RolePipeline):多開+整合+串行角色鏈
特點: 前段同runFanout(agents各名額可自帶fallback、integrate單點整合); 後段同runRolePipeline(stages各階段可自帶AI/fallback/提示詞),其input即前段成果; 本函數不會reject
- Source:
Example
//need cli in system PATH
import runFanoutPipeline from './src/wkf/runFanoutPipeline.mjs'
let providers = {
'sonnet': { kind: 'claude', model: 'sonnet' },
'luna': { kind: 'codex', model: 'gpt-5.6-luna' },
}
let test = async () => {
let r = await runFanoutPipeline({
providers,
task: '分析並只回覆JSON: {"essence":"..."}',
agents: [{ use: 'sonnet' }, { use: 'luna' }],
integrate: { use: 'sonnet' },
stages: [
{ id: 'audit', use: 'luna', prompt: (ctx) => `審計此稿並修訂, 只回覆同格式JSON: ${JSON.stringify(ctx.input)}` },
],
check: (j) => !!j.essence,
})
console.log(r.ok, r.A.integrated, r.B.order)
// => true true [ 'audit' ]
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(布林值)、result(工作流成果)、A(前段runFanout完整結果)、B(後段runRolePipeline完整結果)、totalMs(總耗時毫秒)、error(錯誤訊息字串),本函數不會reject
- Type
- Promise
(async) runRolePipeline(optopt) → {Promise}
- Description:
執行RolePipeline工作流:多角色串行鏈
特點: 各階段可各自指定use/fallback/prompt/check(含rawText純文字階段); 階段成果依序傳遞,最末階段成果即工作流成果; 失敗即止但已完成成果完整回傳(部分接受、便於接續重跑失敗段); 本函數不會reject
- Source:
Example
//need cli in system PATH
import runRolePipeline from './src/wkf/runRolePipeline.mjs'
let providers = {
'sonnet': { kind: 'claude', model: 'sonnet' },
'luna': { kind: 'codex', model: 'gpt-5.6-luna' },
}
let test = async () => {
let r = await runRolePipeline({
providers,
input: '原始任務',
stages: [
{ id: 'draft', use: 'sonnet', prompt: (ctx) => `就「${ctx.input}」寫初稿, 只回覆JSON: {"text":"..."}` },
{ id: 'review', use: 'luna', prompt: (ctx) => `審閱並修訂, 只回覆同格式JSON: ${JSON.stringify(ctx.prev)}` },
],
})
console.log(r.ok, r.order, r.failedStage)
// => true [ 'draft', 'review' ] null
}
await test()
.catch((err) => {
console.log(err)
})
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve回傳結果物件,內含ok(布林值)、result(最末階段成果)、stages(id對階段完整呼叫結果之物件)、results(id對階段成果之物件)、order(階段id順序陣列)、failedStage(失敗階段id,無失敗為null)、totalMs(總耗時毫秒)、error(錯誤訊息字串),本函數不會reject
- Type
- Promise