190proof 1.0.109 → 1.0.111
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 +3 -0
- package/dist/index.d.mts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +212 -111
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +210 -109
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -278,6 +278,9 @@ Optional per-request knobs live on `payload` (`GenericPayload`):
|
|
|
278
278
|
- `payload.requestTimeoutMs`: `number` - Per-attempt HTTP timeout in ms (default: 120000), honored by every adapter — except streaming OpenRouter attempts, which it deliberately does NOT bound (see below). For OpenRouter's non-streaming transport the default is 180000.
|
|
279
279
|
- `payload.streaming`: `boolean` - OpenRouter-only (default: true). Streams the completion over SSE. A streaming attempt is bounded by two independent timers instead of `requestTimeoutMs`: `streamTimeoutMs` (total wall clock, default 600000) and the per-useful-chunk stall timeout (`chunkTimeoutMs` argument, default 15000). A chunk is "useful" only if it advances content, reasoning, tool-call fragments, finish_reason, or usage — SSE comment keep-alives (`: OPENROUTER PROCESSING`) and role-only deltas don't reset the stall timer, so a hung provider dies within one stall window while a healthy long generation can run to the total budget. Set `streaming: false` for the old single-JSON-body transport.
|
|
280
280
|
- `payload.streamTimeoutMs`: `number` - OpenRouter-only: total wall-clock budget per streaming attempt (default: 600000).
|
|
281
|
+
- `payload.streamDeadlineAt`: `number` - OpenRouter-only: absolute deadline (epoch ms) for the whole call **including retries** — the caller's turn budget. Each attempt gets `min(streamTimeoutMs, deadline - now)`, and once under 10s remain the call fails fast instead of starting a generation that cannot be delivered. Use it whenever the caller has its own timeout: a per-attempt budget alone is re-granted on every retry and can outlive that timeout.
|
|
282
|
+
|
|
283
|
+
OpenRouter retries also perform **moderation eviction**: a provider content-moderation rejection (e.g. "Upstream error from Alibaba: Output data may contain inappropriate content.") is deterministic for a given payload, so on the first one the refusing provider is removed from the request's provider preferences (`ignore` += slug, `order` -= slug) and every remaining attempt reroutes to the next provider. Non-moderation errors retry with unchanged preferences, and `fallbackModel` still applies if the whole pool refuses.
|
|
281
284
|
- `payload.signal`: `AbortSignal` - Caller-supplied cancellation. When it aborts, the in-flight provider request is cancelled and `callWithRetries` **rejects immediately — it does not retry or fall back** (both the retry loop and the fallback branch bail on `signal.aborted`). Threaded to the underlying fetch/axios/SDK call of each provider.
|
|
282
285
|
|
|
283
286
|
#### Returns
|
package/dist/index.d.mts
CHANGED
|
@@ -297,6 +297,19 @@ interface GenericPayload {
|
|
|
297
297
|
* by chunks that advance the output, never by keep-alive bytes/comments).
|
|
298
298
|
*/
|
|
299
299
|
streamTimeoutMs?: number;
|
|
300
|
+
/**
|
|
301
|
+
* OpenRouter-only: absolute wall-clock deadline (epoch ms) for the whole
|
|
302
|
+
* call INCLUDING retries — the caller's turn budget, not a per-attempt one.
|
|
303
|
+
* Each streaming attempt gets `min(streamTimeoutMs, deadline - now)`, and
|
|
304
|
+
* once too little time remains to be worth an attempt the call fails fast
|
|
305
|
+
* instead of starting a generation that cannot finish.
|
|
306
|
+
*
|
|
307
|
+
* Without it, a per-attempt budget is re-granted on every retry, so a slow
|
|
308
|
+
* generation can outlive the caller's own turn deadline and get killed with
|
|
309
|
+
* nothing to show (2026-07-28: a 538s completion finished just as the
|
|
310
|
+
* caller's 585s turn budget expired, and the reply was discarded).
|
|
311
|
+
*/
|
|
312
|
+
streamDeadlineAt?: number;
|
|
300
313
|
/**
|
|
301
314
|
* Optional caller-supplied cancellation signal. When it aborts, the in-flight
|
|
302
315
|
* provider request is cancelled and `callWithRetries` rejects immediately —
|
|
@@ -309,10 +322,16 @@ interface GenericPayload {
|
|
|
309
322
|
|
|
310
323
|
declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
|
|
311
324
|
declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
|
|
325
|
+
/**
|
|
326
|
+
* Least time a streaming attempt is worth starting with. Below this the
|
|
327
|
+
* generation cannot plausibly finish before the caller's deadline, so burning
|
|
328
|
+
* a provider call (and paying for tokens that get discarded) is pure waste.
|
|
329
|
+
*/
|
|
330
|
+
declare const MIN_STREAM_ATTEMPT_MS = 10000;
|
|
312
331
|
declare function parseModelString(model: string): {
|
|
313
332
|
provider: Provider;
|
|
314
333
|
modelId: string;
|
|
315
334
|
};
|
|
316
335
|
declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
|
|
317
336
|
|
|
318
|
-
export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
|
|
337
|
+
export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, MIN_STREAM_ATTEMPT_MS, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
|
package/dist/index.d.ts
CHANGED
|
@@ -297,6 +297,19 @@ interface GenericPayload {
|
|
|
297
297
|
* by chunks that advance the output, never by keep-alive bytes/comments).
|
|
298
298
|
*/
|
|
299
299
|
streamTimeoutMs?: number;
|
|
300
|
+
/**
|
|
301
|
+
* OpenRouter-only: absolute wall-clock deadline (epoch ms) for the whole
|
|
302
|
+
* call INCLUDING retries — the caller's turn budget, not a per-attempt one.
|
|
303
|
+
* Each streaming attempt gets `min(streamTimeoutMs, deadline - now)`, and
|
|
304
|
+
* once too little time remains to be worth an attempt the call fails fast
|
|
305
|
+
* instead of starting a generation that cannot finish.
|
|
306
|
+
*
|
|
307
|
+
* Without it, a per-attempt budget is re-granted on every retry, so a slow
|
|
308
|
+
* generation can outlive the caller's own turn deadline and get killed with
|
|
309
|
+
* nothing to show (2026-07-28: a 538s completion finished just as the
|
|
310
|
+
* caller's 585s turn budget expired, and the reply was discarded).
|
|
311
|
+
*/
|
|
312
|
+
streamDeadlineAt?: number;
|
|
300
313
|
/**
|
|
301
314
|
* Optional caller-supplied cancellation signal. When it aborts, the in-flight
|
|
302
315
|
* provider request is cancelled and `callWithRetries` rejects immediately —
|
|
@@ -309,10 +322,16 @@ interface GenericPayload {
|
|
|
309
322
|
|
|
310
323
|
declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
|
|
311
324
|
declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
|
|
325
|
+
/**
|
|
326
|
+
* Least time a streaming attempt is worth starting with. Below this the
|
|
327
|
+
* generation cannot plausibly finish before the caller's deadline, so burning
|
|
328
|
+
* a provider call (and paying for tokens that get discarded) is pure waste.
|
|
329
|
+
*/
|
|
330
|
+
declare const MIN_STREAM_ATTEMPT_MS = 10000;
|
|
312
331
|
declare function parseModelString(model: string): {
|
|
313
332
|
provider: Provider;
|
|
314
333
|
modelId: string;
|
|
315
334
|
};
|
|
316
335
|
declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
|
|
317
336
|
|
|
318
|
-
export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
|
|
337
|
+
export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, MIN_STREAM_ATTEMPT_MS, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
|
package/dist/index.js
CHANGED
|
@@ -28,19 +28,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var proof_exports = {};
|
|
32
|
+
__export(proof_exports, {
|
|
33
33
|
ClaudeModel: () => ClaudeModel,
|
|
34
34
|
GPTModel: () => GPTModel,
|
|
35
35
|
GeminiModel: () => GeminiModel,
|
|
36
36
|
GroqModel: () => GroqModel,
|
|
37
|
+
MIN_STREAM_ATTEMPT_MS: () => MIN_STREAM_ATTEMPT_MS,
|
|
37
38
|
OPENROUTER_NONSTREAM_TIMEOUT_MS: () => OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
38
39
|
OPENROUTER_STREAM_TIMEOUT_MS: () => OPENROUTER_STREAM_TIMEOUT_MS,
|
|
39
40
|
OpenRouterModel: () => OpenRouterModel,
|
|
40
41
|
callWithRetries: () => callWithRetries,
|
|
41
42
|
parseModelString: () => parseModelString
|
|
42
43
|
});
|
|
43
|
-
module.exports = __toCommonJS(
|
|
44
|
+
module.exports = __toCommonJS(proof_exports);
|
|
44
45
|
|
|
45
46
|
// interfaces.ts
|
|
46
47
|
var ClaudeModel = /* @__PURE__ */ ((ClaudeModel2) => {
|
|
@@ -171,7 +172,8 @@ async function withRetries(identifier, apiName, fn, options = {}) {
|
|
|
171
172
|
return await fn();
|
|
172
173
|
} catch (error3) {
|
|
173
174
|
lastError = error3;
|
|
174
|
-
if ((_a = options.signal) == null ? void 0 : _a.aborted)
|
|
175
|
+
if ((_a = options.signal) == null ? void 0 : _a.aborted)
|
|
176
|
+
throw error3;
|
|
175
177
|
if (onError) {
|
|
176
178
|
onError(error3, attempt);
|
|
177
179
|
} else {
|
|
@@ -195,7 +197,8 @@ function parseStreamedResponse(identifier, paragraph, toolCallAccumulators, allo
|
|
|
195
197
|
const functionCalls = [];
|
|
196
198
|
for (let i = 0; i < toolCallAccumulators.length; i++) {
|
|
197
199
|
const acc = toolCallAccumulators[i];
|
|
198
|
-
if (!acc.name || !acc.arguments)
|
|
200
|
+
if (!acc.name || !acc.arguments)
|
|
201
|
+
continue;
|
|
199
202
|
if (allowedFunctionNames && !allowedFunctionNames.has(acc.name)) {
|
|
200
203
|
throw new Error(
|
|
201
204
|
`Stream error: received function call with unknown name: ${acc.name}`
|
|
@@ -334,7 +337,8 @@ function buildOpenAIRequestConfig(identifier, model, config) {
|
|
|
334
337
|
};
|
|
335
338
|
}
|
|
336
339
|
function filterOpenAICompatReasoningDetails(details) {
|
|
337
|
-
if (!Array.isArray(details))
|
|
340
|
+
if (!Array.isArray(details))
|
|
341
|
+
return details || void 0;
|
|
338
342
|
const blocks = details.filter(
|
|
339
343
|
(block) => typeof (block == null ? void 0 : block.type) === "string" && block.type.startsWith("reasoning.")
|
|
340
344
|
);
|
|
@@ -405,11 +409,13 @@ async function prepareOpenAIPayload(identifier, payload) {
|
|
|
405
409
|
};
|
|
406
410
|
});
|
|
407
411
|
}
|
|
408
|
-
if (message.reasoning)
|
|
412
|
+
if (message.reasoning)
|
|
413
|
+
outMessage.reasoning = message.reasoning;
|
|
409
414
|
const reasoningDetails = filterOpenAICompatReasoningDetails(
|
|
410
415
|
message.reasoningDetails
|
|
411
416
|
);
|
|
412
|
-
if (reasoningDetails)
|
|
417
|
+
if (reasoningDetails)
|
|
418
|
+
outMessage.reasoning_details = reasoningDetails;
|
|
413
419
|
preparedPayload.messages.push(outMessage);
|
|
414
420
|
}
|
|
415
421
|
return preparedPayload;
|
|
@@ -471,7 +477,8 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
|
|
|
471
477
|
}
|
|
472
478
|
const jsonStrings = chunk.split(/^data: /gm);
|
|
473
479
|
for (const jsonString of jsonStrings) {
|
|
474
|
-
if (!jsonString)
|
|
480
|
+
if (!jsonString)
|
|
481
|
+
continue;
|
|
475
482
|
if (jsonString.includes("[DONE]")) {
|
|
476
483
|
clearTimeout(overallTimeout);
|
|
477
484
|
return parseStreamedResponse(
|
|
@@ -509,7 +516,8 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
|
|
|
509
516
|
while (toolCallAccumulators.length <= idx) {
|
|
510
517
|
toolCallAccumulators.push({ name: "", arguments: "" });
|
|
511
518
|
}
|
|
512
|
-
if (toolCall.id)
|
|
519
|
+
if (toolCall.id)
|
|
520
|
+
toolCallAccumulators[idx].id = toolCall.id;
|
|
513
521
|
if ((_e = toolCall.function) == null ? void 0 : _e.name)
|
|
514
522
|
toolCallAccumulators[idx].name += toolCall.function.name;
|
|
515
523
|
if ((_f = toolCall.function) == null ? void 0 : _f.arguments)
|
|
@@ -517,9 +525,11 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
|
|
|
517
525
|
}
|
|
518
526
|
}
|
|
519
527
|
const text = (_h = (_g = json.choices[0]) == null ? void 0 : _g.delta) == null ? void 0 : _h.content;
|
|
520
|
-
if (text)
|
|
528
|
+
if (text)
|
|
529
|
+
paragraph += text;
|
|
521
530
|
const reasoningDelta = (_j = (_i = json.choices[0]) == null ? void 0 : _i.delta) == null ? void 0 : _j.reasoning;
|
|
522
|
-
if (reasoningDelta)
|
|
531
|
+
if (reasoningDelta)
|
|
532
|
+
reasoning += reasoningDelta;
|
|
523
533
|
}
|
|
524
534
|
}
|
|
525
535
|
}
|
|
@@ -664,7 +674,8 @@ function jigAnthropicMessages(messages) {
|
|
|
664
674
|
];
|
|
665
675
|
}
|
|
666
676
|
jiggedMessages = jiggedMessages.reduce((acc, message) => {
|
|
667
|
-
if (acc.length === 0)
|
|
677
|
+
if (acc.length === 0)
|
|
678
|
+
return [message];
|
|
668
679
|
const lastMessage = acc[acc.length - 1];
|
|
669
680
|
if (lastMessage.role === message.role) {
|
|
670
681
|
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text", text: lastMessage.content }];
|
|
@@ -918,7 +929,8 @@ function jigGoogleMessages(messages) {
|
|
|
918
929
|
];
|
|
919
930
|
}
|
|
920
931
|
jiggedMessages = jiggedMessages.reduce((acc, message) => {
|
|
921
|
-
if (acc.length === 0)
|
|
932
|
+
if (acc.length === 0)
|
|
933
|
+
return [message];
|
|
922
934
|
const lastMessage = acc[acc.length - 1];
|
|
923
935
|
if (lastMessage.role === message.role) {
|
|
924
936
|
lastMessage.parts = [...lastMessage.parts, ...message.parts];
|
|
@@ -956,7 +968,8 @@ async function prepareGoogleAIPayload(_identifier, payload) {
|
|
|
956
968
|
const toolNameById = /* @__PURE__ */ new Map();
|
|
957
969
|
for (const m of payload.messages) {
|
|
958
970
|
for (const fc of m.functionCalls || []) {
|
|
959
|
-
if (fc.id)
|
|
971
|
+
if (fc.id)
|
|
972
|
+
toolNameById.set(fc.id, fc.name);
|
|
960
973
|
}
|
|
961
974
|
}
|
|
962
975
|
for (const message of payload.messages) {
|
|
@@ -1033,7 +1046,8 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
|
|
|
1033
1046
|
contents,
|
|
1034
1047
|
generationConfig: { responseModalities: ["TEXT"] }
|
|
1035
1048
|
};
|
|
1036
|
-
if (payload.tools)
|
|
1049
|
+
if (payload.tools)
|
|
1050
|
+
requestBody.tools = [payload.tools];
|
|
1037
1051
|
if (payload.tools && payload.toolConfig) {
|
|
1038
1052
|
requestBody.toolConfig = payload.toolConfig;
|
|
1039
1053
|
}
|
|
@@ -1091,7 +1105,8 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
|
|
|
1091
1105
|
});
|
|
1092
1106
|
continue;
|
|
1093
1107
|
}
|
|
1094
|
-
if (part.text)
|
|
1108
|
+
if (part.text)
|
|
1109
|
+
text += part.text;
|
|
1095
1110
|
if ((_m = part.inlineData) == null ? void 0 : _m.data) {
|
|
1096
1111
|
files.push({ mimeType: "image/png", data: part.inlineData.data });
|
|
1097
1112
|
}
|
|
@@ -1168,15 +1183,21 @@ async function callGoogleAIWithRetries(id, payload, retries = 5, requestTimeoutM
|
|
|
1168
1183
|
finishReason: error2.finishReason,
|
|
1169
1184
|
modelVersion: error2.modelVersion
|
|
1170
1185
|
};
|
|
1171
|
-
if (error2.safetyRatings)
|
|
1172
|
-
|
|
1186
|
+
if (error2.safetyRatings)
|
|
1187
|
+
errorDetails.safetyRatings = error2.safetyRatings;
|
|
1188
|
+
if (error2.usageMetadata)
|
|
1189
|
+
errorDetails.usageMetadata = error2.usageMetadata;
|
|
1173
1190
|
if (error2.promptFeedback)
|
|
1174
1191
|
errorDetails.promptFeedback = error2.promptFeedback;
|
|
1175
|
-
if (error2.status)
|
|
1176
|
-
|
|
1177
|
-
if (error2.
|
|
1192
|
+
if (error2.status)
|
|
1193
|
+
errorDetails.httpStatus = error2.status;
|
|
1194
|
+
if (error2.code)
|
|
1195
|
+
errorDetails.errorCode = error2.code;
|
|
1196
|
+
if (error2.details)
|
|
1197
|
+
errorDetails.errorDetails = error2.details;
|
|
1178
1198
|
const fileUris = payload.messages.flatMap((m) => m.parts).filter((p) => "fileData" in p).map((p) => p.fileData.fileUri);
|
|
1179
|
-
if (fileUris.length)
|
|
1199
|
+
if (fileUris.length)
|
|
1200
|
+
errorDetails.fileUris = fileUris;
|
|
1180
1201
|
logger_default.error(
|
|
1181
1202
|
id,
|
|
1182
1203
|
`Retry #${attempt} error: ${error2.message}`,
|
|
@@ -1256,13 +1277,16 @@ function prepareOpenAICompatMessages(messages) {
|
|
|
1256
1277
|
}
|
|
1257
1278
|
};
|
|
1258
1279
|
});
|
|
1259
|
-
if (!message.content)
|
|
1280
|
+
if (!message.content)
|
|
1281
|
+
outMessage.content = null;
|
|
1260
1282
|
}
|
|
1261
|
-
if (message.reasoning)
|
|
1283
|
+
if (message.reasoning)
|
|
1284
|
+
outMessage.reasoning = message.reasoning;
|
|
1262
1285
|
const reasoningDetails = filterOpenAICompatReasoningDetails(
|
|
1263
1286
|
message.reasoningDetails
|
|
1264
1287
|
);
|
|
1265
|
-
if (reasoningDetails)
|
|
1288
|
+
if (reasoningDetails)
|
|
1289
|
+
outMessage.reasoning_details = reasoningDetails;
|
|
1266
1290
|
out.push(outMessage);
|
|
1267
1291
|
}
|
|
1268
1292
|
return out;
|
|
@@ -1405,7 +1429,8 @@ function parseDsmlToolCalls(content) {
|
|
|
1405
1429
|
let last = -1;
|
|
1406
1430
|
let tag;
|
|
1407
1431
|
while ((tag = DSML_ANY_TAG_RE.exec(content)) !== null) {
|
|
1408
|
-
if (first === -1)
|
|
1432
|
+
if (first === -1)
|
|
1433
|
+
first = tag.index;
|
|
1409
1434
|
last = tag.index + tag[0].length;
|
|
1410
1435
|
}
|
|
1411
1436
|
const remaining = first === -1 ? content : (content.slice(0, first) + content.slice(last)).trim();
|
|
@@ -1421,7 +1446,8 @@ function finalizeOpenRouterMessage(id, raw) {
|
|
|
1421
1446
|
const functionCalls = [];
|
|
1422
1447
|
for (let i = 0; i < raw.toolCalls.length; i++) {
|
|
1423
1448
|
const tc = raw.toolCalls[i];
|
|
1424
|
-
if (!tc.name)
|
|
1449
|
+
if (!tc.name)
|
|
1450
|
+
continue;
|
|
1425
1451
|
functionCalls.push({
|
|
1426
1452
|
id: (_a = tc.id) != null ? _a : `call_${i}`,
|
|
1427
1453
|
name: tc.name,
|
|
@@ -1472,7 +1498,8 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1472
1498
|
controller.abort();
|
|
1473
1499
|
};
|
|
1474
1500
|
const unref = (t) => {
|
|
1475
|
-
if (typeof t === "object" && "unref" in t)
|
|
1501
|
+
if (typeof t === "object" && "unref" in t)
|
|
1502
|
+
t.unref();
|
|
1476
1503
|
return t;
|
|
1477
1504
|
};
|
|
1478
1505
|
const totalTimer = unref(
|
|
@@ -1538,88 +1565,98 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1538
1565
|
const reader = response.body.getReader();
|
|
1539
1566
|
const decoder = new TextDecoder();
|
|
1540
1567
|
let lineBuffer = "";
|
|
1541
|
-
outer:
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
let
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
id,
|
|
1564
|
-
"OpenRouter stream: unparseable data line:",
|
|
1565
|
-
dataStr.slice(0, 200)
|
|
1566
|
-
);
|
|
1567
|
-
continue;
|
|
1568
|
-
}
|
|
1569
|
-
dataChunks++;
|
|
1570
|
-
if (json.error) {
|
|
1571
|
-
logger_default.error(id, "OpenRouter stream error event:", json.error);
|
|
1572
|
-
const error2 = new Error(
|
|
1573
|
-
`OpenRouter error: ${json.error.message}`
|
|
1574
|
-
);
|
|
1575
|
-
error2.data = json.error;
|
|
1576
|
-
throw error2;
|
|
1577
|
-
}
|
|
1578
|
-
if (json.provider) provider = json.provider;
|
|
1579
|
-
let useful = false;
|
|
1580
|
-
if (json.usage) {
|
|
1581
|
-
usage = json.usage;
|
|
1582
|
-
useful = true;
|
|
1583
|
-
}
|
|
1584
|
-
const choice = (_c = json.choices) == null ? void 0 : _c[0];
|
|
1585
|
-
if (choice) {
|
|
1586
|
-
const delta = (_d = choice.delta) != null ? _d : {};
|
|
1587
|
-
if (delta.content) {
|
|
1588
|
-
paragraph += delta.content;
|
|
1589
|
-
useful = true;
|
|
1568
|
+
outer:
|
|
1569
|
+
while (true) {
|
|
1570
|
+
const { done, value } = await reader.read();
|
|
1571
|
+
if (done)
|
|
1572
|
+
break;
|
|
1573
|
+
lineBuffer += decoder.decode(value, { stream: true });
|
|
1574
|
+
let newlineIdx;
|
|
1575
|
+
while ((newlineIdx = lineBuffer.indexOf("\n")) !== -1) {
|
|
1576
|
+
let line = lineBuffer.slice(0, newlineIdx);
|
|
1577
|
+
lineBuffer = lineBuffer.slice(newlineIdx + 1);
|
|
1578
|
+
if (line.endsWith("\r"))
|
|
1579
|
+
line = line.slice(0, -1);
|
|
1580
|
+
if (!line)
|
|
1581
|
+
continue;
|
|
1582
|
+
if (line.startsWith(":"))
|
|
1583
|
+
continue;
|
|
1584
|
+
if (!line.startsWith("data:"))
|
|
1585
|
+
continue;
|
|
1586
|
+
const dataStr = line.slice(5).trimStart();
|
|
1587
|
+
if (dataStr === "[DONE]") {
|
|
1588
|
+
sawDone = true;
|
|
1589
|
+
break outer;
|
|
1590
1590
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1591
|
+
let json;
|
|
1592
|
+
try {
|
|
1593
|
+
json = JSON.parse(dataStr);
|
|
1594
|
+
} catch (e) {
|
|
1595
|
+
logger_default.error(
|
|
1596
|
+
id,
|
|
1597
|
+
"OpenRouter stream: unparseable data line:",
|
|
1598
|
+
dataStr.slice(0, 200)
|
|
1599
|
+
);
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
dataChunks++;
|
|
1603
|
+
if (json.error) {
|
|
1604
|
+
logger_default.error(id, "OpenRouter stream error event:", json.error);
|
|
1605
|
+
const error2 = new Error(
|
|
1606
|
+
`OpenRouter error: ${json.error.message}`
|
|
1607
|
+
);
|
|
1608
|
+
error2.data = json.error;
|
|
1609
|
+
throw error2;
|
|
1594
1610
|
}
|
|
1595
|
-
if (
|
|
1596
|
-
|
|
1611
|
+
if (json.provider)
|
|
1612
|
+
provider = json.provider;
|
|
1613
|
+
let useful = false;
|
|
1614
|
+
if (json.usage) {
|
|
1615
|
+
usage = json.usage;
|
|
1597
1616
|
useful = true;
|
|
1598
1617
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1618
|
+
const choice = (_c = json.choices) == null ? void 0 : _c[0];
|
|
1619
|
+
if (choice) {
|
|
1620
|
+
const delta = (_d = choice.delta) != null ? _d : {};
|
|
1621
|
+
if (delta.content) {
|
|
1622
|
+
paragraph += delta.content;
|
|
1623
|
+
useful = true;
|
|
1624
|
+
}
|
|
1625
|
+
if (delta.reasoning) {
|
|
1626
|
+
reasoning += delta.reasoning;
|
|
1627
|
+
useful = true;
|
|
1628
|
+
}
|
|
1629
|
+
if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) {
|
|
1630
|
+
reasoningDetails.push(...delta.reasoning_details);
|
|
1631
|
+
useful = true;
|
|
1632
|
+
}
|
|
1633
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
1634
|
+
for (const toolCall of delta.tool_calls) {
|
|
1635
|
+
const idx = (_e = toolCall.index) != null ? _e : 0;
|
|
1636
|
+
while (toolCalls.length <= idx) {
|
|
1637
|
+
toolCalls.push({ name: "", argumentsJson: "" });
|
|
1638
|
+
}
|
|
1639
|
+
if (toolCall.id)
|
|
1640
|
+
toolCalls[idx].id = toolCall.id;
|
|
1641
|
+
if ((_f = toolCall.function) == null ? void 0 : _f.name)
|
|
1642
|
+
toolCalls[idx].name += toolCall.function.name;
|
|
1643
|
+
if ((_g = toolCall.function) == null ? void 0 : _g.arguments)
|
|
1644
|
+
toolCalls[idx].argumentsJson += toolCall.function.arguments;
|
|
1645
|
+
useful = true;
|
|
1604
1646
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
if ((_g = toolCall.function) == null ? void 0 : _g.arguments)
|
|
1609
|
-
toolCalls[idx].argumentsJson += toolCall.function.arguments;
|
|
1647
|
+
}
|
|
1648
|
+
if (choice.finish_reason) {
|
|
1649
|
+
finishReason = choice.finish_reason;
|
|
1610
1650
|
useful = true;
|
|
1611
1651
|
}
|
|
1612
1652
|
}
|
|
1613
|
-
if (
|
|
1614
|
-
|
|
1615
|
-
useful = true;
|
|
1616
|
-
}
|
|
1653
|
+
if (useful)
|
|
1654
|
+
armStallTimer();
|
|
1617
1655
|
}
|
|
1618
|
-
if (useful) armStallTimer();
|
|
1619
1656
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1657
|
+
if (sawDone)
|
|
1658
|
+
reader.cancel().catch(() => {
|
|
1659
|
+
});
|
|
1623
1660
|
if (!sawDone && !finishReason) {
|
|
1624
1661
|
logger_default.error(
|
|
1625
1662
|
id,
|
|
@@ -1658,7 +1695,9 @@ function parseOpenRouterBody(id, data) {
|
|
|
1658
1695
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1659
1696
|
if (data.error) {
|
|
1660
1697
|
logger_default.error(id, "OpenRouter error:", data.error);
|
|
1661
|
-
|
|
1698
|
+
const error2 = new Error(`OpenRouter error: ${data.error.message}`);
|
|
1699
|
+
error2.data = data.error;
|
|
1700
|
+
throw error2;
|
|
1662
1701
|
}
|
|
1663
1702
|
const answer = (_b = (_a = data.choices) == null ? void 0 : _a[0]) == null ? void 0 : _b.message;
|
|
1664
1703
|
if (!answer) {
|
|
@@ -1695,22 +1734,81 @@ async function callOpenRouterNonStreaming(id, payload, requestTimeoutMs = OPENRO
|
|
|
1695
1734
|
);
|
|
1696
1735
|
return parseOpenRouterBody(id, response.data);
|
|
1697
1736
|
}
|
|
1737
|
+
var normalizeProviderKey = (name) => name.split("/")[0].toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1738
|
+
var MODERATION_RE = /moderat|inappropriate content|content polic|content management|flagged/i;
|
|
1739
|
+
function moderationEvictionSlug(error2, payload) {
|
|
1740
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
1741
|
+
const body = (_c = error2 == null ? void 0 : error2.data) != null ? _c : (_b = (_a = error2 == null ? void 0 : error2.response) == null ? void 0 : _a.data) == null ? void 0 : _b.error;
|
|
1742
|
+
if (!body)
|
|
1743
|
+
return null;
|
|
1744
|
+
const message = String((_d = body.message) != null ? _d : "");
|
|
1745
|
+
const isModeration = body.code === 403 || Array.isArray((_e = body.metadata) == null ? void 0 : _e.reasons) || MODERATION_RE.test(message);
|
|
1746
|
+
if (!isModeration)
|
|
1747
|
+
return null;
|
|
1748
|
+
const display = (_h = (_f = body.metadata) == null ? void 0 : _f.provider_name) != null ? _h : (_g = /Upstream error from ([^:]+):/.exec(message)) == null ? void 0 : _g[1];
|
|
1749
|
+
if (!display)
|
|
1750
|
+
return null;
|
|
1751
|
+
const key = normalizeProviderKey(display);
|
|
1752
|
+
const fromOrder = (_j = (_i = payload.provider) == null ? void 0 : _i.order) == null ? void 0 : _j.find(
|
|
1753
|
+
(entry) => normalizeProviderKey(entry) === key
|
|
1754
|
+
);
|
|
1755
|
+
return fromOrder ? fromOrder.split("/")[0] : display.toLowerCase();
|
|
1756
|
+
}
|
|
1757
|
+
var MIN_STREAM_ATTEMPT_MS = 1e4;
|
|
1758
|
+
function streamAttemptBudgetMs(options) {
|
|
1759
|
+
if (options.streamDeadlineAt === void 0)
|
|
1760
|
+
return options.streamTimeoutMs;
|
|
1761
|
+
const remaining = options.streamDeadlineAt - Date.now();
|
|
1762
|
+
if (remaining < MIN_STREAM_ATTEMPT_MS)
|
|
1763
|
+
return null;
|
|
1764
|
+
return Math.min(options.streamTimeoutMs, remaining);
|
|
1765
|
+
}
|
|
1698
1766
|
async function callOpenRouterWithRetries(id, payload, retries = 5, options, signal) {
|
|
1767
|
+
const evicted = [];
|
|
1699
1768
|
return withRetries(
|
|
1700
1769
|
id,
|
|
1701
1770
|
"OpenRouter",
|
|
1702
|
-
() => options.streaming ?
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1771
|
+
() => (options.streaming ? (() => {
|
|
1772
|
+
const budget = streamAttemptBudgetMs(options);
|
|
1773
|
+
if (budget === null) {
|
|
1774
|
+
const error2 = new Error(
|
|
1775
|
+
"OpenRouter stream skipped: caller deadline leaves too little time for another attempt"
|
|
1776
|
+
);
|
|
1777
|
+
error2.deadlineExceeded = true;
|
|
1778
|
+
logger_default.error(id, error2.message);
|
|
1779
|
+
throw error2;
|
|
1780
|
+
}
|
|
1781
|
+
return callOpenRouterStream(
|
|
1782
|
+
id,
|
|
1783
|
+
payload,
|
|
1784
|
+
budget,
|
|
1785
|
+
options.chunkTimeoutMs,
|
|
1786
|
+
signal
|
|
1787
|
+
);
|
|
1788
|
+
})() : callOpenRouterNonStreaming(
|
|
1709
1789
|
id,
|
|
1710
1790
|
payload,
|
|
1711
1791
|
options.requestTimeoutMs,
|
|
1712
1792
|
signal
|
|
1713
|
-
)
|
|
1793
|
+
)).catch((error2) => {
|
|
1794
|
+
var _a, _b, _c, _d;
|
|
1795
|
+
const slug = moderationEvictionSlug(error2, payload);
|
|
1796
|
+
if (slug && !evicted.includes(slug)) {
|
|
1797
|
+
evicted.push(slug);
|
|
1798
|
+
payload.provider = {
|
|
1799
|
+
...payload.provider,
|
|
1800
|
+
ignore: [...(_b = (_a = payload.provider) == null ? void 0 : _a.ignore) != null ? _b : [], slug],
|
|
1801
|
+
order: (_d = (_c = payload.provider) == null ? void 0 : _c.order) == null ? void 0 : _d.filter(
|
|
1802
|
+
(entry) => normalizeProviderKey(entry) !== normalizeProviderKey(slug)
|
|
1803
|
+
)
|
|
1804
|
+
};
|
|
1805
|
+
logger_default.log(
|
|
1806
|
+
id,
|
|
1807
|
+
`OpenRouter moderation eviction: ignoring provider "${slug}" for remaining attempts`
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
throw error2;
|
|
1811
|
+
}),
|
|
1714
1812
|
{ retries, signal }
|
|
1715
1813
|
);
|
|
1716
1814
|
}
|
|
@@ -1807,6 +1905,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1807
1905
|
{
|
|
1808
1906
|
streaming: (_b = aiPayload.streaming) != null ? _b : true,
|
|
1809
1907
|
streamTimeoutMs: (_c = aiPayload.streamTimeoutMs) != null ? _c : OPENROUTER_STREAM_TIMEOUT_MS,
|
|
1908
|
+
streamDeadlineAt: aiPayload.streamDeadlineAt,
|
|
1810
1909
|
requestTimeoutMs: (_d = aiPayload.requestTimeoutMs) != null ? _d : OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
1811
1910
|
chunkTimeoutMs
|
|
1812
1911
|
},
|
|
@@ -1817,7 +1916,8 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1817
1916
|
(_e = result.provider) != null ? _e : result.provider = provider;
|
|
1818
1917
|
return result;
|
|
1819
1918
|
} catch (error2) {
|
|
1820
|
-
if ((_f = aiPayload.signal) == null ? void 0 : _f.aborted)
|
|
1919
|
+
if ((_f = aiPayload.signal) == null ? void 0 : _f.aborted)
|
|
1920
|
+
throw error2;
|
|
1821
1921
|
if (aiPayload.fallbackModel) {
|
|
1822
1922
|
logger_default.error(
|
|
1823
1923
|
id,
|
|
@@ -1848,6 +1948,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1848
1948
|
GPTModel,
|
|
1849
1949
|
GeminiModel,
|
|
1850
1950
|
GroqModel,
|
|
1951
|
+
MIN_STREAM_ATTEMPT_MS,
|
|
1851
1952
|
OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
1852
1953
|
OPENROUTER_STREAM_TIMEOUT_MS,
|
|
1853
1954
|
OpenRouterModel,
|