@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.
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/dist/context.d.ts +69 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +132 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +86 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +356 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/anthropic.d.ts +26 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +245 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/openai.d.ts +30 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +185 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/retry.d.ts +79 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +200 -0
- package/dist/retry.js.map +1 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +48 -0
- package/dist/schema.js.map +1 -0
- package/dist/tool-args.d.ts +12 -0
- package/dist/tool-args.d.ts.map +1 -0
- package/dist/tool-args.js +113 -0
- package/dist/tool-args.js.map +1 -0
- package/dist/tools.d.ts +82 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +155 -0
- package/dist/tools.js.map +1 -0
- package/dist/transport.d.ts +31 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +157 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +168 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +75 -0
- package/dist/types.js.map +1 -0
- package/dist/usage.d.ts +50 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +71 -0
- package/dist/usage.js.map +1 -0
- package/dist/watchdog.d.ts +34 -0
- package/dist/watchdog.d.ts.map +1 -0
- package/dist/watchdog.js +85 -0
- package/dist/watchdog.js.map +1 -0
- package/dist/zod.d.ts +32 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +49 -0
- package/dist/zod.js.map +1 -0
- package/package.json +76 -0
- package/src/context.ts +150 -0
- package/src/errors.ts +398 -0
- package/src/index.ts +12 -0
- package/src/providers/anthropic.ts +315 -0
- package/src/providers/openai.ts +246 -0
- package/src/retry.ts +246 -0
- package/src/schema.ts +67 -0
- package/src/tool-args.ts +117 -0
- package/src/tools.ts +237 -0
- package/src/transport.ts +162 -0
- package/src/types.ts +231 -0
- package/src/usage.ts +106 -0
- package/src/watchdog.ts +119 -0
- 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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"}
|
package/dist/retry.d.ts
ADDED
|
@@ -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"}
|