beast-agent 2.0.0 → 2.2.0
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/package.json +1 -1
- package/src/agent/engine.js +31 -4
- package/src/agent/llm.js +222 -42
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const
|
|
10
|
-
const {
|
|
9
|
+
const llm = require('./llm');
|
|
10
|
+
const { chatStream, chatStreamAuto, chatOnce } = llm;
|
|
11
|
+
const { chatCompletionsUrl, beastDir } = require('./config');
|
|
11
12
|
const tools = require('./tools');
|
|
12
13
|
const research = require('./research');
|
|
13
14
|
const agentdefs = require('./agentdefs');
|
|
@@ -60,7 +61,8 @@ const OWNER_FAIL_EMAIL = 'info@algokod.com';
|
|
|
60
61
|
OpenAI (reasoning.effort) ve OpenRouter (reasoning_effort) 2026 itibarıyla:
|
|
61
62
|
none · minimal · low · medium · high · xhigh · max.
|
|
62
63
|
0 (Kapalı) parametre göndermez; 1-5 API'ye reasoning_effort olarak gider.
|
|
63
|
-
Model desteklemiyorsa llm.js
|
|
64
|
+
Model desteklemiyorsa llm.js parametreyi atlayıp sessizce retry yapar ve
|
|
65
|
+
bu model için KALICI öğrenir (model-caps.json) → bir daha gönderilmez. */
|
|
64
66
|
const THINK_LEVELS = [
|
|
65
67
|
{ v: 0, label: 'Kapalı', effort: null },
|
|
66
68
|
{ v: 1, label: 'Low', effort: 'low' },
|
|
@@ -249,6 +251,9 @@ class Engine {
|
|
|
249
251
|
};
|
|
250
252
|
this.sessionsDir = opts.sessionsDir;
|
|
251
253
|
fs.mkdirSync(this.sessionsDir, { recursive: true });
|
|
254
|
+
/* model yetenek keşfi: reddedilen parametre/seviyeler kalıcı öğrenilir
|
|
255
|
+
(reasoning_effort seviyeleri, temperature, prompt_cache_key) */
|
|
256
|
+
try { llm.setCapsFile(path.join(beastDir(), 'model-caps.json')); } catch {}
|
|
252
257
|
this.workspace = opts.workspace || process.env.USERPROFILE || '.';
|
|
253
258
|
this.cfg.baseChain = (this.cfg.chain || []).slice();
|
|
254
259
|
this.cfg.chain = [...this.cfg.baseChain, ...customChain(opts.customProviders)];
|
|
@@ -758,6 +763,16 @@ class Engine {
|
|
|
758
763
|
? `bağlantı sorunu — internet dönünce ya da yeniden denemeyle sürer (${attempt}/6)`
|
|
759
764
|
: `sağlayıcı yanıtsız — tekrar deniyor (${attempt}/3)`,
|
|
760
765
|
}),
|
|
766
|
+
onEffortDowngrade: (from, to) =>
|
|
767
|
+
emitSafe(this, session.id, {
|
|
768
|
+
type: 'status',
|
|
769
|
+
status: `🧠 ${c.model}: düşünme seviyesi '${from}' kabul edilmedi → otomatik '${to}' (bu model için kalıcı)`,
|
|
770
|
+
}),
|
|
771
|
+
onParamDrop: (p) =>
|
|
772
|
+
emitSafe(this, session.id, {
|
|
773
|
+
type: 'status',
|
|
774
|
+
status: `⚠ ${c.model}: '${llm.PARAM_LABELS[p] || p}' kabul edilmedi → atlandı (bu model için kalıcı)`,
|
|
775
|
+
}),
|
|
761
776
|
}
|
|
762
777
|
);
|
|
763
778
|
if (i > 0) {
|
|
@@ -3731,7 +3746,19 @@ const skills = require('./skills');
|
|
|
3731
3746
|
reasoningEffort: this._thinkEffort(),
|
|
3732
3747
|
cacheKey: String(sessionId || 'subagent'),
|
|
3733
3748
|
},
|
|
3734
|
-
{
|
|
3749
|
+
{
|
|
3750
|
+
signal: ctrl.signal,
|
|
3751
|
+
onEffortDowngrade: (from, to) =>
|
|
3752
|
+
emitSafe(this, sessionId, {
|
|
3753
|
+
type: 'status',
|
|
3754
|
+
status: `🧠 alt-agent: düşünme seviyesi '${from}' → '${to}' (otomatik uyum)`,
|
|
3755
|
+
}),
|
|
3756
|
+
onParamDrop: (p) =>
|
|
3757
|
+
emitSafe(this, sessionId, {
|
|
3758
|
+
type: 'status',
|
|
3759
|
+
status: `⚠ alt-agent: '${llm.PARAM_LABELS[p] || p}' kabul edilmedi → atlandı`,
|
|
3760
|
+
}),
|
|
3761
|
+
}
|
|
3735
3762
|
);
|
|
3736
3763
|
const assistant = { role: 'assistant', content: res.content || '' };
|
|
3737
3764
|
if (res.toolCalls && res.toolCalls.length) assistant.tool_calls = res.toolCalls;
|
package/src/agent/llm.js
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
KOPMASINDA (fetch failed) çok daha sabırlı: 6 deneme + internet dönene
|
|
6
6
|
dek bekleme (main'deki net izleyicisinden beslenir). Akış ortasında kopan
|
|
7
7
|
bağlantıda: hiç veri akmadıysa sessiz baştan dener, kısmi metin geldiyse
|
|
8
|
-
'length' gibi döner → chatStreamAuto kaldığı yerden DEVAM ETTİRİR.
|
|
8
|
+
'length' gibi döner → chatStreamAuto kaldığı yerden DEVAM ETTİRİR.
|
|
9
|
+
Model reddettiği parametre/seviyeleri (reasoning_effort, temperature,
|
|
10
|
+
prompt_cache_key) 400'den otomatik öğrenir → model-caps.json'a kalıcı. */
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
9
13
|
|
|
10
14
|
const RETRYABLE_STATUS = new Set([408, 409, 425, 429, 500, 502, 503, 504]);
|
|
11
15
|
const MAX_RETRIES = 3; /* sağlayıcı geçici HTTP hataları */
|
|
@@ -83,6 +87,173 @@ function wantsCacheKey(sel) {
|
|
|
83
87
|
);
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
/* ---------- model yetenek keşfi (opencode provider.metadata muadili) ----------
|
|
91
|
+
Her model her parametreyi kabul etmez: Mimo gibi bazıları reasoning_effort'u,
|
|
92
|
+
GPT-5/o1 ailesi temperature'ı reddeder. Sağlayıcı 400 "Invalid request
|
|
93
|
+
parameters" diyorsa parametre TEK TEK atlanıp sessizce yeniden denenir;
|
|
94
|
+
kabul edilen kombinasyon KALICI öğrenilir (model-caps.json) → sonraki
|
|
95
|
+
isteklerde reddedilen parametre hiç gönderilmez. models.dev metadata'sına
|
|
96
|
+
internet bağımlılığı olmadan çalışma-zamanında eşdeğer davranır. */
|
|
97
|
+
|
|
98
|
+
const CAPS_FLAGS = ['noReasoning', 'noTemperature', 'noCacheKey'];
|
|
99
|
+
const PARAM_LABELS = {
|
|
100
|
+
noReasoning: 'reasoning_effort',
|
|
101
|
+
noTemperature: 'temperature',
|
|
102
|
+
noCacheKey: 'prompt_cache_key',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let _capsFile = null;
|
|
106
|
+
const _caps = new Map(); /* "providerId::model" -> { noReasoning?, noTemperature?, noCacheKey? } */
|
|
107
|
+
|
|
108
|
+
function capsKey(sel) {
|
|
109
|
+
return String((sel && sel.providerId) || '') + '::' + String((sel && sel.model) || '');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function capsFor(sel) {
|
|
113
|
+
return _caps.get(capsKey(sel)) || null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resetCaps() {
|
|
117
|
+
_caps.clear();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* caps dosyasını bağla: mevcut öğrenimler yüklenir; null → kapat */
|
|
121
|
+
function setCapsFile(p) {
|
|
122
|
+
_caps.clear();
|
|
123
|
+
_capsFile = typeof p === 'string' && p ? p : null;
|
|
124
|
+
if (!_capsFile) return;
|
|
125
|
+
try {
|
|
126
|
+
const j = JSON.parse(fs.readFileSync(_capsFile, 'utf8'));
|
|
127
|
+
for (const [k, v] of Object.entries(j.models || {})) {
|
|
128
|
+
if (v && typeof v === 'object') _caps.set(k, { ...v });
|
|
129
|
+
}
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function learnCaps(sel, drops) {
|
|
134
|
+
const k = capsKey(sel);
|
|
135
|
+
const prev = _caps.get(k) || {};
|
|
136
|
+
const next = { ...prev };
|
|
137
|
+
let changed = false;
|
|
138
|
+
for (const f of CAPS_FLAGS) {
|
|
139
|
+
if (drops[f] && !prev[f]) {
|
|
140
|
+
next[f] = true;
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/* reddedilen reasoning seviyeleri (["max","xhigh",...]) — seviye bazlı öğrenim */
|
|
145
|
+
if (Array.isArray(drops.failedEfforts) && drops.failedEfforts.length) {
|
|
146
|
+
const merged = [...(prev.failedEfforts || [])];
|
|
147
|
+
for (const e of drops.failedEfforts) {
|
|
148
|
+
if (!merged.includes(e)) merged.push(e);
|
|
149
|
+
}
|
|
150
|
+
if (merged.length !== (prev.failedEfforts || []).length) {
|
|
151
|
+
next.failedEfforts = merged;
|
|
152
|
+
changed = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!changed) return;
|
|
156
|
+
_caps.set(k, next);
|
|
157
|
+
if (!_capsFile) return;
|
|
158
|
+
try {
|
|
159
|
+
const all = {};
|
|
160
|
+
for (const [key, v] of _caps) all[key] = v;
|
|
161
|
+
fs.writeFileSync(_capsFile, JSON.stringify({ version: 1, models: all }, null, 2));
|
|
162
|
+
} catch {}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* reasoning_effort merdiveni: reddedilen seviyede BİR ALT seviye denenir
|
|
166
|
+
(Mimo 2.5: max/xhigh reddediyor, low çalışıyor). low'un altı yok → null. */
|
|
167
|
+
const EFFORT_LOWER = { max: 'xhigh', xhigh: 'high', high: 'medium', medium: 'low', low: null };
|
|
168
|
+
|
|
169
|
+
/* Hangi hata metni "geçersiz istek" dili (Mimo: "Invalid request parameters").
|
|
170
|
+
friendlyError'ın kendi öneki ("HTTP 400 Bad Request") ile eşleşmemesine dikkat. */
|
|
171
|
+
const GENERIC_400_RE =
|
|
172
|
+
/invalid request param|invalid_param|unsupported|not supported|unknown (param|field|argument)|unrecognized|unexpected/i;
|
|
173
|
+
|
|
174
|
+
/* run(drops, body): verilen atlama seti + gövdeyle isteği çalıştırır.
|
|
175
|
+
400 gelirse ve hata parametre reddine işaret ediyorsa:
|
|
176
|
+
1) reasoning_effort önce SEVİYE düşürülerek denenir (max→xhigh→...→low),
|
|
177
|
+
en düşük de reddedilirse parametre tamamen bırakılır (noReasoning);
|
|
178
|
+
2) sonra temperature, prompt_cache_key atlanır;
|
|
179
|
+
Kabul edilen kombinasyon bu model için KALICI öğrenilir → sonraki isteklerde
|
|
180
|
+
reddedilen seviye/parametre hiç gönderilmez (otomatik reasoning uyumu). */
|
|
181
|
+
async function withParamFallbacks(sel, body0, run, { signal, onParamDrop, onEffortDowngrade } = {}) {
|
|
182
|
+
const active = { ...(capsFor(sel) || {}) };
|
|
183
|
+
for (;;) {
|
|
184
|
+
/* seçilen seviyeyi öğrenilen redlere göre otomatik düşür */
|
|
185
|
+
let eff = body0.reasoningEffort ? String(body0.reasoningEffort) : null;
|
|
186
|
+
if (eff && !active.noReasoning) {
|
|
187
|
+
const failed = active.failedEfforts || [];
|
|
188
|
+
let guard = 0;
|
|
189
|
+
while (eff && failed.includes(eff) && guard++ < 5) eff = EFFORT_LOWER[eff] || null;
|
|
190
|
+
}
|
|
191
|
+
if (active.noReasoning) eff = null;
|
|
192
|
+
const body = eff && eff !== body0.reasoningEffort ? { ...body0, reasoningEffort: eff } : body0;
|
|
193
|
+
let res;
|
|
194
|
+
try {
|
|
195
|
+
res = await run({ ...active }, body);
|
|
196
|
+
learnCaps(sel, active);
|
|
197
|
+
return res;
|
|
198
|
+
} catch (e) {
|
|
199
|
+
if (!e || e.status !== 400 || (signal && signal.aborted)) throw e;
|
|
200
|
+
const msg = String(e.message || '');
|
|
201
|
+
const generic = GENERIC_400_RE.test(msg);
|
|
202
|
+
const reasonHint = /reason|effort|thinking/i.test(msg);
|
|
203
|
+
const tempHint = /temperature/i.test(msg);
|
|
204
|
+
|
|
205
|
+
/* denenecek atlamalar öncelik sırasıyla: önce reasoning merdiveni
|
|
206
|
+
(seviye düşür — Mimo: max reddedilir, low çalışır), sonra diğerleri */
|
|
207
|
+
const actions = [];
|
|
208
|
+
if ((reasonHint || generic) && body0.reasoningEffort && !active.noReasoning) actions.push('reasoning');
|
|
209
|
+
if ((generic || tempHint) && !active.noTemperature) actions.push('temperature');
|
|
210
|
+
if ((generic || /cache/i.test(msg)) && body0.cacheKey && wantsCacheKey(sel) && !active.noCacheKey) {
|
|
211
|
+
actions.push('cacheKey');
|
|
212
|
+
}
|
|
213
|
+
const act = actions[0];
|
|
214
|
+
if (!act) throw e;
|
|
215
|
+
|
|
216
|
+
if (act === 'reasoning') {
|
|
217
|
+
const cur = eff;
|
|
218
|
+
const lower = cur && cur in EFFORT_LOWER ? EFFORT_LOWER[cur] : null;
|
|
219
|
+
/* merdiven: generic red (gerçek sebep bilinmiyor → seviyeleri dene)
|
|
220
|
+
ya da red mesajı SEVİYE değerini zikrediyorsa ("effort 'max' invalid").
|
|
221
|
+
Mesaj parametrenin kendisini reddediyorsa ("not supported") direkt bırak. */
|
|
222
|
+
const valueQuoted = cur ? (msg.includes(`'${cur}'`) || msg.includes(`"${cur}"`)) : false;
|
|
223
|
+
/* ipucu parametrenin kendisini reddediyorsa merdivene gerek yok */
|
|
224
|
+
const ladder = valueQuoted || (generic && !reasonHint);
|
|
225
|
+
if (cur && lower && ladder) {
|
|
226
|
+
/* seviye reddedildi → bir alt seviyeye in, kalıcı öğren */
|
|
227
|
+
if (!Array.isArray(active.failedEfforts)) active.failedEfforts = [];
|
|
228
|
+
if (!active.failedEfforts.includes(cur)) {
|
|
229
|
+
active.failedEfforts.push(cur);
|
|
230
|
+
learnCaps(sel, active);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
onEffortDowngrade && onEffortDowngrade(cur, lower);
|
|
234
|
+
} catch {}
|
|
235
|
+
} else {
|
|
236
|
+
/* parametre tamamen reddedildi → bir daha gönderme */
|
|
237
|
+
active.noReasoning = true;
|
|
238
|
+
try {
|
|
239
|
+
onParamDrop && onParamDrop('noReasoning');
|
|
240
|
+
} catch {}
|
|
241
|
+
}
|
|
242
|
+
} else if (act === 'temperature') {
|
|
243
|
+
active.noTemperature = true;
|
|
244
|
+
try {
|
|
245
|
+
onParamDrop && onParamDrop('noTemperature');
|
|
246
|
+
} catch {}
|
|
247
|
+
} else if (act === 'cacheKey') {
|
|
248
|
+
active.noCacheKey = true;
|
|
249
|
+
try {
|
|
250
|
+
onParamDrop && onParamDrop('noCacheKey');
|
|
251
|
+
} catch {}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
86
257
|
/* Retry-After başlığı: saniye ("2"), milisaniye ("120ms" / retry-after-ms)
|
|
87
258
|
ya da HTTP-tarih olabilir (opencode session/retry.ts port) */
|
|
88
259
|
function parseRetryAfter(v) {
|
|
@@ -157,21 +328,25 @@ async function withRetries(fn, { signal, onRetry } = {}) {
|
|
|
157
328
|
}
|
|
158
329
|
}
|
|
159
330
|
|
|
160
|
-
async function openChat(sel, body, { stream, signal,
|
|
331
|
+
async function openChat(sel, body, { stream, signal, drops } = {}) {
|
|
332
|
+
const d = drops || {};
|
|
161
333
|
const payload = {
|
|
162
334
|
model: sel.model,
|
|
163
335
|
messages: body.messages,
|
|
164
336
|
stream,
|
|
165
337
|
...(body.tools && body.tools.length ? { tools: body.tools } : {}),
|
|
166
|
-
temperature: body.temperature ?? 0.6,
|
|
167
338
|
};
|
|
339
|
+
if (!d.noTemperature) {
|
|
340
|
+
payload.temperature = body.temperature ?? 0.6;
|
|
341
|
+
}
|
|
168
342
|
/* Düşünme (reasoning) seviyesi: OpenAI-style reasoning_effort —
|
|
169
|
-
OpenRouter ve GPT-5 ailesi dahil uyumlu sağlayıcılar kabul eder.
|
|
170
|
-
|
|
343
|
+
OpenRouter ve GPT-5 ailesi dahil uyumlu sağlayıcılar kabul eder.
|
|
344
|
+
Kabul etmeyenlerde (Mimo vb.): 400 → parametre atlanır + KALICI öğrenilir. */
|
|
345
|
+
if (body.reasoningEffort && !d.noReasoning) {
|
|
171
346
|
payload.reasoning_effort = String(body.reasoningEffort);
|
|
172
347
|
}
|
|
173
348
|
/* prompt cache anahtarı: oturum başına sabit → sağlayıcı önek önbelleği tutar */
|
|
174
|
-
if (body.cacheKey && wantsCacheKey(sel)) {
|
|
349
|
+
if (body.cacheKey && wantsCacheKey(sel) && !d.noCacheKey) {
|
|
175
350
|
payload.prompt_cache_key = String(body.cacheKey);
|
|
176
351
|
}
|
|
177
352
|
return fetch(sel.url, {
|
|
@@ -187,24 +362,18 @@ async function openChat(sel, body, { stream, signal, omitReasoning } = {}) {
|
|
|
187
362
|
});
|
|
188
363
|
}
|
|
189
364
|
|
|
190
|
-
async function chatStream(sel, body, { signal, onDelta, onRetry } = {}) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
/reason|effort|thinking/i.test(String(e.message || ''))
|
|
200
|
-
) {
|
|
201
|
-
return await streamOnce(sel, body, { signal, onDelta }, true);
|
|
202
|
-
}
|
|
203
|
-
throw e;
|
|
204
|
-
}
|
|
365
|
+
async function chatStream(sel, body, { signal, onDelta, onRetry, onParamDrop, onEffortDowngrade } = {}) {
|
|
366
|
+
/* 400 parametre/seviye reddinde: otomatik uyum (seviye düşür → parametre atla),
|
|
367
|
+
başarılı set kalıcı öğrenilir */
|
|
368
|
+
return withParamFallbacks(
|
|
369
|
+
sel,
|
|
370
|
+
body,
|
|
371
|
+
(drops, b) => streamOnce(sel, b, { signal, onDelta }, drops),
|
|
372
|
+
{ signal, onParamDrop, onEffortDowngrade }
|
|
373
|
+
);
|
|
205
374
|
}
|
|
206
375
|
|
|
207
|
-
async function streamOnce(sel, body, { signal, onDelta, onRetry } = {},
|
|
376
|
+
async function streamOnce(sel, body, { signal, onDelta, onRetry } = {}, drops = {}) {
|
|
208
377
|
/* akış durumu dışarıda tutulur: gövde okunurken bağlantı koparsa
|
|
209
378
|
ne kadarı geldiğine bakılır (kısmi metin → devam; boş/araç → baştan) */
|
|
210
379
|
const state = { content: '', reasoning: '', toolCalls: [], usage: null, finishReason: null };
|
|
@@ -213,7 +382,7 @@ async function streamOnce(sel, body, { signal, onDelta, onRetry } = {}, omitReas
|
|
|
213
382
|
for (;;) {
|
|
214
383
|
const res = await withRetries(
|
|
215
384
|
async () => {
|
|
216
|
-
const r = await openChat(sel, body, { stream: true, signal,
|
|
385
|
+
const r = await openChat(sel, body, { stream: true, signal, drops });
|
|
217
386
|
if (!r.ok) {
|
|
218
387
|
let detail = '';
|
|
219
388
|
try {
|
|
@@ -385,25 +554,31 @@ async function chatStreamAuto(sel, body, opts = {}) {
|
|
|
385
554
|
return last;
|
|
386
555
|
}
|
|
387
556
|
|
|
388
|
-
async function chatOnce(sel, body, { signal, onRetry } = {}) {
|
|
389
|
-
const res = await
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
557
|
+
async function chatOnce(sel, body, { signal, onRetry, onParamDrop, onEffortDowngrade } = {}) {
|
|
558
|
+
const res = await withParamFallbacks(
|
|
559
|
+
sel,
|
|
560
|
+
body,
|
|
561
|
+
(drops, b) =>
|
|
562
|
+
withRetries(
|
|
563
|
+
async () => {
|
|
564
|
+
const r = await openChat(sel, b, { stream: false, signal, drops });
|
|
565
|
+
if (!r.ok) {
|
|
566
|
+
let detail = '';
|
|
567
|
+
try {
|
|
568
|
+
detail = (await r.text()).slice(0, 300);
|
|
569
|
+
} catch {}
|
|
570
|
+
const err = new Error(friendlyError(r.status, r.statusText, detail));
|
|
571
|
+
err.status = r.status;
|
|
572
|
+
err.retryAfterMs = parseRetryAfter(
|
|
573
|
+
r.headers && (r.headers.get('retry-after-ms') || r.headers.get('retry-after'))
|
|
574
|
+
);
|
|
575
|
+
throw err;
|
|
576
|
+
}
|
|
577
|
+
return r;
|
|
578
|
+
},
|
|
579
|
+
{ signal, onRetry }
|
|
580
|
+
),
|
|
581
|
+
{ signal, onParamDrop, onEffortDowngrade }
|
|
407
582
|
);
|
|
408
583
|
const json = await res.json();
|
|
409
584
|
const msg = json.choices?.[0]?.message || {};
|
|
@@ -421,9 +596,14 @@ module.exports = {
|
|
|
421
596
|
chatStreamAuto,
|
|
422
597
|
chatOnce,
|
|
423
598
|
withRetries,
|
|
599
|
+
withParamFallbacks,
|
|
424
600
|
friendlyError,
|
|
425
601
|
sumUsage,
|
|
426
602
|
setNetProbe,
|
|
603
|
+
setCapsFile,
|
|
604
|
+
capsFor,
|
|
605
|
+
resetCaps,
|
|
606
|
+
PARAM_LABELS,
|
|
427
607
|
isNetworkError,
|
|
428
608
|
wantsCacheKey,
|
|
429
609
|
parseRetryAfter,
|