@providerkit/core 0.1.0

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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +245 -0
  3. package/dist/context.d.ts +69 -0
  4. package/dist/context.d.ts.map +1 -0
  5. package/dist/context.js +132 -0
  6. package/dist/context.js.map +1 -0
  7. package/dist/errors.d.ts +86 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +356 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/index.d.ts +13 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +13 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/providers/anthropic.d.ts +26 -0
  16. package/dist/providers/anthropic.d.ts.map +1 -0
  17. package/dist/providers/anthropic.js +245 -0
  18. package/dist/providers/anthropic.js.map +1 -0
  19. package/dist/providers/openai.d.ts +30 -0
  20. package/dist/providers/openai.d.ts.map +1 -0
  21. package/dist/providers/openai.js +185 -0
  22. package/dist/providers/openai.js.map +1 -0
  23. package/dist/retry.d.ts +79 -0
  24. package/dist/retry.d.ts.map +1 -0
  25. package/dist/retry.js +200 -0
  26. package/dist/retry.js.map +1 -0
  27. package/dist/schema.d.ts +2 -0
  28. package/dist/schema.d.ts.map +1 -0
  29. package/dist/schema.js +48 -0
  30. package/dist/schema.js.map +1 -0
  31. package/dist/tool-args.d.ts +12 -0
  32. package/dist/tool-args.d.ts.map +1 -0
  33. package/dist/tool-args.js +113 -0
  34. package/dist/tool-args.js.map +1 -0
  35. package/dist/tools.d.ts +82 -0
  36. package/dist/tools.d.ts.map +1 -0
  37. package/dist/tools.js +155 -0
  38. package/dist/tools.js.map +1 -0
  39. package/dist/transport.d.ts +31 -0
  40. package/dist/transport.d.ts.map +1 -0
  41. package/dist/transport.js +157 -0
  42. package/dist/transport.js.map +1 -0
  43. package/dist/types.d.ts +168 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +75 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/usage.d.ts +50 -0
  48. package/dist/usage.d.ts.map +1 -0
  49. package/dist/usage.js +71 -0
  50. package/dist/usage.js.map +1 -0
  51. package/dist/watchdog.d.ts +34 -0
  52. package/dist/watchdog.d.ts.map +1 -0
  53. package/dist/watchdog.js +85 -0
  54. package/dist/watchdog.js.map +1 -0
  55. package/dist/zod.d.ts +32 -0
  56. package/dist/zod.d.ts.map +1 -0
  57. package/dist/zod.js +49 -0
  58. package/dist/zod.js.map +1 -0
  59. package/package.json +76 -0
  60. package/src/context.ts +150 -0
  61. package/src/errors.ts +398 -0
  62. package/src/index.ts +12 -0
  63. package/src/providers/anthropic.ts +315 -0
  64. package/src/providers/openai.ts +246 -0
  65. package/src/retry.ts +246 -0
  66. package/src/schema.ts +67 -0
  67. package/src/tool-args.ts +117 -0
  68. package/src/tools.ts +237 -0
  69. package/src/transport.ts +162 -0
  70. package/src/types.ts +231 -0
  71. package/src/usage.ts +106 -0
  72. package/src/watchdog.ts +119 -0
  73. package/src/zod.ts +74 -0
@@ -0,0 +1,245 @@
1
+ // Anthropic-shape adapter — SSE from POST /v1/messages.
2
+ import { ProviderError } from "../errors.js";
3
+ import { streamSse, apiUrl } from "../transport.js";
4
+ const DEFAULT_BASE_URL = "https://api.anthropic.com";
5
+ const DEFAULT_VERSION = "2023-06-01";
6
+ /** Anthropic rejects a request without one, so a default is not optional. */
7
+ const DEFAULT_MAX_TOKENS = 8_192;
8
+ /**
9
+ * Thinking budgets, in output tokens. Thinking and the answer SHARE
10
+ * `max_tokens`, so a budget is always left below the ceiling — a budget at or
11
+ * above it leaves no room to answer, and the turn ends mid-thought.
12
+ */
13
+ const THINKING_BUDGET = {
14
+ low: 2_048,
15
+ medium: 8_192,
16
+ high: 16_384,
17
+ max: 32_768,
18
+ };
19
+ function mapStopReason(reason) {
20
+ switch (reason) {
21
+ case "end_turn":
22
+ case "stop_sequence":
23
+ return "stop";
24
+ case "tool_use":
25
+ return "tool_calls";
26
+ case "max_tokens":
27
+ return "length";
28
+ case "refusal":
29
+ return "content_filter";
30
+ default:
31
+ return undefined;
32
+ }
33
+ }
34
+ function partsToAnthropic(content) {
35
+ if (typeof content === "string")
36
+ return [{ type: "text", text: content }];
37
+ return content.map((part) => part.type === "text"
38
+ ? { type: "text", text: part.text }
39
+ : {
40
+ type: "image",
41
+ source: { type: "base64", media_type: part.mimeType, data: part.data },
42
+ });
43
+ }
44
+ /**
45
+ * Anthropic takes `system` at the top level and expects tool RESULTS as user
46
+ * turns carrying `tool_result` blocks — not as a role of their own. Consecutive
47
+ * tool results are merged into one user turn, which the API requires.
48
+ */
49
+ export function toAnthropicMessages(messages) {
50
+ const system = messages
51
+ .filter((m) => m.role === "system")
52
+ .map((m) => m.content)
53
+ .join("\n\n");
54
+ const out = [];
55
+ const pushBlocks = (role, blocks) => {
56
+ const last = out[out.length - 1];
57
+ if (last?.role === role)
58
+ last.content.push(...blocks);
59
+ else
60
+ out.push({ role, content: blocks });
61
+ };
62
+ for (const message of messages) {
63
+ if (message.role === "system")
64
+ continue;
65
+ if (message.role === "user") {
66
+ pushBlocks("user", partsToAnthropic(message.content));
67
+ continue;
68
+ }
69
+ if (message.role === "tool") {
70
+ pushBlocks("user", [
71
+ {
72
+ type: "tool_result",
73
+ tool_use_id: message.toolCallId,
74
+ content: [
75
+ { type: "text", text: message.content },
76
+ ...(message.images ?? []).map((image) => ({
77
+ type: "image",
78
+ source: { type: "base64", media_type: image.mimeType, data: image.data },
79
+ })),
80
+ ],
81
+ },
82
+ ]);
83
+ continue;
84
+ }
85
+ // assistant. Reasoning is deliberately NOT replayed: Anthropic's thinking
86
+ // blocks carry signatures we never captured, and a block without its
87
+ // signature is rejected.
88
+ const blocks = [];
89
+ if (message.content)
90
+ blocks.push({ type: "text", text: message.content });
91
+ for (const call of message.toolCalls ?? []) {
92
+ blocks.push({
93
+ type: "tool_use",
94
+ id: call.id,
95
+ name: call.name,
96
+ input: safeParse(call.arguments),
97
+ });
98
+ }
99
+ if (blocks.length > 0)
100
+ pushBlocks("assistant", blocks);
101
+ }
102
+ return { ...(system ? { system } : {}), messages: out };
103
+ }
104
+ function safeParse(raw) {
105
+ try {
106
+ return raw ? JSON.parse(raw) : {};
107
+ }
108
+ catch {
109
+ return {};
110
+ }
111
+ }
112
+ export function createAnthropicProvider(config) {
113
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
114
+ return {
115
+ id: "anthropic",
116
+ model: config.model,
117
+ async *createStream(messages, tools, opts = {}) {
118
+ const model = opts.model ?? config.model;
119
+ const maxTokens = opts.maxTokens ?? config.maxTokens ?? DEFAULT_MAX_TOKENS;
120
+ const effort = opts.effort ?? config.effort ?? "none";
121
+ const { system, messages: body } = toAnthropicMessages(messages);
122
+ const request = {
123
+ model,
124
+ max_tokens: maxTokens,
125
+ messages: body,
126
+ stream: true,
127
+ };
128
+ if (system)
129
+ request.system = system;
130
+ if (opts.temperature !== undefined)
131
+ request.temperature = opts.temperature;
132
+ if (tools.length > 0) {
133
+ request.tools = tools.map((tool) => ({
134
+ name: tool.name,
135
+ description: tool.description,
136
+ input_schema: tool.inputSchema,
137
+ }));
138
+ }
139
+ if (opts.toolChoice && opts.toolChoice !== "auto") {
140
+ request.tool_choice =
141
+ opts.toolChoice === "none"
142
+ ? { type: "none" }
143
+ : opts.toolChoice === "required"
144
+ ? { type: "any" }
145
+ : { type: "tool", name: opts.toolChoice.name };
146
+ }
147
+ if (effort !== "none") {
148
+ const budget = Math.min(THINKING_BUDGET[effort], Math.floor(maxTokens * 0.8));
149
+ request.thinking = { type: "enabled", budget_tokens: budget };
150
+ // Thinking and sampling are mutually exclusive on this shape.
151
+ delete request.temperature;
152
+ }
153
+ // Anthropic reports cache reads and writes as fields of their OWN,
154
+ // EXCLUDED from `input_tokens` — where the OpenAI shapes report a cached
155
+ // subset already inside the prompt count. Reconciling here is what keeps
156
+ // one usage record meaningful across both, and a cost figure honest.
157
+ let inputTokens = 0;
158
+ let cachedInputTokens = 0;
159
+ let cacheWriteTokens = 0;
160
+ let outputTokens = 0;
161
+ let toolCall = null;
162
+ let blockIndex = -1;
163
+ for await (const data of streamSse({
164
+ url: apiUrl(baseUrl, "/v1/messages"),
165
+ headers: {
166
+ "anthropic-version": config.version ?? DEFAULT_VERSION,
167
+ ...(config.bearer
168
+ ? { authorization: `Bearer ${config.apiKey}` }
169
+ : { "x-api-key": config.apiKey }),
170
+ },
171
+ body: request,
172
+ provider: "anthropic",
173
+ ...(opts.signal ? { signal: opts.signal } : {}),
174
+ ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),
175
+ })) {
176
+ let event;
177
+ try {
178
+ event = JSON.parse(data);
179
+ }
180
+ catch {
181
+ continue; // a keep-alive or a frame we do not model
182
+ }
183
+ switch (event.type) {
184
+ case "error":
185
+ throw new ProviderError("anthropic", "overload", event.error?.message ?? "anthropic stream error", { code: event.error?.type });
186
+ case "message_start": {
187
+ const usage = event.message?.usage;
188
+ cachedInputTokens = usage?.cache_read_input_tokens ?? 0;
189
+ cacheWriteTokens = usage?.cache_creation_input_tokens ?? 0;
190
+ inputTokens = (usage?.input_tokens ?? 0) + cachedInputTokens + cacheWriteTokens;
191
+ break;
192
+ }
193
+ case "content_block_start": {
194
+ blockIndex += 1;
195
+ if (event.content_block?.type === "tool_use") {
196
+ toolCall = {
197
+ index: blockIndex,
198
+ id: event.content_block.id ?? "",
199
+ name: event.content_block.name ?? "",
200
+ };
201
+ yield {
202
+ type: "delta",
203
+ toolCalls: [{ index: toolCall.index, id: toolCall.id, name: toolCall.name }],
204
+ };
205
+ }
206
+ break;
207
+ }
208
+ case "content_block_delta": {
209
+ const delta = event.delta;
210
+ if (delta?.type === "text_delta" && delta.text) {
211
+ yield { type: "delta", content: delta.text };
212
+ }
213
+ else if (delta?.type === "thinking_delta" && delta.thinking) {
214
+ yield { type: "delta", reasoning: delta.thinking };
215
+ }
216
+ else if (delta?.type === "input_json_delta" && toolCall) {
217
+ yield {
218
+ type: "delta",
219
+ toolCalls: [{ index: toolCall.index, arguments: delta.partial_json ?? "" }],
220
+ };
221
+ }
222
+ break;
223
+ }
224
+ case "content_block_stop":
225
+ toolCall = null;
226
+ break;
227
+ case "message_delta": {
228
+ outputTokens = event.usage?.output_tokens ?? outputTokens;
229
+ const finishReason = mapStopReason(event.delta?.stop_reason);
230
+ if (finishReason)
231
+ yield { type: "finish", finishReason };
232
+ break;
233
+ }
234
+ case "message_stop":
235
+ yield {
236
+ type: "usage",
237
+ usage: { inputTokens, cachedInputTokens, cacheWriteTokens, outputTokens },
238
+ };
239
+ break;
240
+ }
241
+ }
242
+ },
243
+ };
244
+ }
245
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { ChatMessage, Effort, Provider } from "../types.ts";
2
+ export interface OpenAIConfig {
3
+ apiKey: string;
4
+ model: string;
5
+ /** Any OpenAI-compatible endpoint. Defaults to OpenAI itself. */
6
+ baseUrl?: string;
7
+ /** Names the provider in errors and logs — "openrouter", "deepseek", … */
8
+ id?: string;
9
+ effort?: Effort;
10
+ maxTokens?: number;
11
+ fetchImpl?: typeof fetch;
12
+ headers?: Record<string, string>;
13
+ /**
14
+ * Pin OpenRouter to preferred upstream hosts so the PROMPT CACHE stays warm
15
+ * across rounds. The cache lives on the upstream host's account and default
16
+ * routing hops between them, and every hop is a cold cache — worse latency
17
+ * and higher effective input cost. Fallbacks stay on: this is a preference,
18
+ * not a lock.
19
+ */
20
+ providerOrder?: string[];
21
+ }
22
+ /**
23
+ * Assistant turns carry `reasoning_content` when the history has it — thinking
24
+ * providers require the prior turn's chain-of-thought replayed on a turn that
25
+ * made a tool call. A caller running a turn with thinking OFF must strip it
26
+ * first (`stripReasoning`); the two cannot be mixed.
27
+ */
28
+ export declare function toOpenAIMessages(messages: readonly ChatMessage[]): unknown[];
29
+ export declare function createOpenAIProvider(config: OpenAIConfig): Provider;
30
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,WAAW,EAEX,MAAM,EAEN,QAAQ,EAIT,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AA6BD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,EAAE,CA4B5E;AAuBD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,QAAQ,CA0HnE"}
@@ -0,0 +1,185 @@
1
+ // OpenAI-shape adapter — SSE from POST /v1/chat/completions.
2
+ //
3
+ // This is the dialect most gateways speak, so one adapter serves OpenAI,
4
+ // OpenRouter, DeepSeek, GLM, Kimi, Groq, Together, vLLM, Ollama and LM Studio.
5
+ // Their divergences are small and named where they appear.
6
+ import { streamSse, apiUrl } from "../transport.js";
7
+ import { toDataUri } from "../types.js";
8
+ const DEFAULT_BASE_URL = "https://api.openai.com";
9
+ function mapFinishReason(reason) {
10
+ switch (reason) {
11
+ case "stop":
12
+ return "stop";
13
+ case "length":
14
+ return "length";
15
+ case "tool_calls":
16
+ case "function_call":
17
+ return "tool_calls";
18
+ case "content_filter":
19
+ return "content_filter";
20
+ default:
21
+ return undefined;
22
+ }
23
+ }
24
+ function partsToOpenAI(content) {
25
+ if (typeof content === "string")
26
+ return content;
27
+ return content.map((part) => part.type === "text"
28
+ ? { type: "text", text: part.text }
29
+ : { type: "image_url", image_url: { url: toDataUri(part) } });
30
+ }
31
+ /**
32
+ * Assistant turns carry `reasoning_content` when the history has it — thinking
33
+ * providers require the prior turn's chain-of-thought replayed on a turn that
34
+ * made a tool call. A caller running a turn with thinking OFF must strip it
35
+ * first (`stripReasoning`); the two cannot be mixed.
36
+ */
37
+ export function toOpenAIMessages(messages) {
38
+ return messages.map((message) => {
39
+ switch (message.role) {
40
+ case "system":
41
+ return { role: "system", content: message.content };
42
+ case "user":
43
+ return { role: "user", content: partsToOpenAI(message.content) };
44
+ case "tool":
45
+ return { role: "tool", tool_call_id: message.toolCallId, content: message.content };
46
+ case "assistant": {
47
+ const out = {
48
+ role: "assistant",
49
+ // Nullable content beside tool_calls is what this shape expects, but
50
+ // several gateways reject a bare null — "" satisfies both.
51
+ content: message.content || "",
52
+ };
53
+ if (message.reasoning)
54
+ out.reasoning_content = message.reasoning;
55
+ if (message.toolCalls?.length) {
56
+ out.tool_calls = message.toolCalls.map((call) => ({
57
+ id: call.id,
58
+ type: "function",
59
+ function: { name: call.name, arguments: call.arguments },
60
+ }));
61
+ }
62
+ return out;
63
+ }
64
+ }
65
+ });
66
+ }
67
+ export function createOpenAIProvider(config) {
68
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
69
+ const id = config.id ?? "openai";
70
+ return {
71
+ id,
72
+ model: config.model,
73
+ async *createStream(messages, tools, opts = {}) {
74
+ const effort = opts.effort ?? config.effort;
75
+ const request = {
76
+ model: opts.model ?? config.model,
77
+ messages: toOpenAIMessages(messages),
78
+ stream: true,
79
+ // Without this the usage record never arrives and every call costs
80
+ // zero — a silent, total loss of the ledger.
81
+ stream_options: { include_usage: true },
82
+ };
83
+ const maxTokens = opts.maxTokens ?? config.maxTokens;
84
+ if (maxTokens !== undefined)
85
+ request.max_tokens = maxTokens;
86
+ if (opts.temperature !== undefined)
87
+ request.temperature = opts.temperature;
88
+ if (effort && effort !== "none")
89
+ request.reasoning_effort = effort;
90
+ if (tools.length > 0) {
91
+ request.tools = tools.map((tool) => ({
92
+ type: "function",
93
+ function: {
94
+ name: tool.name,
95
+ description: tool.description,
96
+ parameters: tool.inputSchema,
97
+ },
98
+ }));
99
+ }
100
+ if (opts.toolChoice && opts.toolChoice !== "auto") {
101
+ request.tool_choice =
102
+ typeof opts.toolChoice === "string"
103
+ ? opts.toolChoice
104
+ : { type: "function", function: { name: opts.toolChoice.name } };
105
+ }
106
+ if (opts.json) {
107
+ request.response_format = {
108
+ type: "json_schema",
109
+ json_schema: { name: opts.json.name, schema: opts.json.schema, strict: true },
110
+ };
111
+ }
112
+ if (config.providerOrder?.length) {
113
+ request.provider = { order: config.providerOrder, allow_fallbacks: true };
114
+ }
115
+ for await (const data of streamSse({
116
+ url: apiUrl(baseUrl, "/v1/chat/completions"),
117
+ headers: { authorization: `Bearer ${config.apiKey}`, ...config.headers },
118
+ body: request,
119
+ provider: id,
120
+ ...(opts.signal ? { signal: opts.signal } : {}),
121
+ ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),
122
+ })) {
123
+ let chunk;
124
+ try {
125
+ chunk = JSON.parse(data);
126
+ }
127
+ catch {
128
+ continue;
129
+ }
130
+ // A usage-only frame carries no choices — this shape sends it last.
131
+ if (chunk.usage) {
132
+ const input = chunk.usage.prompt_tokens ?? 0;
133
+ const cached = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;
134
+ yield {
135
+ type: "usage",
136
+ usage: {
137
+ inputTokens: input,
138
+ // Already a SUBSET of prompt_tokens on this shape — unlike
139
+ // Anthropic's, which excludes them. No reconciling to do.
140
+ cachedInputTokens: cached,
141
+ outputTokens: chunk.usage.completion_tokens ?? 0,
142
+ },
143
+ };
144
+ }
145
+ const choice = chunk.choices?.[0];
146
+ if (!choice)
147
+ continue;
148
+ const delta = choice.delta;
149
+ if (delta) {
150
+ const out = { type: "delta" };
151
+ let has = false;
152
+ if (delta.content) {
153
+ out.content = delta.content;
154
+ has = true;
155
+ }
156
+ // `reasoning_content` is DeepSeek's field; `reasoning` is
157
+ // OpenRouter's normalized one. Whichever arrives is the same thing.
158
+ const reasoning = delta.reasoning_content ?? delta.reasoning;
159
+ if (reasoning) {
160
+ out.reasoning = reasoning;
161
+ has = true;
162
+ }
163
+ if (delta.tool_calls?.length) {
164
+ out.toolCalls = delta.tool_calls.map((call, position) => ({
165
+ // Some gateways omit `index` entirely on single-tool turns.
166
+ index: call.index ?? position,
167
+ ...(call.id ? { id: call.id } : {}),
168
+ ...(call.function?.name ? { name: call.function.name } : {}),
169
+ ...(call.function?.arguments !== undefined
170
+ ? { arguments: call.function.arguments }
171
+ : {}),
172
+ }));
173
+ has = true;
174
+ }
175
+ if (has)
176
+ yield out;
177
+ }
178
+ const finishReason = mapFinishReason(choice.finish_reason);
179
+ if (finishReason)
180
+ yield { type: "finish", finishReason };
181
+ }
182
+ },
183
+ };
184
+ }
185
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ export interface RetryOptions {
2
+ /** Total attempts including the first. Default 3. */
3
+ maxAttempts?: number;
4
+ baseDelayMs?: number;
5
+ maxDelayMs?: number;
6
+ /** Aborts the wait as well as the work, so Stop lands promptly. */
7
+ signal?: AbortSignal;
8
+ /** Decide retryability. Default: transient kinds only. */
9
+ shouldRetry?: (err: unknown, attempt: number) => boolean;
10
+ onRetry?: (info: {
11
+ error: unknown;
12
+ attempt: number;
13
+ delayMs: number;
14
+ }) => void;
15
+ /** Injected in tests so a suite never really waits. */
16
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
17
+ }
18
+ /**
19
+ * Full-jitter exponential backoff: a random delay in
20
+ * `[0, min(cap, base · 2^(attempt-1))]`.
21
+ *
22
+ * The jitter is the point, not the exponent. Without it, every client that
23
+ * failed against the same overloaded upstream retries in the same instant and
24
+ * rebuilds the thundering herd that caused the failure.
25
+ */
26
+ export declare function backoffMs(attempt: number, base?: number, cap?: number): number;
27
+ /** A sleep that wakes early when the caller aborts, and rejects with the
28
+ * abort reason rather than resolving into work nobody wants any more. */
29
+ export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
30
+ /**
31
+ * Run `fn`, retrying transient failures with backoff. For one-shot calls —
32
+ * a title, a summary, a compaction pass.
33
+ */
34
+ export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, opts?: RetryOptions): Promise<T>;
35
+ /**
36
+ * The streaming twin — with the rule that makes it safe: a retry happens only
37
+ * while NOTHING has been yielded yet.
38
+ *
39
+ * `factory` is re-invoked per attempt and gets a fresh signal, so an abandoned
40
+ * attempt's upstream request is cancelled rather than left racing the retry.
41
+ * Once the first chunk is out, the stream is committed and any later failure
42
+ * propagates untouched.
43
+ */
44
+ export declare function withStreamRetry<T>(factory: (signal: AbortSignal, attempt: number) => AsyncIterable<T>, opts?: RetryOptions): AsyncGenerator<T>;
45
+ export interface BackupModelOptions {
46
+ /** Tried in order: `[primary, ...backups]`. The first success wins. */
47
+ models: string[];
48
+ /**
49
+ * Whether a failure may fall through to the remaining models. Default:
50
+ * overload and rate limits only — those are per-model-endpoint, and nothing
51
+ * else on the list is. An auth failure or an invalid request would land
52
+ * identically on every backup.
53
+ */
54
+ shouldTryNext?: (err: unknown) => boolean;
55
+ onModelFailed?: (info: {
56
+ model: string;
57
+ error: unknown;
58
+ position: number;
59
+ total: number;
60
+ }) => void;
61
+ onFallback?: (info: {
62
+ model: string;
63
+ position: number;
64
+ total: number;
65
+ }) => void;
66
+ }
67
+ /** Walk `[primary, ...backups]` until one succeeds. Rethrows the last error. */
68
+ export declare function withBackupModels<T>(attempt: (model: string) => Promise<T>, opts: BackupModelOptions): Promise<T>;
69
+ /**
70
+ * The streaming twin — carrying the same commitment rule as `withStreamRetry`.
71
+ *
72
+ * This is the correction worth naming: walking to a backup model AFTER chunks
73
+ * have already reached the consumer replays the answer from the top, in a
74
+ * different model's voice, on top of text the caller has already rendered. So
75
+ * a stream that fails past its first chunk ends the walk, exactly as it ends a
76
+ * retry.
77
+ */
78
+ export declare function streamWithBackupModels<T>(attempt: (model: string) => AsyncIterable<T>, opts: BackupModelOptions): AsyncGenerator<T>;
79
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/E,uDAAuD;IACvD,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAMD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,SAAwB,EAC5B,GAAG,SAAuB,GACzB,MAAM,CAGR;AAED;0EAC0E;AAC1E,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAarE;AAaD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,CAAC,CAAC,CAqBZ;AAED;;;;;;;;GAQG;AACH,wBAAuB,eAAe,CAAC,CAAC,EACtC,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,EACnE,IAAI,GAAE,YAAiB,GACtB,cAAc,CAAC,CAAC,CAAC,CAuCnB;AAED,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,OAAO,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;KACf,KAAK,IAAI,CAAC;IACX,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjF;AAQD,gFAAgF;AAChF,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACtC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,CAAC,CAAC,CAiBZ;AAED;;;;;;;;GAQG;AACH,wBAAuB,sBAAsB,CAAC,CAAC,EAC7C,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,EAC5C,IAAI,EAAE,kBAAkB,GACvB,cAAc,CAAC,CAAC,CAAC,CAsBnB"}