190proof 1.0.107 → 1.0.109
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 +4 -2
- package/dist/index.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +284 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +282 -58
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -273,9 +273,11 @@ Main function to make requests to any supported AI provider.
|
|
|
273
273
|
- `retries`: `number` - Number of retry attempts (default: 5)
|
|
274
274
|
- `chunkTimeoutMs`: `number` - Timeout for streaming chunks in ms (default: 15000)
|
|
275
275
|
|
|
276
|
-
|
|
276
|
+
Optional per-request knobs live on `payload` (`GenericPayload`):
|
|
277
277
|
|
|
278
|
-
- `payload.requestTimeoutMs`: `number` - Per-attempt HTTP timeout in ms (default: 120000), honored by every adapter.
|
|
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
|
+
- `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
|
+
- `payload.streamTimeoutMs`: `number` - OpenRouter-only: total wall-clock budget per streaming attempt (default: 600000).
|
|
279
281
|
- `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.
|
|
280
282
|
|
|
281
283
|
#### Returns
|
package/dist/index.d.mts
CHANGED
|
@@ -279,6 +279,24 @@ interface GenericPayload {
|
|
|
279
279
|
* valid response isn't cut short.
|
|
280
280
|
*/
|
|
281
281
|
requestTimeoutMs?: number;
|
|
282
|
+
/**
|
|
283
|
+
* OpenRouter-only: stream the completion over SSE instead of waiting for a
|
|
284
|
+
* single JSON body. Defaults to true. Streaming attempts are bounded by
|
|
285
|
+
* `streamTimeoutMs` (total) plus a per-useful-chunk stall timeout — NOT by
|
|
286
|
+
* `requestTimeoutMs`, which only governs non-streaming attempts (default
|
|
287
|
+
* 180s for OpenRouter). Set to false to force the old non-streaming path.
|
|
288
|
+
*/
|
|
289
|
+
streaming?: boolean;
|
|
290
|
+
/**
|
|
291
|
+
* OpenRouter-only: total wall-clock budget in ms for one streaming attempt
|
|
292
|
+
* (connect + full generation). Defaults to 600s. Independent of
|
|
293
|
+
* `requestTimeoutMs` by design: a healthy long generation keeps streaming
|
|
294
|
+
* useful chunks and may run far past any sane non-streaming deadline, while
|
|
295
|
+
* a hung one is killed much earlier by the per-useful-chunk stall timeout
|
|
296
|
+
* (`chunkTimeoutMs` argument of `callWithRetries`, default 15s — reset only
|
|
297
|
+
* by chunks that advance the output, never by keep-alive bytes/comments).
|
|
298
|
+
*/
|
|
299
|
+
streamTimeoutMs?: number;
|
|
282
300
|
/**
|
|
283
301
|
* Optional caller-supplied cancellation signal. When it aborts, the in-flight
|
|
284
302
|
* provider request is cancelled and `callWithRetries` rejects immediately —
|
|
@@ -289,10 +307,12 @@ interface GenericPayload {
|
|
|
289
307
|
signal?: AbortSignal;
|
|
290
308
|
}
|
|
291
309
|
|
|
310
|
+
declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
|
|
311
|
+
declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
|
|
292
312
|
declare function parseModelString(model: string): {
|
|
293
313
|
provider: Provider;
|
|
294
314
|
modelId: string;
|
|
295
315
|
};
|
|
296
316
|
declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
|
|
297
317
|
|
|
298
|
-
export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -279,6 +279,24 @@ interface GenericPayload {
|
|
|
279
279
|
* valid response isn't cut short.
|
|
280
280
|
*/
|
|
281
281
|
requestTimeoutMs?: number;
|
|
282
|
+
/**
|
|
283
|
+
* OpenRouter-only: stream the completion over SSE instead of waiting for a
|
|
284
|
+
* single JSON body. Defaults to true. Streaming attempts are bounded by
|
|
285
|
+
* `streamTimeoutMs` (total) plus a per-useful-chunk stall timeout — NOT by
|
|
286
|
+
* `requestTimeoutMs`, which only governs non-streaming attempts (default
|
|
287
|
+
* 180s for OpenRouter). Set to false to force the old non-streaming path.
|
|
288
|
+
*/
|
|
289
|
+
streaming?: boolean;
|
|
290
|
+
/**
|
|
291
|
+
* OpenRouter-only: total wall-clock budget in ms for one streaming attempt
|
|
292
|
+
* (connect + full generation). Defaults to 600s. Independent of
|
|
293
|
+
* `requestTimeoutMs` by design: a healthy long generation keeps streaming
|
|
294
|
+
* useful chunks and may run far past any sane non-streaming deadline, while
|
|
295
|
+
* a hung one is killed much earlier by the per-useful-chunk stall timeout
|
|
296
|
+
* (`chunkTimeoutMs` argument of `callWithRetries`, default 15s — reset only
|
|
297
|
+
* by chunks that advance the output, never by keep-alive bytes/comments).
|
|
298
|
+
*/
|
|
299
|
+
streamTimeoutMs?: number;
|
|
282
300
|
/**
|
|
283
301
|
* Optional caller-supplied cancellation signal. When it aborts, the in-flight
|
|
284
302
|
* provider request is cancelled and `callWithRetries` rejects immediately —
|
|
@@ -289,10 +307,12 @@ interface GenericPayload {
|
|
|
289
307
|
signal?: AbortSignal;
|
|
290
308
|
}
|
|
291
309
|
|
|
310
|
+
declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
|
|
311
|
+
declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
|
|
292
312
|
declare function parseModelString(model: string): {
|
|
293
313
|
provider: Provider;
|
|
294
314
|
modelId: string;
|
|
295
315
|
};
|
|
296
316
|
declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
|
|
297
317
|
|
|
298
|
-
export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,8 @@ __export(index_exports, {
|
|
|
34
34
|
GPTModel: () => GPTModel,
|
|
35
35
|
GeminiModel: () => GeminiModel,
|
|
36
36
|
GroqModel: () => GroqModel,
|
|
37
|
+
OPENROUTER_NONSTREAM_TIMEOUT_MS: () => OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
38
|
+
OPENROUTER_STREAM_TIMEOUT_MS: () => OPENROUTER_STREAM_TIMEOUT_MS,
|
|
37
39
|
OpenRouterModel: () => OpenRouterModel,
|
|
38
40
|
callWithRetries: () => callWithRetries,
|
|
39
41
|
parseModelString: () => parseModelString
|
|
@@ -1409,47 +1411,25 @@ function parseDsmlToolCalls(content) {
|
|
|
1409
1411
|
const remaining = first === -1 ? content : (content.slice(0, first) + content.slice(last)).trim();
|
|
1410
1412
|
return { calls, remainingContent: remaining.length ? remaining : null };
|
|
1411
1413
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
// Override point for tests (deadline behavior needs a local server).
|
|
1420
|
-
`${process.env.OPENROUTER_BASE_URL || "https://openrouter.ai"}/api/v1/chat/completions`,
|
|
1421
|
-
payload,
|
|
1422
|
-
{
|
|
1423
|
-
headers: {
|
|
1424
|
-
"content-type": "application/json",
|
|
1425
|
-
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`
|
|
1426
|
-
},
|
|
1427
|
-
timeout: requestTimeoutMs,
|
|
1428
|
-
signal: mergedSignal
|
|
1429
|
-
}
|
|
1430
|
-
)
|
|
1431
|
-
);
|
|
1432
|
-
if (response.data.error) {
|
|
1433
|
-
logger_default.error(id, "OpenRouter error:", response.data.error);
|
|
1434
|
-
throw new Error(`OpenRouter error: ${response.data.error.message}`);
|
|
1435
|
-
}
|
|
1436
|
-
const answer = (_b = (_a = response.data.choices) == null ? void 0 : _a[0]) == null ? void 0 : _b.message;
|
|
1437
|
-
if (!answer) {
|
|
1438
|
-
logger_default.error(id, "Missing answer in OpenRouter API response:", response.data);
|
|
1439
|
-
throw new Error("Missing answer in OpenRouter API");
|
|
1440
|
-
}
|
|
1414
|
+
var OPENROUTER_STREAM_TIMEOUT_MS = 6e5;
|
|
1415
|
+
var OPENROUTER_NONSTREAM_TIMEOUT_MS = 18e4;
|
|
1416
|
+
function openRouterEndpoint() {
|
|
1417
|
+
return `${process.env.OPENROUTER_BASE_URL || "https://openrouter.ai"}/api/v1/chat/completions`;
|
|
1418
|
+
}
|
|
1419
|
+
function finalizeOpenRouterMessage(id, raw) {
|
|
1420
|
+
var _a, _b, _c, _d, _e;
|
|
1441
1421
|
const functionCalls = [];
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1422
|
+
for (let i = 0; i < raw.toolCalls.length; i++) {
|
|
1423
|
+
const tc = raw.toolCalls[i];
|
|
1424
|
+
if (!tc.name) continue;
|
|
1425
|
+
functionCalls.push({
|
|
1426
|
+
id: (_a = tc.id) != null ? _a : `call_${i}`,
|
|
1427
|
+
name: tc.name,
|
|
1428
|
+
// Streamed no-arg calls can close with an empty fragment; treat as {}.
|
|
1429
|
+
arguments: tc.argumentsJson.trim() ? JSON.parse(tc.argumentsJson) : {}
|
|
1430
|
+
});
|
|
1451
1431
|
}
|
|
1452
|
-
let content =
|
|
1432
|
+
let content = raw.content;
|
|
1453
1433
|
if (!functionCalls.length && content && DSML_ENVELOPE_RE.test(content)) {
|
|
1454
1434
|
const { calls, remainingContent } = parseDsmlToolCalls(content);
|
|
1455
1435
|
if (calls.length) {
|
|
@@ -1459,11 +1439,7 @@ async function callOpenRouter(id, payload, requestTimeoutMs = 12e4, signal) {
|
|
|
1459
1439
|
}
|
|
1460
1440
|
const hasUnparsedDsml = !!content && DSML_DELIMITER_RE.test(content);
|
|
1461
1441
|
if (!functionCalls.length && (!content || hasUnparsedDsml)) {
|
|
1462
|
-
logger_default.error(
|
|
1463
|
-
id,
|
|
1464
|
-
"OpenRouter: empty or unparseable completion:",
|
|
1465
|
-
JSON.stringify(response.data)
|
|
1466
|
-
);
|
|
1442
|
+
logger_default.error(id, "OpenRouter: empty or unparseable completion:", raw.forLog());
|
|
1467
1443
|
throw new Error(
|
|
1468
1444
|
"OpenRouter: received message without usable content or function_call"
|
|
1469
1445
|
);
|
|
@@ -1474,24 +1450,267 @@ async function callOpenRouter(id, payload, requestTimeoutMs = 12e4, signal) {
|
|
|
1474
1450
|
function_call: functionCalls[0] || null,
|
|
1475
1451
|
function_calls: functionCalls,
|
|
1476
1452
|
files: [],
|
|
1477
|
-
reasoning:
|
|
1478
|
-
reasoningDetails: (
|
|
1453
|
+
reasoning: raw.reasoning || void 0,
|
|
1454
|
+
reasoningDetails: (_b = raw.reasoningDetails) != null ? _b : void 0,
|
|
1479
1455
|
// The upstream provider OpenRouter routed to (e.g. "Baidu") — finer-grained
|
|
1480
1456
|
// than the "openrouter" stamp callWithRetries would apply.
|
|
1481
|
-
provider: (
|
|
1482
|
-
usage:
|
|
1483
|
-
prompt_tokens:
|
|
1484
|
-
completion_tokens:
|
|
1485
|
-
total_tokens:
|
|
1486
|
-
cached_tokens: (
|
|
1457
|
+
provider: (_c = raw.provider) != null ? _c : void 0,
|
|
1458
|
+
usage: raw.usage ? {
|
|
1459
|
+
prompt_tokens: raw.usage.prompt_tokens,
|
|
1460
|
+
completion_tokens: raw.usage.completion_tokens,
|
|
1461
|
+
total_tokens: raw.usage.total_tokens,
|
|
1462
|
+
cached_tokens: (_e = (_d = raw.usage.prompt_tokens_details) == null ? void 0 : _d.cached_tokens) != null ? _e : 0
|
|
1487
1463
|
} : null
|
|
1488
1464
|
};
|
|
1489
1465
|
}
|
|
1490
|
-
async function
|
|
1466
|
+
async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_STREAM_TIMEOUT_MS, chunkTimeoutMs = 15e3, signal) {
|
|
1467
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1468
|
+
const controller = new AbortController();
|
|
1469
|
+
let abortReason = null;
|
|
1470
|
+
const abortWith = (reason) => {
|
|
1471
|
+
abortReason = reason;
|
|
1472
|
+
controller.abort();
|
|
1473
|
+
};
|
|
1474
|
+
const unref = (t) => {
|
|
1475
|
+
if (typeof t === "object" && "unref" in t) t.unref();
|
|
1476
|
+
return t;
|
|
1477
|
+
};
|
|
1478
|
+
const totalTimer = unref(
|
|
1479
|
+
setTimeout(
|
|
1480
|
+
() => abortWith(
|
|
1481
|
+
`OpenRouter stream exceeded total deadline of ${streamTimeoutMs}ms`
|
|
1482
|
+
),
|
|
1483
|
+
streamTimeoutMs
|
|
1484
|
+
)
|
|
1485
|
+
);
|
|
1486
|
+
let stallTimer;
|
|
1487
|
+
const armStallTimer = () => {
|
|
1488
|
+
clearTimeout(stallTimer);
|
|
1489
|
+
stallTimer = unref(
|
|
1490
|
+
setTimeout(
|
|
1491
|
+
() => abortWith(
|
|
1492
|
+
`OpenRouter stream stalled: no useful chunk for ${chunkTimeoutMs}ms`
|
|
1493
|
+
),
|
|
1494
|
+
chunkTimeoutMs
|
|
1495
|
+
)
|
|
1496
|
+
);
|
|
1497
|
+
};
|
|
1498
|
+
let paragraph = "";
|
|
1499
|
+
let reasoning = "";
|
|
1500
|
+
const reasoningDetails = [];
|
|
1501
|
+
const toolCalls = [];
|
|
1502
|
+
let provider;
|
|
1503
|
+
let usage = null;
|
|
1504
|
+
let finishReason = null;
|
|
1505
|
+
let sawDone = false;
|
|
1506
|
+
let dataChunks = 0;
|
|
1507
|
+
try {
|
|
1508
|
+
armStallTimer();
|
|
1509
|
+
const response = await fetch(openRouterEndpoint(), {
|
|
1510
|
+
method: "POST",
|
|
1511
|
+
headers: {
|
|
1512
|
+
"content-type": "application/json",
|
|
1513
|
+
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`
|
|
1514
|
+
},
|
|
1515
|
+
body: JSON.stringify({ ...payload, stream: true, usage: { include: true } }),
|
|
1516
|
+
signal: signal ? anySignal([controller.signal, signal]) : controller.signal
|
|
1517
|
+
});
|
|
1518
|
+
if (!response.ok) {
|
|
1519
|
+
let data;
|
|
1520
|
+
try {
|
|
1521
|
+
data = await response.json();
|
|
1522
|
+
} catch (e) {
|
|
1523
|
+
data = void 0;
|
|
1524
|
+
}
|
|
1525
|
+
logger_default.error(id, `OpenRouter stream HTTP ${response.status}:`, data);
|
|
1526
|
+
const error2 = new Error(
|
|
1527
|
+
`OpenRouter error: ${((_a = data == null ? void 0 : data.error) == null ? void 0 : _a.message) || `HTTP ${response.status}`}`
|
|
1528
|
+
);
|
|
1529
|
+
error2.response = { status: response.status, data };
|
|
1530
|
+
throw error2;
|
|
1531
|
+
}
|
|
1532
|
+
if ((_b = response.headers.get("content-type")) == null ? void 0 : _b.includes("application/json")) {
|
|
1533
|
+
return parseOpenRouterBody(id, await response.json());
|
|
1534
|
+
}
|
|
1535
|
+
if (!response.body) {
|
|
1536
|
+
throw new Error("OpenRouter stream error: no response body");
|
|
1537
|
+
}
|
|
1538
|
+
const reader = response.body.getReader();
|
|
1539
|
+
const decoder = new TextDecoder();
|
|
1540
|
+
let lineBuffer = "";
|
|
1541
|
+
outer: while (true) {
|
|
1542
|
+
const { done, value } = await reader.read();
|
|
1543
|
+
if (done) break;
|
|
1544
|
+
lineBuffer += decoder.decode(value, { stream: true });
|
|
1545
|
+
let newlineIdx;
|
|
1546
|
+
while ((newlineIdx = lineBuffer.indexOf("\n")) !== -1) {
|
|
1547
|
+
let line = lineBuffer.slice(0, newlineIdx);
|
|
1548
|
+
lineBuffer = lineBuffer.slice(newlineIdx + 1);
|
|
1549
|
+
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
1550
|
+
if (!line) continue;
|
|
1551
|
+
if (line.startsWith(":")) continue;
|
|
1552
|
+
if (!line.startsWith("data:")) continue;
|
|
1553
|
+
const dataStr = line.slice(5).trimStart();
|
|
1554
|
+
if (dataStr === "[DONE]") {
|
|
1555
|
+
sawDone = true;
|
|
1556
|
+
break outer;
|
|
1557
|
+
}
|
|
1558
|
+
let json;
|
|
1559
|
+
try {
|
|
1560
|
+
json = JSON.parse(dataStr);
|
|
1561
|
+
} catch (e) {
|
|
1562
|
+
logger_default.error(
|
|
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;
|
|
1590
|
+
}
|
|
1591
|
+
if (delta.reasoning) {
|
|
1592
|
+
reasoning += delta.reasoning;
|
|
1593
|
+
useful = true;
|
|
1594
|
+
}
|
|
1595
|
+
if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) {
|
|
1596
|
+
reasoningDetails.push(...delta.reasoning_details);
|
|
1597
|
+
useful = true;
|
|
1598
|
+
}
|
|
1599
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
1600
|
+
for (const toolCall of delta.tool_calls) {
|
|
1601
|
+
const idx = (_e = toolCall.index) != null ? _e : 0;
|
|
1602
|
+
while (toolCalls.length <= idx) {
|
|
1603
|
+
toolCalls.push({ name: "", argumentsJson: "" });
|
|
1604
|
+
}
|
|
1605
|
+
if (toolCall.id) toolCalls[idx].id = toolCall.id;
|
|
1606
|
+
if ((_f = toolCall.function) == null ? void 0 : _f.name)
|
|
1607
|
+
toolCalls[idx].name += toolCall.function.name;
|
|
1608
|
+
if ((_g = toolCall.function) == null ? void 0 : _g.arguments)
|
|
1609
|
+
toolCalls[idx].argumentsJson += toolCall.function.arguments;
|
|
1610
|
+
useful = true;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
if (choice.finish_reason) {
|
|
1614
|
+
finishReason = choice.finish_reason;
|
|
1615
|
+
useful = true;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
if (useful) armStallTimer();
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
if (sawDone) reader.cancel().catch(() => {
|
|
1622
|
+
});
|
|
1623
|
+
if (!sawDone && !finishReason) {
|
|
1624
|
+
logger_default.error(
|
|
1625
|
+
id,
|
|
1626
|
+
`OpenRouter stream ended prematurely after ${dataChunks} data chunks`
|
|
1627
|
+
);
|
|
1628
|
+
throw new Error("OpenRouter stream error: ended prematurely");
|
|
1629
|
+
}
|
|
1630
|
+
return finalizeOpenRouterMessage(id, {
|
|
1631
|
+
content: paragraph || null,
|
|
1632
|
+
toolCalls,
|
|
1633
|
+
reasoning,
|
|
1634
|
+
reasoningDetails: reasoningDetails.length ? reasoningDetails : void 0,
|
|
1635
|
+
provider,
|
|
1636
|
+
usage,
|
|
1637
|
+
forLog: () => JSON.stringify({
|
|
1638
|
+
finishReason,
|
|
1639
|
+
provider,
|
|
1640
|
+
usage,
|
|
1641
|
+
paragraph: paragraph.slice(0, 500),
|
|
1642
|
+
reasoningChars: reasoning.length,
|
|
1643
|
+
toolCalls
|
|
1644
|
+
})
|
|
1645
|
+
});
|
|
1646
|
+
} catch (error2) {
|
|
1647
|
+
if (abortReason && !(signal == null ? void 0 : signal.aborted)) {
|
|
1648
|
+
logger_default.error(id, abortReason);
|
|
1649
|
+
throw new Error(abortReason);
|
|
1650
|
+
}
|
|
1651
|
+
throw error2;
|
|
1652
|
+
} finally {
|
|
1653
|
+
clearTimeout(totalTimer);
|
|
1654
|
+
clearTimeout(stallTimer);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
function parseOpenRouterBody(id, data) {
|
|
1658
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1659
|
+
if (data.error) {
|
|
1660
|
+
logger_default.error(id, "OpenRouter error:", data.error);
|
|
1661
|
+
throw new Error(`OpenRouter error: ${data.error.message}`);
|
|
1662
|
+
}
|
|
1663
|
+
const answer = (_b = (_a = data.choices) == null ? void 0 : _a[0]) == null ? void 0 : _b.message;
|
|
1664
|
+
if (!answer) {
|
|
1665
|
+
logger_default.error(id, "Missing answer in OpenRouter API response:", data);
|
|
1666
|
+
throw new Error("Missing answer in OpenRouter API");
|
|
1667
|
+
}
|
|
1668
|
+
return finalizeOpenRouterMessage(id, {
|
|
1669
|
+
content: (_c = answer.content) != null ? _c : null,
|
|
1670
|
+
toolCalls: ((_d = answer.tool_calls) != null ? _d : []).map((tc) => ({
|
|
1671
|
+
id: tc.id,
|
|
1672
|
+
name: tc.function.name,
|
|
1673
|
+
argumentsJson: tc.function.arguments
|
|
1674
|
+
})),
|
|
1675
|
+
reasoning: (_e = answer.reasoning) != null ? _e : void 0,
|
|
1676
|
+
reasoningDetails: (_f = answer.reasoning_details) != null ? _f : void 0,
|
|
1677
|
+
provider: (_g = data.provider) != null ? _g : void 0,
|
|
1678
|
+
usage: (_h = data.usage) != null ? _h : null,
|
|
1679
|
+
forLog: () => JSON.stringify(data)
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
async function callOpenRouterNonStreaming(id, payload, requestTimeoutMs = OPENROUTER_NONSTREAM_TIMEOUT_MS, signal) {
|
|
1683
|
+
const response = await withRequestDeadline(
|
|
1684
|
+
"OpenRouter",
|
|
1685
|
+
requestTimeoutMs,
|
|
1686
|
+
signal,
|
|
1687
|
+
(mergedSignal) => import_axios.default.post(openRouterEndpoint(), payload, {
|
|
1688
|
+
headers: {
|
|
1689
|
+
"content-type": "application/json",
|
|
1690
|
+
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`
|
|
1691
|
+
},
|
|
1692
|
+
timeout: requestTimeoutMs,
|
|
1693
|
+
signal: mergedSignal
|
|
1694
|
+
})
|
|
1695
|
+
);
|
|
1696
|
+
return parseOpenRouterBody(id, response.data);
|
|
1697
|
+
}
|
|
1698
|
+
async function callOpenRouterWithRetries(id, payload, retries = 5, options, signal) {
|
|
1491
1699
|
return withRetries(
|
|
1492
1700
|
id,
|
|
1493
1701
|
"OpenRouter",
|
|
1494
|
-
() =>
|
|
1702
|
+
() => options.streaming ? callOpenRouterStream(
|
|
1703
|
+
id,
|
|
1704
|
+
payload,
|
|
1705
|
+
options.streamTimeoutMs,
|
|
1706
|
+
options.chunkTimeoutMs,
|
|
1707
|
+
signal
|
|
1708
|
+
) : callOpenRouterNonStreaming(
|
|
1709
|
+
id,
|
|
1710
|
+
payload,
|
|
1711
|
+
options.requestTimeoutMs,
|
|
1712
|
+
signal
|
|
1713
|
+
),
|
|
1495
1714
|
{ retries, signal }
|
|
1496
1715
|
);
|
|
1497
1716
|
}
|
|
@@ -1533,7 +1752,7 @@ function parseModelString(model) {
|
|
|
1533
1752
|
);
|
|
1534
1753
|
}
|
|
1535
1754
|
async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeoutMs = 15e3) {
|
|
1536
|
-
var _a, _b, _c;
|
|
1755
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1537
1756
|
try {
|
|
1538
1757
|
const { provider, modelId } = parseModelString(aiPayload.model);
|
|
1539
1758
|
const routingPayload = { ...aiPayload, model: modelId };
|
|
@@ -1585,15 +1804,20 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1585
1804
|
id,
|
|
1586
1805
|
prepareOpenRouterPayload(routingPayload),
|
|
1587
1806
|
retries,
|
|
1588
|
-
|
|
1807
|
+
{
|
|
1808
|
+
streaming: (_b = aiPayload.streaming) != null ? _b : true,
|
|
1809
|
+
streamTimeoutMs: (_c = aiPayload.streamTimeoutMs) != null ? _c : OPENROUTER_STREAM_TIMEOUT_MS,
|
|
1810
|
+
requestTimeoutMs: (_d = aiPayload.requestTimeoutMs) != null ? _d : OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
1811
|
+
chunkTimeoutMs
|
|
1812
|
+
},
|
|
1589
1813
|
signal
|
|
1590
1814
|
);
|
|
1591
1815
|
break;
|
|
1592
1816
|
}
|
|
1593
|
-
(
|
|
1817
|
+
(_e = result.provider) != null ? _e : result.provider = provider;
|
|
1594
1818
|
return result;
|
|
1595
1819
|
} catch (error2) {
|
|
1596
|
-
if ((
|
|
1820
|
+
if ((_f = aiPayload.signal) == null ? void 0 : _f.aborted) throw error2;
|
|
1597
1821
|
if (aiPayload.fallbackModel) {
|
|
1598
1822
|
logger_default.error(
|
|
1599
1823
|
id,
|
|
@@ -1624,6 +1848,8 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1624
1848
|
GPTModel,
|
|
1625
1849
|
GeminiModel,
|
|
1626
1850
|
GroqModel,
|
|
1851
|
+
OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
1852
|
+
OPENROUTER_STREAM_TIMEOUT_MS,
|
|
1627
1853
|
OpenRouterModel,
|
|
1628
1854
|
callWithRetries,
|
|
1629
1855
|
parseModelString
|