190proof 1.0.110 → 1.0.112
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 +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +61 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -9
- 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
|
+
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.
|
|
281
284
|
|
|
282
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.
|
|
283
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.
|
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;
|
|
@@ -297,6 +306,19 @@ interface GenericPayload {
|
|
|
297
306
|
* by chunks that advance the output, never by keep-alive bytes/comments).
|
|
298
307
|
*/
|
|
299
308
|
streamTimeoutMs?: number;
|
|
309
|
+
/**
|
|
310
|
+
* OpenRouter-only: absolute wall-clock deadline (epoch ms) for the whole
|
|
311
|
+
* call INCLUDING retries — the caller's turn budget, not a per-attempt one.
|
|
312
|
+
* Each streaming attempt gets `min(streamTimeoutMs, deadline - now)`, and
|
|
313
|
+
* once too little time remains to be worth an attempt the call fails fast
|
|
314
|
+
* instead of starting a generation that cannot finish.
|
|
315
|
+
*
|
|
316
|
+
* Without it, a per-attempt budget is re-granted on every retry, so a slow
|
|
317
|
+
* generation can outlive the caller's own turn deadline and get killed with
|
|
318
|
+
* nothing to show (2026-07-28: a 538s completion finished just as the
|
|
319
|
+
* caller's 585s turn budget expired, and the reply was discarded).
|
|
320
|
+
*/
|
|
321
|
+
streamDeadlineAt?: number;
|
|
300
322
|
/**
|
|
301
323
|
* Optional caller-supplied cancellation signal. When it aborts, the in-flight
|
|
302
324
|
* provider request is cancelled and `callWithRetries` rejects immediately —
|
|
@@ -309,10 +331,16 @@ interface GenericPayload {
|
|
|
309
331
|
|
|
310
332
|
declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
|
|
311
333
|
declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
|
|
334
|
+
/**
|
|
335
|
+
* Least time a streaming attempt is worth starting with. Below this the
|
|
336
|
+
* generation cannot plausibly finish before the caller's deadline, so burning
|
|
337
|
+
* a provider call (and paying for tokens that get discarded) is pure waste.
|
|
338
|
+
*/
|
|
339
|
+
declare const MIN_STREAM_ATTEMPT_MS = 10000;
|
|
312
340
|
declare function parseModelString(model: string): {
|
|
313
341
|
provider: Provider;
|
|
314
342
|
modelId: string;
|
|
315
343
|
};
|
|
316
344
|
declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
|
|
317
345
|
|
|
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 };
|
|
346
|
+
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
|
@@ -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;
|
|
@@ -297,6 +306,19 @@ interface GenericPayload {
|
|
|
297
306
|
* by chunks that advance the output, never by keep-alive bytes/comments).
|
|
298
307
|
*/
|
|
299
308
|
streamTimeoutMs?: number;
|
|
309
|
+
/**
|
|
310
|
+
* OpenRouter-only: absolute wall-clock deadline (epoch ms) for the whole
|
|
311
|
+
* call INCLUDING retries — the caller's turn budget, not a per-attempt one.
|
|
312
|
+
* Each streaming attempt gets `min(streamTimeoutMs, deadline - now)`, and
|
|
313
|
+
* once too little time remains to be worth an attempt the call fails fast
|
|
314
|
+
* instead of starting a generation that cannot finish.
|
|
315
|
+
*
|
|
316
|
+
* Without it, a per-attempt budget is re-granted on every retry, so a slow
|
|
317
|
+
* generation can outlive the caller's own turn deadline and get killed with
|
|
318
|
+
* nothing to show (2026-07-28: a 538s completion finished just as the
|
|
319
|
+
* caller's 585s turn budget expired, and the reply was discarded).
|
|
320
|
+
*/
|
|
321
|
+
streamDeadlineAt?: number;
|
|
300
322
|
/**
|
|
301
323
|
* Optional caller-supplied cancellation signal. When it aborts, the in-flight
|
|
302
324
|
* provider request is cancelled and `callWithRetries` rejects immediately —
|
|
@@ -309,10 +331,16 @@ interface GenericPayload {
|
|
|
309
331
|
|
|
310
332
|
declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
|
|
311
333
|
declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
|
|
334
|
+
/**
|
|
335
|
+
* Least time a streaming attempt is worth starting with. Below this the
|
|
336
|
+
* generation cannot plausibly finish before the caller's deadline, so burning
|
|
337
|
+
* a provider call (and paying for tokens that get discarded) is pure waste.
|
|
338
|
+
*/
|
|
339
|
+
declare const MIN_STREAM_ATTEMPT_MS = 10000;
|
|
312
340
|
declare function parseModelString(model: string): {
|
|
313
341
|
provider: Provider;
|
|
314
342
|
modelId: string;
|
|
315
343
|
};
|
|
316
344
|
declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
|
|
317
345
|
|
|
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 };
|
|
346
|
+
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
|
@@ -34,6 +34,7 @@ __export(proof_exports, {
|
|
|
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,
|
|
@@ -1492,7 +1493,9 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1492
1493
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1493
1494
|
const controller = new AbortController();
|
|
1494
1495
|
let abortReason = null;
|
|
1495
|
-
|
|
1496
|
+
let abortKind = null;
|
|
1497
|
+
const abortWith = (kind, reason) => {
|
|
1498
|
+
abortKind = kind;
|
|
1496
1499
|
abortReason = reason;
|
|
1497
1500
|
controller.abort();
|
|
1498
1501
|
};
|
|
@@ -1504,6 +1507,7 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1504
1507
|
const totalTimer = unref(
|
|
1505
1508
|
setTimeout(
|
|
1506
1509
|
() => abortWith(
|
|
1510
|
+
"deadline",
|
|
1507
1511
|
`OpenRouter stream exceeded total deadline of ${streamTimeoutMs}ms`
|
|
1508
1512
|
),
|
|
1509
1513
|
streamTimeoutMs
|
|
@@ -1515,6 +1519,7 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1515
1519
|
stallTimer = unref(
|
|
1516
1520
|
setTimeout(
|
|
1517
1521
|
() => abortWith(
|
|
1522
|
+
"stall",
|
|
1518
1523
|
`OpenRouter stream stalled: no useful chunk for ${chunkTimeoutMs}ms`
|
|
1519
1524
|
),
|
|
1520
1525
|
chunkTimeoutMs
|
|
@@ -1681,7 +1686,31 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
|
|
|
1681
1686
|
});
|
|
1682
1687
|
} catch (error2) {
|
|
1683
1688
|
if (abortReason && !(signal == null ? void 0 : signal.aborted)) {
|
|
1684
|
-
|
|
1689
|
+
const kept = paragraph.trim();
|
|
1690
|
+
if (abortKind === "deadline" && kept && !toolCalls.length) {
|
|
1691
|
+
try {
|
|
1692
|
+
const message = finalizeOpenRouterMessage(id, {
|
|
1693
|
+
content: kept,
|
|
1694
|
+
toolCalls: [],
|
|
1695
|
+
reasoning,
|
|
1696
|
+
reasoningDetails: reasoningDetails.length ? reasoningDetails : void 0,
|
|
1697
|
+
provider,
|
|
1698
|
+
usage,
|
|
1699
|
+
forLog: () => JSON.stringify({ provider, kept: kept.slice(0, 500) })
|
|
1700
|
+
});
|
|
1701
|
+
message.truncated = true;
|
|
1702
|
+
logger_default.log(
|
|
1703
|
+
id,
|
|
1704
|
+
`${abortReason} \u2014 returning truncated answer (${kept.length} chars, ~${estimateTokens(kept)} tokens kept)`
|
|
1705
|
+
);
|
|
1706
|
+
return message;
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
logger_default.error(
|
|
1711
|
+
id,
|
|
1712
|
+
`${abortReason} \u2014 discarding ~${estimateTokens(paragraph + reasoning)} generated tokens (content ${paragraph.length} chars, reasoning ${reasoning.length} chars, ${dataChunks} chunks)`
|
|
1713
|
+
);
|
|
1685
1714
|
throw new Error(abortReason);
|
|
1686
1715
|
}
|
|
1687
1716
|
throw error2;
|
|
@@ -1753,18 +1782,39 @@ function moderationEvictionSlug(error2, payload) {
|
|
|
1753
1782
|
);
|
|
1754
1783
|
return fromOrder ? fromOrder.split("/")[0] : display.toLowerCase();
|
|
1755
1784
|
}
|
|
1785
|
+
var MIN_STREAM_ATTEMPT_MS = 1e4;
|
|
1786
|
+
var estimateTokens = (text) => Math.round(text.length / 4);
|
|
1787
|
+
function streamAttemptBudgetMs(options) {
|
|
1788
|
+
if (options.streamDeadlineAt === void 0)
|
|
1789
|
+
return options.streamTimeoutMs;
|
|
1790
|
+
const remaining = options.streamDeadlineAt - Date.now();
|
|
1791
|
+
if (remaining < MIN_STREAM_ATTEMPT_MS)
|
|
1792
|
+
return null;
|
|
1793
|
+
return Math.min(options.streamTimeoutMs, remaining);
|
|
1794
|
+
}
|
|
1756
1795
|
async function callOpenRouterWithRetries(id, payload, retries = 5, options, signal) {
|
|
1757
1796
|
const evicted = [];
|
|
1758
1797
|
return withRetries(
|
|
1759
1798
|
id,
|
|
1760
1799
|
"OpenRouter",
|
|
1761
|
-
() => (options.streaming ?
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1800
|
+
() => (options.streaming ? (() => {
|
|
1801
|
+
const budget = streamAttemptBudgetMs(options);
|
|
1802
|
+
if (budget === null) {
|
|
1803
|
+
const error2 = new Error(
|
|
1804
|
+
"OpenRouter stream skipped: caller deadline leaves too little time for another attempt"
|
|
1805
|
+
);
|
|
1806
|
+
error2.deadlineExceeded = true;
|
|
1807
|
+
logger_default.error(id, error2.message);
|
|
1808
|
+
throw error2;
|
|
1809
|
+
}
|
|
1810
|
+
return callOpenRouterStream(
|
|
1811
|
+
id,
|
|
1812
|
+
payload,
|
|
1813
|
+
budget,
|
|
1814
|
+
options.chunkTimeoutMs,
|
|
1815
|
+
signal
|
|
1816
|
+
);
|
|
1817
|
+
})() : callOpenRouterNonStreaming(
|
|
1768
1818
|
id,
|
|
1769
1819
|
payload,
|
|
1770
1820
|
options.requestTimeoutMs,
|
|
@@ -1884,6 +1934,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1884
1934
|
{
|
|
1885
1935
|
streaming: (_b = aiPayload.streaming) != null ? _b : true,
|
|
1886
1936
|
streamTimeoutMs: (_c = aiPayload.streamTimeoutMs) != null ? _c : OPENROUTER_STREAM_TIMEOUT_MS,
|
|
1937
|
+
streamDeadlineAt: aiPayload.streamDeadlineAt,
|
|
1887
1938
|
requestTimeoutMs: (_d = aiPayload.requestTimeoutMs) != null ? _d : OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
1888
1939
|
chunkTimeoutMs
|
|
1889
1940
|
},
|
|
@@ -1926,6 +1977,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
|
|
|
1926
1977
|
GPTModel,
|
|
1927
1978
|
GeminiModel,
|
|
1928
1979
|
GroqModel,
|
|
1980
|
+
MIN_STREAM_ATTEMPT_MS,
|
|
1929
1981
|
OPENROUTER_NONSTREAM_TIMEOUT_MS,
|
|
1930
1982
|
OPENROUTER_STREAM_TIMEOUT_MS,
|
|
1931
1983
|
OpenRouterModel,
|