w-dispatch-ai 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/dist/w-dispatch-ai.umd.js +2 -2
- package/dist/w-dispatch-ai.umd.js.map +1 -1
- package/docs/WDispatchAi.mjs.html +4 -3
- package/docs/adapters.mjs.html +2 -2
- package/docs/castPintOr.mjs.html +109 -0
- package/docs/dfTimeoutMs.mjs.html +2 -2
- package/docs/dispatchAi.mjs.html +14 -13
- package/docs/dispatchAiFallback.mjs.html +156 -97
- package/docs/dispatchAiWkf.mjs.html +2 -2
- package/docs/dispatchAntigravity.mjs.html +10 -14
- package/docs/dispatchApiOpenaiCompat.mjs.html +50 -64
- package/docs/dispatchClaude.mjs.html +8 -13
- package/docs/dispatchCodex.mjs.html +8 -13
- package/docs/dispatchOpencode.mjs.html +8 -13
- package/docs/getCliArgs.mjs.html +2 -2
- package/docs/getErrorResult.mjs.html +12 -5
- package/docs/getErrorType.mjs.html +171 -0
- package/docs/global.html +1275 -88
- package/docs/index.html +2 -2
- package/docs/resolveProviders.mjs.html +2 -2
- package/docs/wkf_callAiWithFallback.mjs.html +14 -10
- package/docs/wkf_extractJsonLoose.mjs.html +2 -2
- package/docs/wkf_runFanout.mjs.html +8 -8
- package/docs/wkf_runFanoutPipeline.mjs.html +14 -18
- package/docs/wkf_runRolePipeline.mjs.html +5 -4
- package/package.json +1 -1
- package/src/WDispatchAi.mjs +2 -1
- package/src/castPintOr.mjs +37 -0
- package/src/dispatchAi.mjs +12 -11
- package/src/dispatchAiFallback.mjs +154 -95
- package/src/dispatchAntigravity.mjs +8 -12
- package/src/dispatchApiOpenaiCompat.mjs +48 -62
- package/src/dispatchClaude.mjs +6 -11
- package/src/dispatchCodex.mjs +6 -11
- package/src/dispatchOpencode.mjs +6 -11
- package/src/getErrorResult.mjs +10 -3
- package/src/getErrorType.mjs +99 -0
- package/src/providers.mjs +5 -0
- package/src/wkf/callAiWithFallback.mjs +12 -8
- package/src/wkf/runFanout.mjs +6 -6
- package/src/wkf/runFanoutPipeline.mjs +12 -16
- package/src/wkf/runRolePipeline.mjs +3 -2
- package/test/tools/fakeServerForApiTest.mjs +4 -1
- package/test/unit-callAiWithFallback.test.mjs +24 -0
- package/test/unit-castPintOr.test.mjs +28 -0
- package/test/unit-dispatchAiFallback.test.mjs +137 -0
- package/test/unit-dispatchApiOpenaiCompat.test.mjs +39 -0
- package/test/unit-getErrorResult.test.mjs +13 -1
- package/test/unit-getErrorType.test.mjs +36 -0
- package/test/unit-providers.test.mjs +24 -0
- package/test/unit-runFanout.test.mjs +14 -0
- package/test/unit-runRolePipeline.test.mjs +12 -0
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -85,7 +85,7 @@ export default dfTimeoutMs
|
|
|
85
85
|
<br class="clear">
|
|
86
86
|
|
|
87
87
|
<footer>
|
|
88
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
88
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 15 2026 11:56:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
89
89
|
</footer>
|
|
90
90
|
|
|
91
91
|
<script>prettyPrint();</script>
|
package/docs/dispatchAi.mjs.html
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -53,18 +53,19 @@ import getErrorResult from './getErrorResult.mjs'
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* 依供應商種類(kind)
|
|
56
|
+
* 依供應商種類(kind)分派至對應之轉接器
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* kind清單以adapters.mjs對照表為唯一來源(目前為'opencode'、'claude'、'codex'、'antigravity'、
|
|
59
|
+
* 'api-openai-compat',CLI或API之選型判準見adapters.mjs檔頭)。
|
|
60
|
+
* 各家金鑰模式不同(2026-08-08起於本機實測確認):opencode與api-openai-compat支援逐次注入金鑰,
|
|
61
|
+
* 故可多把金鑰輪替;claude/codex/antigravity沿用CLI既有登入狀態,無逐次金鑰概念。
|
|
61
62
|
* 故「輪替」的單位是「供應商條目」而非單純的金鑰:一個條目即一組(kind, model, 可選的key/provider),
|
|
62
|
-
*
|
|
63
|
+
* 輪到誰就用誰的轉接器與模型
|
|
63
64
|
*
|
|
64
|
-
* @param {String} kind
|
|
65
|
-
* @param {String} prompt
|
|
66
|
-
* @param {Object} [opt={}] 輸入設定物件,原樣轉傳對應轉接器,各轉接器可用設定詳見dispatchOpencode、dispatchClaude、dispatchCodex,預設{}
|
|
67
|
-
* @returns {Promise} 回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
|
|
65
|
+
* @param {String} kind 輸入供應商種類字串,須為adapters.mjs對照表之鍵名,目前可選'opencode'、'claude'、'codex'、'antigravity'、'api-openai-compat'
|
|
66
|
+
* @param {String} prompt 輸入提示詞字串
|
|
67
|
+
* @param {Object} [opt={}] 輸入設定物件,原樣轉傳對應轉接器,各轉接器可用設定詳見dispatchOpencode、dispatchClaude、dispatchCodex、dispatchAntigravity、dispatchApiOpenaiCompat,預設{}
|
|
68
|
+
* @returns {Promise} 回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、errorType(僅失敗時,機器可讀錯誤類別字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
|
|
68
69
|
* @example
|
|
69
70
|
* //need claude, codex or opencode cli in system PATH
|
|
70
71
|
*
|
|
@@ -77,8 +78,8 @@ import getErrorResult from './getErrorResult.mjs'
|
|
|
77
78
|
* // => true '完成'
|
|
78
79
|
*
|
|
79
80
|
* let re = await dispatchAi('gemini', 'abc')
|
|
80
|
-
* console.log(re.ok, re.error)
|
|
81
|
-
* // => false
|
|
81
|
+
* console.log(re.ok, re.error.indexOf('unknown ai kind: "gemini"') === 0)
|
|
82
|
+
* // => false true
|
|
82
83
|
*
|
|
83
84
|
* }
|
|
84
85
|
* await test()
|
|
@@ -120,7 +121,7 @@ export default dispatchAi
|
|
|
120
121
|
<br class="clear">
|
|
121
122
|
|
|
122
123
|
<footer>
|
|
123
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
124
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 15 2026 11:56:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
124
125
|
</footer>
|
|
125
126
|
|
|
126
127
|
<script>prettyPrint();</script>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -55,6 +55,7 @@ import ispint from 'wsemi/src/ispint.mjs'
|
|
|
55
55
|
import cint from 'wsemi/src/cint.mjs'
|
|
56
56
|
import dispatchAi from './dispatchAi.mjs'
|
|
57
57
|
import getErrorResult from './getErrorResult.mjs'
|
|
58
|
+
import castPintOr from './castPintOr.mjs'
|
|
58
59
|
import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
59
60
|
|
|
60
61
|
|
|
@@ -79,9 +80,23 @@ import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
|
79
80
|
// (使用端實測: 一次107秒的多階段請求中72秒耗在重複踩同一組429, 啟用冷卻後降至15秒)。
|
|
80
81
|
// 設計與「金鑰停用清單」(已否決)的關鍵差異: 以「條目」為單位、短視窗、且「只降序不移除」
|
|
81
82
|
// ——冷卻中的條目移到鏈尾而非移除, 前面全敗時照樣會被嘗試, 故不存在把已恢復服務冰住的問題;
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
83
|
+
// 任一次成功立即解除。內建觸發限於限流(HTTP 429, 僅api-openai-compat可靠偵測; CLI類之
|
|
84
|
+
// 限流埋在stderr文字中, 各家字樣不同且隨版本漂移, 本套件不維護簽章表)與逾時(TIMEOUT開頭,
|
|
85
|
+
// 各kind皆可)兩類——其餘失敗已有換金鑰換家機制處理, 納入反而誤傷。
|
|
86
|
+
// CLI類限流之偵測採依賴注入: 呼叫端於實測中觀察到穩定字樣時, 以coolDetect(r)=>Boolean
|
|
87
|
+
// 自行判定(收到完整失敗結果含stderr), 命中即視同冷卻觸發——簽章表由觀察到字樣的呼叫端
|
|
88
|
+
// 維護, 漏判僅退回現狀(每階段重探一次), 誤判也只是降尾非移除, 兩邊代價都有上限。
|
|
89
|
+
// 狀態存於state.cooling, 與cursors同走store持久化。
|
|
90
|
+
//
|
|
91
|
+
// 【中止(shouldStop)】呼叫端(如server於客戶端斷線後)可注入shouldStop()=>Boolean,
|
|
92
|
+
// 於「每次嘗試之間」檢查, true即停止遞補回報ABORTED——把「斷線後仍空耗整條鏈」
|
|
93
|
+
// 縮成「至多再耗當前這一家」。檢查點只此一處: 工作流各層經omit轉傳自動獲得,
|
|
94
|
+
// 中止後每個後續呼叫進門即回ABORTED, 整條工作流自然快速收束, 不需逐層實作。
|
|
95
|
+
// 不中止進行中之嘗試(不殺子進程/不斷開請求), 此為已知設計取捨(避免侵入execCli層)。
|
|
96
|
+
//
|
|
97
|
+
// 【meta保留鍵】「剔除自用鍵後原樣轉傳」令條目即調校點, 但呼叫端放進條目/opt的任何
|
|
98
|
+
// 自有欄位都會被靜默轉傳——保留meta一鍵保證永不轉傳, 呼叫端要掛分類/標籤/註記
|
|
99
|
+
// 一律放meta, 與轉傳機制永久絕緣(工作流各層之規格物件同此約定)。
|
|
85
100
|
//
|
|
86
101
|
// 【時間預算】budgetMs限制整輪遞補的總時長, 剩餘預算會壓進每次呼叫的timeoutMs,
|
|
87
102
|
// 防止多家連續卡逾時而撞破外部排程的執行上限。
|
|
@@ -116,11 +131,12 @@ import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
|
116
131
|
|
|
117
132
|
|
|
118
133
|
//fallback層自用之設定鍵, 其餘鍵作為各attempt之共用預設原樣轉傳
|
|
119
|
-
|
|
134
|
+
//meta為保留鍵: 呼叫端掛自有資訊(分類/標籤/註記)用, 保證永不轉傳(見檔頭【meta保留鍵】)
|
|
135
|
+
let FALLBACK_KEYS = ['providers', 'budgetMs', 'minAttemptMs', 'cooldownMs', 'shouldStop', 'coolDetect', 'store', 'onEvent', 'meta']
|
|
120
136
|
|
|
121
137
|
|
|
122
138
|
//providers條目自用之設定鍵, 其餘鍵(含kind)即該條目之opt原樣轉傳對應轉接器
|
|
123
|
-
let ENTRY_KEYS = ['id', 'keys']
|
|
139
|
+
let ENTRY_KEYS = ['id', 'keys', 'meta']
|
|
124
140
|
|
|
125
141
|
|
|
126
142
|
//預設值
|
|
@@ -132,6 +148,79 @@ let DEFAULT_TIMEOUT_MS = dfTimeoutMs //全套件統一預設300000
|
|
|
132
148
|
let memoryState = { cursors: {} }
|
|
133
149
|
|
|
134
150
|
|
|
151
|
+
/**
|
|
152
|
+
* 初始化游標與冷卻狀態(store有效即載入持久化狀態, 否則用行程內記憶體)
|
|
153
|
+
*
|
|
154
|
+
* @param {Object} store 輸入狀態持久化物件{get,set},無效代表用行程內記憶體
|
|
155
|
+
* @returns {Object} 回傳物件,內含state(狀態物件,保證有cursors與cooling)與saveState(寫回函數,store無效或寫入失敗皆靜默)
|
|
156
|
+
*/
|
|
157
|
+
function initState(store) {
|
|
158
|
+
let useStore = isobj(store) && isfun(store.get) && isfun(store.set)
|
|
159
|
+
let state = null
|
|
160
|
+
if (useStore) {
|
|
161
|
+
try {
|
|
162
|
+
state = store.get()
|
|
163
|
+
}
|
|
164
|
+
catch {}
|
|
165
|
+
}
|
|
166
|
+
if (!isobj(state)) {
|
|
167
|
+
state = useStore ? { cursors: {} } : memoryState
|
|
168
|
+
}
|
|
169
|
+
if (!isobj(state.cursors)) {
|
|
170
|
+
state.cursors = {}
|
|
171
|
+
}
|
|
172
|
+
if (!isobj(state.cooling)) {
|
|
173
|
+
state.cooling = {}
|
|
174
|
+
}
|
|
175
|
+
let saveState = () => {
|
|
176
|
+
if (useStore) {
|
|
177
|
+
try {
|
|
178
|
+
store.set(state)
|
|
179
|
+
}
|
|
180
|
+
catch {}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { state, saveState }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 依冷卻狀態重排providers:冷卻中的條目「只降序不移除」——移到鏈尾, 前面全敗時仍會被嘗試,
|
|
189
|
+
* 故不存在把已恢復服務冰住的問題(此為與「金鑰停用清單」的關鍵差異, 後者已被否決)。
|
|
190
|
+
* 僅追蹤有明給id之條目(索引式id會因重排而錯位); 過期紀錄順手清除並寫回
|
|
191
|
+
*
|
|
192
|
+
* @param {Array} providers 輸入供應商條目陣列
|
|
193
|
+
* @param {Object} state 輸入狀態物件(取其cooling)
|
|
194
|
+
* @param {Number} cooldownMs 輸入冷卻視窗毫秒正整數
|
|
195
|
+
* @param {Function} saveState 輸入狀態寫回函數
|
|
196
|
+
* @returns {Array} 回傳重排後之條目陣列(active在前, 冷卻中殿後, 各自保持原相對順序)
|
|
197
|
+
*/
|
|
198
|
+
function reorderByCooling(providers, state, cooldownMs, saveState) {
|
|
199
|
+
let now = Date.now()
|
|
200
|
+
let act = []
|
|
201
|
+
let cool = []
|
|
202
|
+
let dirty = false
|
|
203
|
+
for (let p of providers) {
|
|
204
|
+
let pid = get(p, 'id', null)
|
|
205
|
+
let ts = isestr(pid) ? get(state.cooling, pid, null) : null
|
|
206
|
+
if (ispint(ts) && (now - ts) < cooldownMs) {
|
|
207
|
+
cool.push(p)
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
if (isestr(pid) && state.cooling[pid] !== undefined) {
|
|
211
|
+
delete state.cooling[pid] //冷卻已過期, 清除
|
|
212
|
+
dirty = true
|
|
213
|
+
}
|
|
214
|
+
act.push(p)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (dirty) {
|
|
218
|
+
saveState()
|
|
219
|
+
}
|
|
220
|
+
return [...act, ...cool]
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
135
224
|
/**
|
|
136
225
|
* 判斷失敗結果是否與「哪一把金鑰」無關(換組內金鑰必然再敗, 應整組跳過)
|
|
137
226
|
*
|
|
@@ -193,15 +282,19 @@ function isKeyIndependentFail(r) {
|
|
|
193
282
|
* @param {Array} opt.providers 輸入供應商條目物件陣列,順序即優先序。各條目除下列鍵外,其餘鍵(kind、model、exe、provider、config、sandbox、timeoutMs等)即該條目之opt原樣透傳對應轉接器
|
|
194
283
|
* @param {String} [opt.providers[].id=條目索引字串] 輸入群組識別字串,游標以此為鍵、亦為日誌標籤,本套件不解讀其內容。須區分到「模型」而非只到「廠商」(如'claude:sonnet'而非'claude'),同一模型經不同路徑取得時須帶上路徑(如'poolside:laguna-s-2.1'與'or:poolside/laguna-s-2.1:free'),且務必唯一。省略時回退為陣列索引字串——索引是位置不是身分,日後插入條目會令後續條目繼承他人游標進度,故正式設定一律明給。詳見本檔檔頭之id設計規則
|
|
195
284
|
* @param {Array} [opt.providers[].keys=[]] 輸入同一服務之多把API key字串陣列,逐次注入輪替(kind為opencode時須同時於條目給予provider),省略代表沿用CLI既有登入狀態之單一虛擬金鑰
|
|
285
|
+
* @param {*} [opt.providers[].meta=undefined] 輸入呼叫端自有資訊(分類、標籤、註記),保留鍵保證永不轉傳對應轉接器——條目其餘鍵一律原樣轉傳,自有欄位放此鍵可與轉傳機制永久絕緣,預設undefined
|
|
196
286
|
* @param {Number} [opt.budgetMs=null] 輸入整輪遞補之時間上限毫秒正整數,剩餘預算會壓進每次呼叫之timeoutMs,預設null代表不限
|
|
197
287
|
* @param {Number} [opt.minAttemptMs=20000] 輸入單次嘗試之最低剩餘預算毫秒正整數,剩餘低於此值即停止嘗試回報budget exhausted,預設20000
|
|
198
288
|
* @param {Object} [opt.store=null] 輸入狀態持久化物件{get:()=>state,set:(state)=>{}},state內含cursors(逐群組游標)與cooling(供應商冷卻時間戳,僅cooldownMs>0時使用),省略代表用行程內記憶體(跨呼叫有效,重啟歸零)。假定單行程序列調用,並行請自行加鎖
|
|
199
|
-
* @param {Number} [opt.cooldownMs=0] 輸入供應商冷卻視窗毫秒非負整數,>0啟用:條目(限有明給id者)遭遇限流(HTTP 429,僅api
|
|
200
|
-
* @param {Function} [opt.
|
|
289
|
+
* @param {Number} [opt.cooldownMs=0] 輸入供應商冷卻視窗毫秒非負整數,>0啟用:條目(限有明給id者)遭遇限流(HTTP 429,僅api類可偵測;CLI類可經coolDetect注入判定)或逾時(TIMEOUT開頭)後,於冷卻視窗內之後續呼叫中被移至鏈尾——只降序不移除,前面全敗時仍會被嘗試,任一次成功立即解除;注意啟用時「providers順序即優先序」會被暫時重排,此即本機制之目的;預設0代表不啟用
|
|
290
|
+
* @param {Function} [opt.coolDetect=null] 輸入冷卻觸發判定函數(r)=>Boolean,收完整失敗結果物件(含stdout、stderr、code、error),回傳true即視同冷卻觸發(內建429/TIMEOUT觸發不受影響)——CLI類限流埋在stderr且各家字樣不同,簽章表由觀察到字樣的呼叫端維護,如(r)=>/FreeUsageLimitError/i.test(r.stderr||'');僅cooldownMs>0時有效,回調拋出例外視同false,預設null
|
|
291
|
+
* @param {Function} [opt.shouldStop=null] 輸入中止判定函數()=>Boolean,於每次嘗試之間檢查,回傳true即停止遞補回報ABORTED(不中止進行中之嘗試)——供呼叫端於成果已無人接收時(如客戶端斷線)止損;經工作流層原樣轉傳,中止後各後續呼叫進門即回ABORTED令整條工作流快速收束;回調拋出例外視同false,預設null
|
|
292
|
+
* @param {*} [opt.meta=undefined] 輸入呼叫端自有資訊,保留鍵保證永不轉傳各轉接器,預設undefined
|
|
293
|
+
* @param {Function} [opt.onEvent=null] 輸入事件回調函數(ev)=>{},ev.type可為'try'、'ok'、'next-key'、'skip-group'、'budget-out'、'aborted'、'cooled'(冷卻觸發,帶error與cooldownMs,僅cooldownMs>0時出現);失敗事件(next-key/skip-group)另帶errorType、stdout(被拒回覆)與stderr(錯誤輸出)供診斷,後兩者於失敗路徑已由轉接器截斷;回調拋出例外不影響主流程,預設null
|
|
201
294
|
* @param {Number} [opt.timeoutMs=300000] 輸入各attempt共用之逾時毫秒正整數,條目可覆寫,全套件統一預設300000
|
|
202
295
|
* @param {String|Function} [opt.validate=undefined] 輸入各attempt共用之stdout驗證規則,條目可覆寫,預設undefined
|
|
203
296
|
* @param {Number} [opt.maxRetries=0] 輸入各attempt共用之同家重試次數非負整數,韌性建議交給換家而非重試同一家,預設0
|
|
204
|
-
* @returns {Promise} 回傳Promise,resolve回傳結果物件,除execCli既有欄位(ok、stdout、stderr、code、error、durationMs、attempts、pid)外,追加providerId(實際使用之群組)、keyIndex(實際使用之金鑰索引,無keys時為null)、kind、model、tried(全部嘗試歷程陣列,成功時亦回傳;失敗項含stdout與stderr供診斷被拒原因)
|
|
297
|
+
* @returns {Promise} 回傳Promise,resolve回傳結果物件,除execCli既有欄位(ok、stdout、stderr、code、error、durationMs、attempts、pid)外,追加providerId(實際使用之群組)、keyIndex(實際使用之金鑰索引,無keys時為null)、kind、model、tried(全部嘗試歷程陣列,成功時亦回傳;失敗項含errorType、stdout與stderr供診斷被拒原因);失敗結果帶機器可讀之errorType(一覽見getErrorType.mjs檔頭);api類轉接器提供usage(token用量)時原樣流出於結果與tried各項,CLI類無此欄;本函數不會reject
|
|
205
298
|
* @example
|
|
206
299
|
* //need opencode, claude, codex cli in system PATH
|
|
207
300
|
*
|
|
@@ -258,87 +351,44 @@ async function dispatchAiFallback(prompt, opt = {}) {
|
|
|
258
351
|
}
|
|
259
352
|
|
|
260
353
|
//budgetMs, 無效視為不限
|
|
261
|
-
let budgetMs = get(opt, 'budgetMs', null)
|
|
262
|
-
if (!ispint(budgetMs)) {
|
|
263
|
-
budgetMs = null
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
budgetMs = cint(budgetMs)
|
|
267
|
-
}
|
|
354
|
+
let budgetMs = castPintOr(get(opt, 'budgetMs', null), null)
|
|
268
355
|
let deadline = (budgetMs === null) ? null : Date.now() + budgetMs
|
|
269
356
|
|
|
270
357
|
//minAttemptMs, 無效回退預設20000
|
|
271
|
-
let minAttemptMs = get(opt, 'minAttemptMs', null)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
358
|
+
let minAttemptMs = castPintOr(get(opt, 'minAttemptMs', null), DEFAULT_MIN_ATTEMPT_MS)
|
|
359
|
+
|
|
360
|
+
//state與saveState, store須同時具get與set函數才視為有效, 否則用行程內記憶體
|
|
361
|
+
let { state, saveState } = initState(get(opt, 'store', null))
|
|
362
|
+
|
|
363
|
+
//cooldownMs, 無效視為0=不啟用(現行行為零改變)
|
|
364
|
+
let cooldownMs = castPintOr(get(opt, 'cooldownMs', null), 0)
|
|
365
|
+
|
|
366
|
+
//coolDetect, 冷卻觸發之注入判定(CLI類限流簽章由呼叫端維護, 見檔頭), 僅cooldownMs>0時有意義
|
|
367
|
+
let coolDetect = get(opt, 'coolDetect', null)
|
|
368
|
+
if (!isfun(coolDetect)) {
|
|
369
|
+
coolDetect = null
|
|
277
370
|
}
|
|
278
371
|
|
|
279
|
-
//
|
|
280
|
-
let
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
372
|
+
//shouldStop, 中止判定, 於每次嘗試之間檢查; 回調拋出例外視同false(不中止), 不得中斷主流程
|
|
373
|
+
let shouldStop = get(opt, 'shouldStop', null)
|
|
374
|
+
if (!isfun(shouldStop)) {
|
|
375
|
+
shouldStop = null
|
|
376
|
+
}
|
|
377
|
+
let stopRequested = () => {
|
|
378
|
+
if (shouldStop === null) {
|
|
379
|
+
return false
|
|
380
|
+
}
|
|
284
381
|
try {
|
|
285
|
-
|
|
382
|
+
return shouldStop() === true
|
|
286
383
|
}
|
|
287
|
-
catch {
|
|
288
|
-
|
|
289
|
-
if (!isobj(state)) {
|
|
290
|
-
state = useStore ? { cursors: {} } : memoryState
|
|
291
|
-
}
|
|
292
|
-
if (!isobj(state.cursors)) {
|
|
293
|
-
state.cursors = {}
|
|
294
|
-
}
|
|
295
|
-
if (!isobj(state.cooling)) {
|
|
296
|
-
state.cooling = {}
|
|
297
|
-
}
|
|
298
|
-
let saveState = () => {
|
|
299
|
-
if (useStore) {
|
|
300
|
-
try {
|
|
301
|
-
store.set(state)
|
|
302
|
-
}
|
|
303
|
-
catch {}
|
|
384
|
+
catch {
|
|
385
|
+
return false
|
|
304
386
|
}
|
|
305
387
|
}
|
|
306
388
|
|
|
307
|
-
|
|
308
|
-
let cooldownMs = get(opt, 'cooldownMs', null)
|
|
309
|
-
if (!ispint(cooldownMs)) {
|
|
310
|
-
cooldownMs = 0
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
cooldownMs = cint(cooldownMs)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
//供應商冷卻: 冷卻中的條目「只降序不移除」——移到鏈尾, 前面全敗時仍會被嘗試,
|
|
317
|
-
//故不存在把已恢復服務冰住的問題(此為與「金鑰停用清單」的關鍵差異, 後者已被否決)。
|
|
318
|
-
//僅追蹤有明給id之條目(索引式id會因重排而錯位); 過期項順手清除
|
|
389
|
+
//供應商冷卻: 冷卻中的條目降至鏈尾(細節見reorderByCooling)
|
|
319
390
|
if (cooldownMs > 0) {
|
|
320
|
-
|
|
321
|
-
let act = []
|
|
322
|
-
let cool = []
|
|
323
|
-
let dirty = false
|
|
324
|
-
for (let p of providers) {
|
|
325
|
-
let pid = get(p, 'id', null)
|
|
326
|
-
let ts = isestr(pid) ? get(state.cooling, pid, null) : null
|
|
327
|
-
if (ispint(ts) && (now - ts) < cooldownMs) {
|
|
328
|
-
cool.push(p)
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
if (isestr(pid) && state.cooling[pid] !== undefined) {
|
|
332
|
-
delete state.cooling[pid] //冷卻已過期, 清除
|
|
333
|
-
dirty = true
|
|
334
|
-
}
|
|
335
|
-
act.push(p)
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
providers = [...act, ...cool]
|
|
339
|
-
if (dirty) {
|
|
340
|
-
saveState()
|
|
341
|
-
}
|
|
391
|
+
providers = reorderByCooling(providers, state, cooldownMs, saveState)
|
|
342
392
|
}
|
|
343
393
|
|
|
344
394
|
//onEvent, 回調拋出例外不得中斷主流程
|
|
@@ -394,6 +444,13 @@ async function dispatchAiFallback(prompt, opt = {}) {
|
|
|
394
444
|
let skipGroup = false
|
|
395
445
|
for (let a = 0; a < nAttempts && !skipGroup; a++) {
|
|
396
446
|
|
|
447
|
+
//中止檢查(嘗試邊界): 成果已無人接收時止損, 不中止進行中之嘗試(見檔頭【中止】)
|
|
448
|
+
if (stopRequested()) {
|
|
449
|
+
emit({ type: 'aborted', providerId: id, keyIndex: null, keyId: id })
|
|
450
|
+
tried.push({ providerId: id, keyIndex: null, keyId: id, outcome: 'aborted' })
|
|
451
|
+
return { ...getErrorResult('ABORTED', 'aborted'), tried }
|
|
452
|
+
}
|
|
453
|
+
|
|
397
454
|
//keyIndex, 無keys時為null
|
|
398
455
|
let keyIndex = (nk > 0) ? (cursor + a) % nk : null
|
|
399
456
|
let keyId = (keyIndex === null) ? id : `${id}#${keyIndex}`
|
|
@@ -405,13 +462,7 @@ async function dispatchAiFallback(prompt, opt = {}) {
|
|
|
405
462
|
}
|
|
406
463
|
|
|
407
464
|
//有效timeout, 有預算時以剩餘預算封頂
|
|
408
|
-
let effTimeout = get(attemptOpt, 'timeoutMs', null)
|
|
409
|
-
if (!ispint(effTimeout)) {
|
|
410
|
-
effTimeout = DEFAULT_TIMEOUT_MS
|
|
411
|
-
}
|
|
412
|
-
else {
|
|
413
|
-
effTimeout = cint(effTimeout)
|
|
414
|
-
}
|
|
465
|
+
let effTimeout = castPintOr(get(attemptOpt, 'timeoutMs', null), DEFAULT_TIMEOUT_MS)
|
|
415
466
|
if (deadline !== null) {
|
|
416
467
|
let remaining = deadline - Date.now()
|
|
417
468
|
|
|
@@ -419,7 +470,7 @@ async function dispatchAiFallback(prompt, opt = {}) {
|
|
|
419
470
|
if (remaining < minAttemptMs) {
|
|
420
471
|
emit({ type: 'budget-out', providerId: id, keyIndex, keyId, remainingMs: remaining })
|
|
421
472
|
tried.push({ providerId: id, keyIndex, keyId, outcome: 'budget-out' })
|
|
422
|
-
return { ...getErrorResult('budget exhausted'), tried }
|
|
473
|
+
return { ...getErrorResult('budget exhausted', 'budget'), tried }
|
|
423
474
|
}
|
|
424
475
|
effTimeout = Math.min(effTimeout, remaining)
|
|
425
476
|
}
|
|
@@ -440,20 +491,28 @@ async function dispatchAiFallback(prompt, opt = {}) {
|
|
|
440
491
|
saveState()
|
|
441
492
|
}
|
|
442
493
|
emit({ type: 'ok', providerId: id, keyIndex, keyId, durationMs: r.durationMs })
|
|
443
|
-
tried.push({ providerId: id, keyIndex, keyId, outcome: 'ok', durationMs: r.durationMs })
|
|
494
|
+
tried.push({ providerId: id, keyIndex, keyId, outcome: 'ok', durationMs: r.durationMs, ...(r.usage !== undefined ? { usage: r.usage } : {}) })
|
|
444
495
|
return { ...r, providerId: id, keyIndex, kind, model, tried }
|
|
445
496
|
}
|
|
446
497
|
|
|
447
498
|
//失敗分流
|
|
448
499
|
lastResult = r
|
|
449
500
|
lastMeta = { providerId: id, keyIndex, kind, model }
|
|
450
|
-
//冷卻觸發:
|
|
451
|
-
//其餘失敗(金鑰無效/服務端錯誤)已有換金鑰換家機制處理,
|
|
501
|
+
//冷卻觸發: 內建為限流(HTTP 429, 僅api類可偵測)與逾時(TIMEOUT開頭, CLI與api皆可)——
|
|
502
|
+
//其餘失敗(金鑰無效/服務端錯誤)已有換金鑰換家機制處理, 納入冷卻反而誤傷;
|
|
503
|
+
//CLI類限流簽章經coolDetect注入判定(呼叫端維護, 見檔頭), 拋出例外視同false
|
|
452
504
|
if (cooldownMs > 0 && idExplicit) {
|
|
453
505
|
let isCoolTrigger = (r.code === 429) || (isestr(r.error) && r.error.indexOf('TIMEOUT') === 0)
|
|
506
|
+
if (!isCoolTrigger && coolDetect !== null) {
|
|
507
|
+
try {
|
|
508
|
+
isCoolTrigger = coolDetect(r) === true
|
|
509
|
+
}
|
|
510
|
+
catch {}
|
|
511
|
+
}
|
|
454
512
|
if (isCoolTrigger) {
|
|
455
513
|
state.cooling[id] = Date.now()
|
|
456
514
|
saveState()
|
|
515
|
+
emit({ type: 'cooled', providerId: id, keyIndex, keyId, error: r.error, cooldownMs })
|
|
457
516
|
}
|
|
458
517
|
}
|
|
459
518
|
|
|
@@ -462,22 +521,22 @@ async function dispatchAiFallback(prompt, opt = {}) {
|
|
|
462
521
|
if (isKeyIndependentFail(r)) {
|
|
463
522
|
|
|
464
523
|
//與金鑰無關, 整組跳過
|
|
465
|
-
emit({ type: 'skip-group', providerId: id, keyIndex, keyId, error: r.error, stdout: r.stdout, stderr: r.stderr })
|
|
466
|
-
tried.push({ providerId: id, keyIndex, keyId, outcome: 'skip-group', error: r.error, stdout: r.stdout, stderr: r.stderr, durationMs: r.durationMs })
|
|
524
|
+
emit({ type: 'skip-group', providerId: id, keyIndex, keyId, error: r.error, errorType: r.errorType, stdout: r.stdout, stderr: r.stderr })
|
|
525
|
+
tried.push({ providerId: id, keyIndex, keyId, outcome: 'skip-group', error: r.error, errorType: r.errorType, stdout: r.stdout, stderr: r.stderr, durationMs: r.durationMs, ...(r.usage !== undefined ? { usage: r.usage } : {}) })
|
|
467
526
|
skipGroup = true
|
|
468
527
|
}
|
|
469
528
|
else {
|
|
470
529
|
|
|
471
530
|
//其餘(含額度上限/金鑰無效/未分類), 換組內下一把, 不記憶不停用
|
|
472
|
-
emit({ type: 'next-key', providerId: id, keyIndex, keyId, error: r.error, stdout: r.stdout, stderr: r.stderr })
|
|
473
|
-
tried.push({ providerId: id, keyIndex, keyId, outcome: 'next-key', error: r.error, stdout: r.stdout, stderr: r.stderr, durationMs: r.durationMs })
|
|
531
|
+
emit({ type: 'next-key', providerId: id, keyIndex, keyId, error: r.error, errorType: r.errorType, stdout: r.stdout, stderr: r.stderr })
|
|
532
|
+
tried.push({ providerId: id, keyIndex, keyId, outcome: 'next-key', error: r.error, errorType: r.errorType, stdout: r.stdout, stderr: r.stderr, durationMs: r.durationMs, ...(r.usage !== undefined ? { usage: r.usage } : {}) })
|
|
474
533
|
}
|
|
475
534
|
|
|
476
535
|
}
|
|
477
536
|
}
|
|
478
537
|
|
|
479
|
-
//全數失敗,
|
|
480
|
-
let r = lastResult || getErrorResult('all providers failed')
|
|
538
|
+
//全數失敗, 回傳最後一筆失敗結果(含其errorType)與完整歷程
|
|
539
|
+
let r = lastResult || getErrorResult('all providers failed', 'exec')
|
|
481
540
|
return { ...r, ...(lastMeta || {}), tried }
|
|
482
541
|
}
|
|
483
542
|
|
|
@@ -497,7 +556,7 @@ export default dispatchAiFallback
|
|
|
497
556
|
<br class="clear">
|
|
498
557
|
|
|
499
558
|
<footer>
|
|
500
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
559
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 15 2026 11:56:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
501
560
|
</footer>
|
|
502
561
|
|
|
503
562
|
<script>prettyPrint();</script>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -193,7 +193,7 @@ export default dispatchAiWkf
|
|
|
193
193
|
<br class="clear">
|
|
194
194
|
|
|
195
195
|
<footer>
|
|
196
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
196
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 15 2026 11:56:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
197
197
|
</footer>
|
|
198
198
|
|
|
199
199
|
<script>prettyPrint();</script>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<nav >
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WDispatchAi">WDispatchAi</a></li><li><a href="global.html#adapters">adapters</a></li><li><a href="global.html#attachErrorType">attachErrorType</a></li><li><a href="global.html#buildChain">buildChain</a></li><li><a href="global.html#buildValidator">buildValidator</a></li><li><a href="global.html#callAiWithFallback">callAiWithFallback</a></li><li><a href="global.html#callOnce">callOnce</a></li><li><a href="global.html#castPintOr">castPintOr</a></li><li><a href="global.html#defaultIntegratePrompt">defaultIntegratePrompt</a></li><li><a href="global.html#dfTimeoutMs">dfTimeoutMs</a></li><li><a href="global.html#dispatchAi">dispatchAi</a></li><li><a href="global.html#dispatchAiFallback">dispatchAiFallback</a></li><li><a href="global.html#dispatchAiWkf">dispatchAiWkf</a></li><li><a href="global.html#dispatchAntigravity">dispatchAntigravity</a></li><li><a href="global.html#dispatchApiOpenaiCompat">dispatchApiOpenaiCompat</a></li><li><a href="global.html#dispatchClaude">dispatchClaude</a></li><li><a href="global.html#dispatchCodex">dispatchCodex</a></li><li><a href="global.html#dispatchOpencode">dispatchOpencode</a></li><li><a href="global.html#extractJsonLoose">extractJsonLoose</a></li><li><a href="global.html#getCliArgs">getCliArgs</a></li><li><a href="global.html#getErrorResult">getErrorResult</a></li><li><a href="global.html#getErrorType">getErrorType</a></li><li><a href="global.html#initState">initState</a></li><li><a href="global.html#isKeyIndependentFail">isKeyIndependentFail</a></li><li><a href="global.html#reorderByCooling">reorderByCooling</a></li><li><a href="global.html#resolveProviders">resolveProviders</a></li><li><a href="global.html#runFanout">runFanout</a></li><li><a href="global.html#runFanoutPipeline">runFanoutPipeline</a></li><li><a href="global.html#runRolePipeline">runRolePipeline</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -50,11 +50,11 @@ import omit from 'lodash-es/omit.js'
|
|
|
50
50
|
import isarr from 'wsemi/src/isarr.mjs'
|
|
51
51
|
import isbol from 'wsemi/src/isbol.mjs'
|
|
52
52
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
53
|
-
import ispint from 'wsemi/src/ispint.mjs'
|
|
54
|
-
import cint from 'wsemi/src/cint.mjs'
|
|
55
53
|
import execCli from 'wsemi/src/execCli.mjs'
|
|
56
54
|
import getCliArgs from './getCliArgs.mjs'
|
|
57
55
|
import getErrorResult from './getErrorResult.mjs'
|
|
56
|
+
import { attachErrorType } from './getErrorType.mjs'
|
|
57
|
+
import castPintOr from './castPintOr.mjs'
|
|
58
58
|
import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
59
59
|
|
|
60
60
|
|
|
@@ -181,13 +181,7 @@ async function dispatchAntigravity(prompt, opt = {}) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
//timeoutMs, 先行取值以供printTimeout推導, 無效回退全套件統一預設300000
|
|
184
|
-
let timeoutMs = get(opt, 'timeoutMs', null)
|
|
185
|
-
if (!ispint(timeoutMs)) {
|
|
186
|
-
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
timeoutMs = cint(timeoutMs)
|
|
190
|
-
}
|
|
184
|
+
let timeoutMs = castPintOr(get(opt, 'timeoutMs', null), DEFAULT_TIMEOUT_MS)
|
|
191
185
|
|
|
192
186
|
//printTimeout, 未給時由timeoutMs推導並預留緩衝, 令agy先於外層逾時而回報自身錯誤訊息
|
|
193
187
|
let printTimeout = get(opt, 'printTimeout', null)
|
|
@@ -234,15 +228,17 @@ async function dispatchAntigravity(prompt, opt = {}) {
|
|
|
234
228
|
let optCli = omit(opt, OWN_KEYS)
|
|
235
229
|
|
|
236
230
|
//execCli, 不帶input(agy由--print取得prompt); try/catch兜底轉義膨脹等
|
|
237
|
-
//前置檢查漏網之spawn同步拋出(如ENAMETOOLONG), 維持不reject
|
|
231
|
+
//前置檢查漏網之spawn同步拋出(如ENAMETOOLONG), 維持不reject契約;
|
|
232
|
+
//失敗結果補上機器可讀之errorType(僅機械可判者, 見getErrorType.mjs)
|
|
238
233
|
try {
|
|
239
|
-
|
|
234
|
+
let r = await execCli(exe, args, {
|
|
240
235
|
...optCli,
|
|
241
236
|
timeoutMs,
|
|
242
237
|
})
|
|
238
|
+
return attachErrorType(r)
|
|
243
239
|
}
|
|
244
240
|
catch (err) {
|
|
245
|
-
return getErrorResult(`${err.code || 'UNKNOWN'}: ${err.message}
|
|
241
|
+
return getErrorResult(`${err.code || 'UNKNOWN'}: ${err.message}`, 'spawn')
|
|
246
242
|
}
|
|
247
243
|
}
|
|
248
244
|
|
|
@@ -262,7 +258,7 @@ export default dispatchAntigravity
|
|
|
262
258
|
<br class="clear">
|
|
263
259
|
|
|
264
260
|
<footer>
|
|
265
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
261
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 15 2026 11:56:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
266
262
|
</footer>
|
|
267
263
|
|
|
268
264
|
<script>prettyPrint();</script>
|