190proof 1.0.111 → 1.0.113
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 +2 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +37 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +37 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -280,6 +280,8 @@ Optional per-request knobs live on `payload` (`GenericPayload`):
|
|
|
280
280
|
- `payload.streamTimeoutMs`: `number` - OpenRouter-only: total wall-clock budget per streaming attempt (default: 600000).
|
|
281
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
282
|
|
|
283
|
+
When a streaming attempt is cut at its **total deadline** and prose has already arrived, the partial answer is returned with `truncated: true` on the response rather than discarded — those tokens were generated and billed, so throwing them away costs money and gives the user nothing. Surface such a reply as incomplete. Salvage never applies to tool-call turns (half-streamed arguments are unparseable JSON), to stalls (the provider died mid-thought), or to caller aborts. When nothing is salvageable, the discard is logged with an approximate token count — aborted attempts never receive OpenRouter's `usage` chunk, so that log line is the only record of the wasted spend.
|
|
284
|
+
|
|
283
285
|
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.
|
|
284
286
|
- `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.
|
|
285
287
|
|
package/dist/index.d.mts
CHANGED
|
@@ -184,6 +184,15 @@ interface ParsedResponseMessage {
|
|
|
184
184
|
* mismatch with the requested model's provider reveals the fallback.
|
|
185
185
|
*/
|
|
186
186
|
provider?: string;
|
|
187
|
+
/**
|
|
188
|
+
* True when the answer is INCOMPLETE: the streamed generation was cut at the
|
|
189
|
+
* caller's deadline and the partial prose is returned instead of discarded.
|
|
190
|
+
* Content is mid-sentence (or mid-file) by definition — surface it to the
|
|
191
|
+
* end user as truncated rather than presenting it as a finished answer.
|
|
192
|
+
* Never set on tool-call turns (a half-streamed arguments fragment can't be
|
|
193
|
+
* salvaged) and never on a normal completion.
|
|
194
|
+
*/
|
|
195
|
+
truncated?: boolean;
|
|
187
196
|
usage: {
|
|
188
197
|
prompt_tokens: number;
|
|
189
198
|
completion_tokens: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -184,6 +184,15 @@ interface ParsedResponseMessage {
|
|
|
184
184
|
* mismatch with the requested model's provider reveals the fallback.
|
|
185
185
|
*/
|
|
186
186
|
provider?: string;
|
|
187
|
+
/**
|
|
188
|
+
* True when the answer is INCOMPLETE: the streamed generation was cut at the
|
|
189
|
+
* caller's deadline and the partial prose is returned instead of discarded.
|
|
190
|
+
* Content is mid-sentence (or mid-file) by definition — surface it to the
|
|
191
|
+
* end user as truncated rather than presenting it as a finished answer.
|
|
192
|
+
* Never set on tool-call turns (a half-streamed arguments fragment can't be
|
|
193
|
+
* salvaged) and never on a normal completion.
|
|
194
|
+
*/
|
|
195
|
+
truncated?: boolean;
|
|
187
196
|
usage: {
|
|
188
197
|
prompt_tokens: number;
|
|
189
198
|
completion_tokens: number;
|
package/dist/index.js
CHANGED
|
@@ -1261,9 +1261,13 @@ function prepareOpenAICompatMessages(messages) {
|
|
|
1261
1261
|
}
|
|
1262
1262
|
continue;
|
|
1263
1263
|
}
|
|
1264
|
+
const fileRefs = (message.files || []).filter((file) => file.url).map(
|
|
1265
|
+
(file) => ALLOWED_IMAGE_MIME_TYPES.includes(file.mimeType) ? `Image (${file.url})` : `File (${file.url})`
|
|
1266
|
+
);
|
|
1267
|
+
const content = [normalizeMessageContent(message.content), ...fileRefs].filter(Boolean).join("\n");
|
|
1264
1268
|
const outMessage = {
|
|
1265
1269
|
role: message.role,
|
|
1266
|
-
content
|
|
1270
|
+
content
|
|
1267
1271
|
};
|
|
1268
1272
|
if ((_a = message.functionCalls) == null ? void 0 : _a.length) {
|
|
1269
1273
|
outMessage.tool_calls = message.functionCalls.map((fc, i) => {
|
|
@@ -1277,7 +1281,7 @@ function prepareOpenAICompatMessages(messages) {
|
|
|
1277
1281
|
}
|
|
1278
1282
|
};
|
|
1279
1283
|
});
|
|
1280
|
-
if (!
|
|
1284
|
+
if (!content)
|
|
1281
1285
|
outMessage.content = null;
|
|
1282
1286
|
}
|
|
1283
1287
|
if (message.reasoning)
|
|
@@ -1493,7 +1497,9 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1493
1497
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1494
1498
|
const controller = new AbortController();
|
|
1495
1499
|
let abortReason = null;
|
|
1496
|
-
|
|
1500
|
+
let abortKind = null;
|
|
1501
|
+
const abortWith = (kind, reason) => {
|
|
1502
|
+
abortKind = kind;
|
|
1497
1503
|
abortReason = reason;
|
|
1498
1504
|
controller.abort();
|
|
1499
1505
|
};
|
|
@@ -1505,6 +1511,7 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1505
1511
|
const totalTimer = unref(
|
|
1506
1512
|
setTimeout(
|
|
1507
1513
|
() => abortWith(
|
|
1514
|
+
"deadline",
|
|
1508
1515
|
`OpenRouter stream exceeded total deadline of ${streamTimeoutMs}ms`
|
|
1509
1516
|
),
|
|
1510
1517
|
streamTimeoutMs
|
|
@@ -1516,6 +1523,7 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1516
1523
|
stallTimer = unref(
|
|
1517
1524
|
setTimeout(
|
|
1518
1525
|
() => abortWith(
|
|
1526
|
+
"stall",
|
|
1519
1527
|
`OpenRouter stream stalled: no useful chunk for ${chunkTimeoutMs}ms`
|
|
1520
1528
|
),
|
|
1521
1529
|
chunkTimeoutMs
|
|
@@ -1682,7 +1690,31 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1682
1690
|
});
|
|
1683
1691
|
} catch (error2) {
|
|
1684
1692
|
if (abortReason && !(signal == null ? void 0 : signal.aborted)) {
|
|
1685
|
-
|
|
1693
|
+
const kept = paragraph.trim();
|
|
1694
|
+
if (abortKind === "deadline" && kept && !toolCalls.length) {
|
|
1695
|
+
try {
|
|
1696
|
+
const message = finalizeOpenRouterMessage(id, {
|
|
1697
|
+
content: kept,
|
|
1698
|
+
toolCalls: [],
|
|
1699
|
+
reasoning,
|
|
1700
|
+
reasoningDetails: reasoningDetails.length ? reasoningDetails : void 0,
|
|
1701
|
+
provider,
|
|
1702
|
+
usage,
|
|
1703
|
+
forLog: () => JSON.stringify({ provider, kept: kept.slice(0, 500) })
|
|
1704
|
+
});
|
|
1705
|
+
message.truncated = true;
|
|
1706
|
+
logger_default.log(
|
|
1707
|
+
id,
|
|
1708
|
+
`${abortReason} \u2014 returning truncated answer (${kept.length} chars, ~${estimateTokens(kept)} tokens kept)`
|
|
1709
|
+
);
|
|
1710
|
+
return message;
|
|
1711
|
+
} catch (e) {
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
logger_default.error(
|
|
1715
|
+
id,
|
|
1716
|
+
`${abortReason} \u2014 discarding ~${estimateTokens(paragraph + reasoning)} generated tokens (content ${paragraph.length} chars, reasoning ${reasoning.length} chars, ${dataChunks} chunks)`
|
|
1717
|
+
);
|
|
1686
1718
|
throw new Error(abortReason);
|
|
1687
1719
|
}
|
|
1688
1720
|
throw error2;
|
|
@@ -1755,6 +1787,7 @@ function moderationEvictionSlug(error2, payload) {
|
|
|
1755
1787
|
return fromOrder ? fromOrder.split("/")[0] : display.toLowerCase();
|
|
1756
1788
|
}
|
|
1757
1789
|
var MIN_STREAM_ATTEMPT_MS = 1e4;
|
|
1790
|
+
var estimateTokens = (text) => Math.round(text.length / 4);
|
|
1758
1791
|
function streamAttemptBudgetMs(options) {
|
|
1759
1792
|
if (options.streamDeadlineAt === void 0)
|
|
1760
1793
|
return options.streamTimeoutMs;
|