w-dispatch-ai 1.0.11 → 1.0.13
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 +33 -5
- package/dist/w-dispatch-ai.umd.js +2 -2
- package/dist/w-dispatch-ai.umd.js.map +1 -1
- package/docs/WDispatchAi.mjs.html +15 -3
- package/docs/adapters.mjs.html +2 -2
- package/docs/budgetFor.mjs.html +124 -0
- package/docs/castPintOr.mjs.html +2 -2
- package/docs/dfTimeoutMs.mjs.html +2 -2
- package/docs/dispatchAi.mjs.html +2 -2
- package/docs/dispatchAiFallback.mjs.html +2 -2
- package/docs/dispatchAiWkf.mjs.html +2 -2
- package/docs/dispatchAntigravity.mjs.html +2 -2
- package/docs/dispatchApiOpenaiCompat.mjs.html +2 -2
- package/docs/dispatchClaude.mjs.html +2 -2
- package/docs/dispatchCodex.mjs.html +2 -2
- package/docs/dispatchOpencode.mjs.html +2 -2
- package/docs/getCliArgs.mjs.html +2 -2
- package/docs/getErrorResult.mjs.html +2 -2
- package/docs/getErrorType.mjs.html +2 -2
- package/docs/global.html +1632 -49
- package/docs/index.html +2 -2
- package/docs/readEnvFile.mjs.html +132 -0
- package/docs/resolveProviders.mjs.html +54 -6
- package/docs/wkf_callAiWithFallback.mjs.html +7 -22
- package/docs/wkf_createFileStore.mjs.html +175 -0
- package/docs/wkf_createUsageCounter.mjs.html +213 -0
- package/docs/wkf_extractJsonLoose.mjs.html +2 -2
- package/docs/wkf_noSideEffectPrefix.mjs.html +121 -0
- package/docs/wkf_runFanout.mjs.html +2 -2
- package/docs/wkf_runFanoutPipeline.mjs.html +2 -2
- package/docs/wkf_runRolePipeline.mjs.html +2 -2
- package/docs/wkf_salvageTruncatedArray.mjs.html +167 -0
- package/package.json +2 -2
- package/src/WDispatchAi.mjs +13 -1
- package/src/budgetFor.mjs +52 -0
- package/src/providers.mjs +4 -3
- package/src/readEnvFile.mjs +60 -0
- package/src/resolveProviders.mjs +52 -4
- package/src/wkf/callAiWithFallback.mjs +5 -20
- package/src/wkf/createFileStore.mjs +103 -0
- package/src/wkf/createUsageCounter.mjs +141 -0
- package/src/wkf/noSideEffectPrefix.mjs +49 -0
- package/src/wkf/salvageTruncatedArray.mjs +95 -0
- package/test/unit-WDispatchAi.test.mjs +9 -3
- package/test/unit-budgetFor.test.mjs +26 -0
- package/test/unit-callAiWithFallback.test.mjs +8 -0
- package/test/unit-createFileStore.test.mjs +66 -0
- package/test/unit-createUsageCounter.test.mjs +76 -0
- package/test/unit-readEnvFile.test.mjs +52 -0
- package/test/unit-resolveProviders.test.mjs +49 -0
- package/test/unit-salvageTruncatedArray.test.mjs +39 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>wkf/createUsageCounter.mjs - Documentation</title>
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
10
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
11
|
+
<!--[if lt IE 9]>
|
|
12
|
+
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
13
|
+
<![endif]-->
|
|
14
|
+
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
|
|
15
|
+
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
|
|
16
|
+
<script src="scripts/nav.js" defer></script>
|
|
17
|
+
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
|
|
22
|
+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
23
|
+
<label for="nav-trigger" class="navicon-button x">
|
|
24
|
+
<div class="navicon"></div>
|
|
25
|
+
</label>
|
|
26
|
+
|
|
27
|
+
<label for="nav-trigger" class="overlay"></label>
|
|
28
|
+
|
|
29
|
+
<nav >
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
|
+
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<div id="main">
|
|
37
|
+
|
|
38
|
+
<h1 class="page-title">wkf/createUsageCounter.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>import path from 'path'
|
|
49
|
+
import get from 'lodash-es/get.js'
|
|
50
|
+
import isobj from 'wsemi/src/isobj.mjs'
|
|
51
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
52
|
+
import isfun from 'wsemi/src/isfun.mjs'
|
|
53
|
+
import ispint from 'wsemi/src/ispint.mjs'
|
|
54
|
+
import fsReadJson from 'wsemi/src/fsReadJson.mjs'
|
|
55
|
+
import fsWriteJson from 'wsemi/src/fsWriteJson.mjs'
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// createUsageCounter.mjs — 逐日、逐鍵之AI用量計帳(純觀測)
|
|
59
|
+
//
|
|
60
|
+
// 【純觀測, 絕不據以節流或阻擋——本檔最重要的警語】用量門檻是臆測值: 服務端額度視窗
|
|
61
|
+
// 形態多樣(逐日/逐時/5小時滾動/以token計而非以次數計), 呼叫端量得到的只有次數。
|
|
62
|
+
// 以臆測門檻擋自己的呼叫等同拿猜測當事實: 太保守則白白不用已有額度, 太寬鬆則毫無作用。
|
|
63
|
+
// 正確作法是「打下去、失敗了換下一家」(dispatchAiFallback之職責), 本計數器只提供
|
|
64
|
+
// 事後回答「那天各鍵各打了幾次」的本機證據——真要查「某家是不是被打爆了」時,
|
|
65
|
+
// 這是唯一的本機依據。
|
|
66
|
+
//
|
|
67
|
+
// 【於try事件記帳而非事後統計tried】即使行程被外部排程之時限中途砍掉,
|
|
68
|
+
// 已發出的請求仍會留下紀錄; 事後統計則會漏掉被砍那一輪的全部嘗試。
|
|
69
|
+
// 兩個消費端專案已各自寫過此接線(且各寫兩處), 故本函數直接提供onEvent。
|
|
70
|
+
//
|
|
71
|
+
// 【keyOf決定計帳粒度】預設記到金鑰(keyId如'agnes:agnes-2.5-flash#0'),
|
|
72
|
+
// 要記到條目改傳(ev)=>ev.providerId即可。
|
|
73
|
+
//
|
|
74
|
+
// 【本工廠為同步函數會throw】file與dir皆缺屬設定錯誤, 應於組裝期即失敗(fail fast),
|
|
75
|
+
// 與dispatchAiWkf工廠同一約定; 執行期之記帳與查詢則不throw。
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 建立逐日、逐鍵之用量計數器(純觀測, 不參與任何判斷)
|
|
80
|
+
*
|
|
81
|
+
* 特點:
|
|
82
|
+
* onEvent可直接掛進dispatchAiFallback/dispatchAiWkf,於type為'try'時依keyOf取鍵累加——
|
|
83
|
+
* 嘗試時即記帳,行程被外部時限中途砍掉已發出的請求仍有紀錄;
|
|
84
|
+
* 逐日分桶並僅保留最近keepDays天,避免檔案無限成長;
|
|
85
|
+
* getDate可注入時區錨定之日期函數——預設隨系統時區,排程session之系統時區可能為UTC+0
|
|
86
|
+
* 而使日界錯8小時,排程環境務必注入
|
|
87
|
+
*
|
|
88
|
+
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
89
|
+
* @param {String} [opt.file=null] 輸入用量檔完整路徑字串,與dir/name二擇一
|
|
90
|
+
* @param {String} [opt.dir=null] 輸入狀態目錄字串
|
|
91
|
+
* @param {String} [opt.name='ai-usage.json'] 輸入用量檔名字串,預設'ai-usage.json'
|
|
92
|
+
* @param {Function} [opt.getDate=系統日期] 輸入日期函數()=>'YYYY-MM-DD',預設隨系統時區(排程環境建議注入時區錨定者)
|
|
93
|
+
* @param {Number} [opt.keepDays=14] 輸入保留天數正整數,預設14
|
|
94
|
+
* @param {Function} [opt.keyOf=(ev)=>ev.keyId||ev.providerId] 輸入計帳鍵函數(ev)=>String,決定粒度(金鑰或條目),預設優先keyId
|
|
95
|
+
* @returns {Object} 回傳計數器物件,內含onEvent(可直接餵dispatch之onEvent)、bump(手動累加)、today(今日統計)與file(用量檔路徑)
|
|
96
|
+
* @example
|
|
97
|
+
*
|
|
98
|
+
* import createUsageCounter from './src/wkf/createUsageCounter.mjs'
|
|
99
|
+
*
|
|
100
|
+
* let usage = createUsageCounter({ dir: './state' })
|
|
101
|
+
* //let r = await dispatchAiFallback(prompt, { providers, onEvent: usage.onEvent })
|
|
102
|
+
* //console.log(usage.today())
|
|
103
|
+
* // => { today: '2026-08-18', byKey: { 'agnes:agnes-2.5-flash#0': 3 }, total: 3 }
|
|
104
|
+
*
|
|
105
|
+
*/
|
|
106
|
+
function createUsageCounter(opt = {}) {
|
|
107
|
+
let file = get(opt, 'file', null)
|
|
108
|
+
if (!isestr(file)) {
|
|
109
|
+
let dir = get(opt, 'dir', null)
|
|
110
|
+
if (!isestr(dir)) {
|
|
111
|
+
throw new Error('createUsageCounter: file or dir is required')
|
|
112
|
+
}
|
|
113
|
+
let name = get(opt, 'name', null)
|
|
114
|
+
file = path.join(dir, isestr(name) ? name : 'ai-usage.json')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let getDate = get(opt, 'getDate', null)
|
|
118
|
+
if (!isfun(getDate)) {
|
|
119
|
+
getDate = () => {
|
|
120
|
+
let d = new Date()
|
|
121
|
+
let p2 = (n) => String(n).padStart(2, '0')
|
|
122
|
+
return `${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())}`
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let keepDays = get(opt, 'keepDays', null)
|
|
127
|
+
if (!ispint(keepDays)) {
|
|
128
|
+
keepDays = 14
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let keyOf = get(opt, 'keyOf', null)
|
|
132
|
+
if (!isfun(keyOf)) {
|
|
133
|
+
keyOf = (ev) => get(ev, 'keyId', null) || get(ev, 'providerId', '')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let readAll = () => {
|
|
137
|
+
let r = fsReadJson(file)
|
|
138
|
+
let j = (get(r, 'error', undefined) !== undefined) ? null : get(r, 'success', null)
|
|
139
|
+
return isobj(j) ? j : {}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let bump = (key, n = 1) => {
|
|
143
|
+
if (!isestr(key)) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
let raw = readAll()
|
|
147
|
+
let today = getDate()
|
|
148
|
+
let cur = isobj(raw[today]) ? raw[today] : {}
|
|
149
|
+
cur[key] = (Number(cur[key]) || 0) + n
|
|
150
|
+
raw[today] = cur
|
|
151
|
+
|
|
152
|
+
//只留最近keepDays天
|
|
153
|
+
let days = Object.keys(raw).sort().slice(-keepDays)
|
|
154
|
+
let trimmed = {}
|
|
155
|
+
for (let d of days) {
|
|
156
|
+
trimmed[d] = raw[d]
|
|
157
|
+
}
|
|
158
|
+
fsWriteJson(file, trimmed, { useFormat: true })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
|
|
163
|
+
file,
|
|
164
|
+
|
|
165
|
+
//可直接餵dispatch之onEvent: 於type為'try'時依keyOf記帳。要同時掛自有回調, 於自有onEvent內轉呼叫本函數即可
|
|
166
|
+
onEvent: (ev) => {
|
|
167
|
+
if (get(ev, 'type', null) === 'try') {
|
|
168
|
+
bump(keyOf(ev))
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
//手動累加(不經事件之呼叫路徑用)
|
|
173
|
+
bump,
|
|
174
|
+
|
|
175
|
+
//今日各鍵用量統計
|
|
176
|
+
today: () => {
|
|
177
|
+
let raw = readAll()
|
|
178
|
+
let today = getDate()
|
|
179
|
+
let byKey = isobj(raw[today]) ? raw[today] : {}
|
|
180
|
+
let total = Object.values(byKey).reduce((a, b) => a + (Number(b) || 0), 0)
|
|
181
|
+
return { today, byKey, total }
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
export default createUsageCounter
|
|
189
|
+
</code></pre>
|
|
190
|
+
</article>
|
|
191
|
+
</section>
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<br class="clear">
|
|
201
|
+
|
|
202
|
+
<footer>
|
|
203
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
204
|
+
</footer>
|
|
205
|
+
|
|
206
|
+
<script>prettyPrint();</script>
|
|
207
|
+
<script src="scripts/polyfill.js"></script>
|
|
208
|
+
<script src="scripts/linenumber.js"></script>
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
</body>
|
|
213
|
+
</html>
|
|
@@ -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#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>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -167,7 +167,7 @@ export default extractJsonLoose
|
|
|
167
167
|
<br class="clear">
|
|
168
168
|
|
|
169
169
|
<footer>
|
|
170
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026
|
|
170
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
171
171
|
</footer>
|
|
172
172
|
|
|
173
173
|
<script>prettyPrint();</script>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>wkf/noSideEffectPrefix.mjs - Documentation</title>
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
10
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
11
|
+
<!--[if lt IE 9]>
|
|
12
|
+
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
13
|
+
<![endif]-->
|
|
14
|
+
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
|
|
15
|
+
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
|
|
16
|
+
<script src="scripts/nav.js" defer></script>
|
|
17
|
+
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
|
|
22
|
+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
23
|
+
<label for="nav-trigger" class="navicon-button x">
|
|
24
|
+
<div class="navicon"></div>
|
|
25
|
+
</label>
|
|
26
|
+
|
|
27
|
+
<label for="nav-trigger" class="overlay"></label>
|
|
28
|
+
|
|
29
|
+
<nav >
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
|
+
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<div id="main">
|
|
37
|
+
|
|
38
|
+
<h1 class="page-title">wkf/noSideEffectPrefix.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>// noSideEffectPrefix.mjs — 防副作用prompt前綴(單一來源)
|
|
49
|
+
//
|
|
50
|
+
// 【為何獨立成檔】此措辭原內嵌於callAiWithFallback, 但不經該層、直接呼叫
|
|
51
|
+
// dispatchAiFallback的呼叫端也需要同一約束——實例(2026-08-18使用端調研):
|
|
52
|
+
// 某使用端自帶一份「舊措辭」(一律禁止執行任何指令), 而該措辭的codex禁讀bug
|
|
53
|
+
// 本套件早已修正, 形成「套件修了、消費端沒跟上」的分岔。抽成獨立模組後
|
|
54
|
+
// 所有路徑引用同一來源, 措辭再修正時全體同步。
|
|
55
|
+
//
|
|
56
|
+
// 【為何需要此前綴】agentic CLI自帶檔案讀寫工具且對cwd隔離免疫(會自行解析
|
|
57
|
+
// 專案根目錄以絕對路徑寫檔), 實測會「順手」把結果另存孤兒檔,
|
|
58
|
+
// 甚至可能覆蓋呼叫端資料本體(殷鑑: 2026-08-10執行任務歷史.md遭AI覆寫、
|
|
59
|
+
// 評比腳本繞過前綴又產生根目錄孤兒檔); 各家CLI的權限機制彼此不同(codex有sandbox、
|
|
60
|
+
// claude有--disallowedTools、opencode有config.permission),
|
|
61
|
+
// 唯有prompt指示跨provider一致生效。CLI層限制可作第二道防線並存
|
|
62
|
+
// (內建providers.mjs各CLI條目即自帶機械防寫, 對照表見README)。
|
|
63
|
+
//
|
|
64
|
+
// 【措辭須豁免「唯讀查閱」(2026-08-13 A/B實測)】各CLI取得檔案內容的途徑不同——
|
|
65
|
+
// opencode與claude有獨立的read/grep工具, 不受「禁止執行指令」約束;
|
|
66
|
+
// 但codex讀檔即是執行shell指令, 舊措辭「禁止執行任何指令」對它等同「禁止讀檔」,
|
|
67
|
+
// 凡需讀專案的任務會得到一句「請貼上檔案內容」而非成果, 且該拒答是合法字串,
|
|
68
|
+
// 會通過驗證被遞補層當成「成功」, 整條fallback鏈就此停住——兜底地位被靜默廢掉。
|
|
69
|
+
// 實測: 舊措辭下codex回「無法讀取該檔案」, 現行措辭正常讀檔作答(11.1s),
|
|
70
|
+
// 防副作用(建檔/改檔/刪檔/改動系統狀態)之本旨不變。
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 防副作用prompt前綴:禁止寫入類副作用、豁免唯讀查閱
|
|
75
|
+
*
|
|
76
|
+
* 用法:`NO_SIDE_EFFECT + prompt`後交dispatchAiFallback;
|
|
77
|
+
* callAiWithFallback預設自動掛上(傳promptPrefix:''關閉),
|
|
78
|
+
* 直接呼叫dispatchAiFallback的呼叫端自行前綴
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
*
|
|
82
|
+
* import NO_SIDE_EFFECT from './src/wkf/noSideEffectPrefix.mjs'
|
|
83
|
+
*
|
|
84
|
+
* //let r = await dispatchAiFallback(NO_SIDE_EFFECT + prompt, { providers })
|
|
85
|
+
*
|
|
86
|
+
*/
|
|
87
|
+
let NO_SIDE_EFFECT = [
|
|
88
|
+
'【執行約束】你只需把結果輸出在回覆內容中。',
|
|
89
|
+
'禁止建立、修改或刪除任何檔案,也不要執行任何會改動磁碟或系統狀態的指令——',
|
|
90
|
+
'呼叫端只讀取你的回覆文字,任何寫入磁碟的動作都不會被採用,只會製造無人讀取的垃圾檔。',
|
|
91
|
+
'唯讀查閱(讀取檔案、搜尋內容、列出目錄)不在此限,需要時請照常使用。',
|
|
92
|
+
'', '',
|
|
93
|
+
].join('\n')
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
export default NO_SIDE_EFFECT
|
|
97
|
+
</code></pre>
|
|
98
|
+
</article>
|
|
99
|
+
</section>
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<br class="clear">
|
|
109
|
+
|
|
110
|
+
<footer>
|
|
111
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
112
|
+
</footer>
|
|
113
|
+
|
|
114
|
+
<script>prettyPrint();</script>
|
|
115
|
+
<script src="scripts/polyfill.js"></script>
|
|
116
|
+
<script src="scripts/linenumber.js"></script>
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
</body>
|
|
121
|
+
</html>
|
|
@@ -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#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>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -217,7 +217,7 @@ export { defaultIntegratePrompt }
|
|
|
217
217
|
<br class="clear">
|
|
218
218
|
|
|
219
219
|
<footer>
|
|
220
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026
|
|
220
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
221
221
|
</footer>
|
|
222
222
|
|
|
223
223
|
<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#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>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -161,7 +161,7 @@ export default runFanoutPipeline
|
|
|
161
161
|
<br class="clear">
|
|
162
162
|
|
|
163
163
|
<footer>
|
|
164
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026
|
|
164
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
165
165
|
</footer>
|
|
166
166
|
|
|
167
167
|
<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#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>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
33
|
|
|
34
34
|
</nav>
|
|
35
35
|
|
|
@@ -183,7 +183,7 @@ export default runRolePipeline
|
|
|
183
183
|
<br class="clear">
|
|
184
184
|
|
|
185
185
|
<footer>
|
|
186
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026
|
|
186
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
187
187
|
</footer>
|
|
188
188
|
|
|
189
189
|
<script>prettyPrint();</script>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>wkf/salvageTruncatedArray.mjs - Documentation</title>
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
<script src="scripts/prettify/prettify.js"></script>
|
|
10
|
+
<script src="scripts/prettify/lang-css.js"></script>
|
|
11
|
+
<!--[if lt IE 9]>
|
|
12
|
+
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
13
|
+
<![endif]-->
|
|
14
|
+
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
|
|
15
|
+
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
|
|
16
|
+
<script src="scripts/nav.js" defer></script>
|
|
17
|
+
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
|
|
22
|
+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
|
|
23
|
+
<label for="nav-trigger" class="navicon-button x">
|
|
24
|
+
<div class="navicon"></div>
|
|
25
|
+
</label>
|
|
26
|
+
|
|
27
|
+
<label for="nav-trigger" class="overlay"></label>
|
|
28
|
+
|
|
29
|
+
<nav >
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#NO_SIDE_EFFECT">NO_SIDE_EFFECT</a></li><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#budgetFor">budgetFor</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#createFileStore">createFileStore</a></li><li><a href="global.html#createUsageCounter">createUsageCounter</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#readEnvFile">readEnvFile</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><li><a href="global.html#salvageTruncatedArray">salvageTruncatedArray</a></li></ul>
|
|
33
|
+
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<div id="main">
|
|
37
|
+
|
|
38
|
+
<h1 class="page-title">wkf/salvageTruncatedArray.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>// salvageTruncatedArray.mjs — 截斷JSON陣列之前段搶救
|
|
49
|
+
//
|
|
50
|
+
// 【為何需要】免費模型常在長陣列輸出中途被截斷(無閉合括號), 整包JSON.parse必失敗,
|
|
51
|
+
// 但前面k個元素是完整的。使用端殷鑑(2026-08-07~08): 兩日22次OUTPUT_VALIDATION_FAILED,
|
|
52
|
+
// preview首元素皆完整合法——整批成果被截斷尾巴拖垮, 其中一輪3/3批全滅、整輪0產出。
|
|
53
|
+
// 字元掃描(含字串與跳脫感知)找出「最後一個完整的頂層元素」, 補上`]`後重新解析——
|
|
54
|
+
// 搶救回來的是**完整合法**的前段元素, 不是半成品。
|
|
55
|
+
//
|
|
56
|
+
// 【定位: 獨立工具, 不併入extractJsonLoose預設行為】「截斷」有兩種同樣合法的策略:
|
|
57
|
+
// ①判失敗換家重產(全涵蓋驗證, 如逐篇摘要——缺篇的輸出本身即不完整);
|
|
58
|
+
// ②搶救前段部分接受(批次萃取——避免全有全無, 一批30項救回25項遠勝重跑)。
|
|
59
|
+
// 套件提供工具, 策略由呼叫端選: extractJsonLoose檔頭明示之opt.parse擴充點即為此而留。
|
|
60
|
+
//
|
|
61
|
+
// 【只搶救「掃到結尾仍未閉合」的截斷】陣列有正常閉合但parse失敗屬別種毛病
|
|
62
|
+
// (非法字元/壞跳脫), 補`]`救不了反而可能拼出語意錯誤的結果, 一律不碰回null。
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 自截斷的JSON陣列文字搶救出前段完整元素(頂層元素須為物件)
|
|
67
|
+
*
|
|
68
|
+
* 特點:
|
|
69
|
+
* 自第一個`[`起逐字元掃描(字串與跳脫感知),追蹤「最後一個完整結束的頂層物件元素」,
|
|
70
|
+
* 補上`]`重新解析——回傳的每個元素皆完整合法,不是半成品;
|
|
71
|
+
* 陣列有正常閉合(=非截斷)、無`[`、或救回後為空陣列,一律回null不硬救;
|
|
72
|
+
* 典型用法:extractJsonLoose回null時之後備,或組成自訂parse注入callAiWithFallback
|
|
73
|
+
*
|
|
74
|
+
* @param {String} text 輸入模型輸出文字字串
|
|
75
|
+
* @returns {Array|null} 回傳搶救出之非空陣列,無法搶救回null
|
|
76
|
+
* @example
|
|
77
|
+
*
|
|
78
|
+
* import salvageTruncatedArray from './src/wkf/salvageTruncatedArray.mjs'
|
|
79
|
+
*
|
|
80
|
+
* console.log(salvageTruncatedArray('[{"a":1},{"b":2},{"c":'))
|
|
81
|
+
* // => [ { a: 1 }, { b: 2 } ]
|
|
82
|
+
*
|
|
83
|
+
* console.log(salvageTruncatedArray('[{"a":1}]')) //有閉合=非截斷, 不在搶救範圍
|
|
84
|
+
* // => null
|
|
85
|
+
*
|
|
86
|
+
*/
|
|
87
|
+
function salvageTruncatedArray(text) {
|
|
88
|
+
let s = String(text || '')
|
|
89
|
+
let start = s.indexOf('[')
|
|
90
|
+
if (start < 0) {
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
let depth = 0
|
|
94
|
+
let inStr = false
|
|
95
|
+
let esc = false
|
|
96
|
+
let lastComplete = -1
|
|
97
|
+
for (let i = start; i < s.length; i++) {
|
|
98
|
+
let ch = s[i]
|
|
99
|
+
if (esc) {
|
|
100
|
+
esc = false
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
if (inStr) {
|
|
104
|
+
if (ch === '\\') {
|
|
105
|
+
esc = true
|
|
106
|
+
}
|
|
107
|
+
else if (ch === '"') {
|
|
108
|
+
inStr = false
|
|
109
|
+
}
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
if (ch === '"') {
|
|
113
|
+
inStr = true
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
if (ch === '[' || ch === '{') {
|
|
117
|
+
depth++
|
|
118
|
+
}
|
|
119
|
+
else if (ch === ']' || ch === '}') {
|
|
120
|
+
depth--
|
|
121
|
+
if (depth === 1 && ch === '}') {
|
|
122
|
+
lastComplete = i //頂層元素(物件)完整結束
|
|
123
|
+
}
|
|
124
|
+
if (depth === 0) {
|
|
125
|
+
return null //陣列有閉合=非截斷, 不在搶救範圍
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (lastComplete < 0) {
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
let r = JSON.parse(s.slice(start, lastComplete + 1) + ']')
|
|
134
|
+
return (Array.isArray(r) && r.length > 0) ? r : null
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
export default salvageTruncatedArray
|
|
143
|
+
</code></pre>
|
|
144
|
+
</article>
|
|
145
|
+
</section>
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<br class="clear">
|
|
155
|
+
|
|
156
|
+
<footer>
|
|
157
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 15:44:01 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
158
|
+
</footer>
|
|
159
|
+
|
|
160
|
+
<script>prettyPrint();</script>
|
|
161
|
+
<script src="scripts/polyfill.js"></script>
|
|
162
|
+
<script src="scripts/linenumber.js"></script>
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
</body>
|
|
167
|
+
</html>
|