190proof 1.0.110 → 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 CHANGED
@@ -278,6 +278,7 @@ 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.
281
282
 
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.
283
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.
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
@@ -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,
@@ -1753,18 +1754,38 @@ function moderationEvictionSlug(error2, payload) {
1753
1754
  );
1754
1755
  return fromOrder ? fromOrder.split("/")[0] : display.toLowerCase();
1755
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
+ }
1756
1766
  async function callOpenRouterWithRetries(id, payload, retries = 5, options, signal) {
1757
1767
  const evicted = [];
1758
1768
  return withRetries(
1759
1769
  id,
1760
1770
  "OpenRouter",
1761
- () => (options.streaming ? callOpenRouterStream(
1762
- id,
1763
- payload,
1764
- options.streamTimeoutMs,
1765
- options.chunkTimeoutMs,
1766
- signal
1767
- ) : callOpenRouterNonStreaming(
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(
1768
1789
  id,
1769
1790
  payload,
1770
1791
  options.requestTimeoutMs,
@@ -1884,6 +1905,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1884
1905
  {
1885
1906
  streaming: (_b = aiPayload.streaming) != null ? _b : true,
1886
1907
  streamTimeoutMs: (_c = aiPayload.streamTimeoutMs) != null ? _c : OPENROUTER_STREAM_TIMEOUT_MS,
1908
+ streamDeadlineAt: aiPayload.streamDeadlineAt,
1887
1909
  requestTimeoutMs: (_d = aiPayload.requestTimeoutMs) != null ? _d : OPENROUTER_NONSTREAM_TIMEOUT_MS,
1888
1910
  chunkTimeoutMs
1889
1911
  },
@@ -1926,6 +1948,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1926
1948
  GPTModel,
1927
1949
  GeminiModel,
1928
1950
  GroqModel,
1951
+ MIN_STREAM_ATTEMPT_MS,
1929
1952
  OPENROUTER_NONSTREAM_TIMEOUT_MS,
1930
1953
  OPENROUTER_STREAM_TIMEOUT_MS,
1931
1954
  OpenRouterModel,