@providerkit/core 0.8.1 → 0.10.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/dist/capability.d.ts +41 -0
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +94 -0
- package/dist/capability.js.map +1 -1
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +171 -13
- package/dist/errors.js.map +1 -1
- package/dist/presets.d.ts +173 -28
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +172 -26
- package/dist/presets.js.map +1 -1
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/factory.d.ts.map +1 -1
- package/dist/providers/factory.js +1 -0
- package/dist/providers/factory.js.map +1 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +28 -10
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/openai.d.ts +6 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +58 -4
- package/dist/providers/openai.js.map +1 -1
- package/dist/rate-limit.d.ts +2 -0
- package/dist/rate-limit.d.ts.map +1 -1
- package/dist/rate-limit.js +91 -34
- package/dist/rate-limit.js.map +1 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +2 -2
- package/dist/retry.js.map +1 -1
- package/dist/schema.d.ts +20 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +112 -0
- package/dist/schema.js.map +1 -1
- package/dist/tool-args.d.ts +13 -1
- package/dist/tool-args.d.ts.map +1 -1
- package/dist/tool-args.js +50 -1
- package/dist/tool-args.js.map +1 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +3 -0
- package/dist/transport.js.map +1 -1
- package/dist/usage.d.ts +6 -0
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +20 -0
- package/dist/usage.js.map +1 -1
- package/package.json +3 -1
- package/src/capability.ts +157 -0
- package/src/errors.ts +181 -13
- package/src/presets.ts +177 -28
- package/src/providers/anthropic.ts +2 -2
- package/src/providers/factory.ts +1 -0
- package/src/providers/gemini.ts +30 -10
- package/src/providers/openai.ts +64 -4
- package/src/rate-limit.ts +98 -36
- package/src/retry.ts +8 -2
- package/src/schema.ts +127 -0
- package/src/tool-args.ts +48 -1
- package/src/transport.ts +4 -0
- package/src/usage.ts +21 -0
package/src/presets.ts
CHANGED
|
@@ -37,9 +37,11 @@ export type PresetAuth = "key" | "bearer" | "oauth";
|
|
|
37
37
|
export interface ProviderPreset {
|
|
38
38
|
/** Adapter wire format. */
|
|
39
39
|
shape: PresetShape;
|
|
40
|
-
/** API root —
|
|
41
|
-
*
|
|
40
|
+
/** API root — if it ends with /v1, /v4 or another version segment, the
|
|
41
|
+
* adapter automatically routes to /chat/completions instead of /v1/chat/completions. */
|
|
42
42
|
baseUrl: string;
|
|
43
|
+
/** Explicit path override when a gateway has custom routing. */
|
|
44
|
+
path?: string;
|
|
43
45
|
auth: PresetAuth;
|
|
44
46
|
/** Fallback when the caller passes no model. */
|
|
45
47
|
defaultModel?: string;
|
|
@@ -65,15 +67,15 @@ export const PROVIDER_PRESETS = {
|
|
|
65
67
|
shape: "anthropic",
|
|
66
68
|
baseUrl: "https://api.anthropic.com",
|
|
67
69
|
auth: "key",
|
|
68
|
-
defaultModel: "claude-sonnet-
|
|
69
|
-
models: ["claude-sonnet-
|
|
70
|
+
defaultModel: "claude-sonnet-5",
|
|
71
|
+
models: ["claude-sonnet-5", "claude-opus-5", "claude-fable-5-1", "claude-haiku-4-5"],
|
|
70
72
|
},
|
|
71
73
|
openai: {
|
|
72
74
|
shape: "openai",
|
|
73
75
|
baseUrl: "https://api.openai.com",
|
|
74
76
|
auth: "key",
|
|
75
77
|
defaultModel: "gpt-5.6-sol",
|
|
76
|
-
models: ["gpt-5.6-sol", "gpt-5-pro", "gpt-5-
|
|
78
|
+
models: ["gpt-5.6-sol", "gpt-5.5-pro", "gpt-5.4-mini"],
|
|
77
79
|
},
|
|
78
80
|
google: {
|
|
79
81
|
// Native Generative Language REST — the adapter authenticates with
|
|
@@ -81,8 +83,8 @@ export const PROVIDER_PRESETS = {
|
|
|
81
83
|
shape: "gemini",
|
|
82
84
|
baseUrl: "https://generativelanguage.googleapis.com",
|
|
83
85
|
auth: "key",
|
|
84
|
-
defaultModel: "gemini-3.1-pro",
|
|
85
|
-
models: ["gemini-3.1-pro", "gemini-
|
|
86
|
+
defaultModel: "gemini-3.1-pro-preview",
|
|
87
|
+
models: ["gemini-3.1-pro-preview", "gemini-3.8-flash", "gemini-flash-latest"],
|
|
86
88
|
},
|
|
87
89
|
/** Gemini behind its OpenAI-compatible route — the drop-in for codebases
|
|
88
90
|
* that only speak the openai shape. */
|
|
@@ -90,15 +92,17 @@ export const PROVIDER_PRESETS = {
|
|
|
90
92
|
shape: "openai",
|
|
91
93
|
baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
92
94
|
auth: "key",
|
|
93
|
-
defaultModel: "gemini-3.1-pro",
|
|
94
|
-
models: ["gemini-3.1-pro", "gemini-
|
|
95
|
+
defaultModel: "gemini-3.1-pro-preview",
|
|
96
|
+
models: ["gemini-3.1-pro-preview", "gemini-3.8-flash", "gemini-flash-latest"],
|
|
95
97
|
},
|
|
96
98
|
deepseek: {
|
|
97
99
|
shape: "openai",
|
|
98
100
|
baseUrl: "https://api.deepseek.com",
|
|
99
101
|
auth: "key",
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
// Measured 2026-09-13 via /models on live key: first-party serves only
|
|
103
|
+
// deepseek-flash and deepseek-v4-pro. v4.1-flash is OpenRouter/Baseten.
|
|
104
|
+
defaultModel: "deepseek-flash",
|
|
105
|
+
models: ["deepseek-flash", "deepseek-v4-pro"],
|
|
102
106
|
// Text-only API — an image part in the body is a hard 400 (measured).
|
|
103
107
|
textOnly: true,
|
|
104
108
|
},
|
|
@@ -107,7 +111,7 @@ export const PROVIDER_PRESETS = {
|
|
|
107
111
|
baseUrl: "https://openrouter.ai/api",
|
|
108
112
|
auth: "key",
|
|
109
113
|
defaultModel: "deepseek/deepseek-v4.1-flash",
|
|
110
|
-
models: ["deepseek/deepseek-v4.1-flash", "z-ai/glm-5.3-flash", "
|
|
114
|
+
models: ["deepseek/deepseek-v4.1-flash", "z-ai/glm-5.3-flash", "x-ai/grok-4.6", "openai/gpt-5.6-luna-pro"],
|
|
111
115
|
},
|
|
112
116
|
groq: {
|
|
113
117
|
shape: "openai",
|
|
@@ -121,25 +125,28 @@ export const PROVIDER_PRESETS = {
|
|
|
121
125
|
baseUrl: "https://api.mistral.ai/v1",
|
|
122
126
|
auth: "key",
|
|
123
127
|
defaultModel: "devstral-2512",
|
|
124
|
-
models: ["devstral-2512", "
|
|
128
|
+
models: ["devstral-2512", "mistral-large-latest", "magistral-small"],
|
|
125
129
|
},
|
|
126
130
|
xai: {
|
|
127
131
|
shape: "openai",
|
|
128
132
|
baseUrl: "https://api.x.ai/v1",
|
|
129
133
|
auth: "key",
|
|
130
|
-
defaultModel: "grok-4.
|
|
131
|
-
models: ["grok-4.
|
|
134
|
+
defaultModel: "grok-4.6",
|
|
135
|
+
models: ["grok-4.6", "grok-4.5", "grok-4.3"],
|
|
132
136
|
},
|
|
133
137
|
together: {
|
|
134
138
|
shape: "openai",
|
|
135
139
|
baseUrl: "https://api.together.xyz/v1",
|
|
136
140
|
auth: "key",
|
|
137
|
-
defaultModel: "
|
|
141
|
+
defaultModel: "MiniMaxAI/MiniMax-M3",
|
|
142
|
+
models: ["MiniMaxAI/MiniMax-M3", "Qwen/Qwen3-Coder-Next-FP8"],
|
|
138
143
|
},
|
|
139
144
|
fireworks: {
|
|
140
145
|
shape: "openai",
|
|
141
146
|
baseUrl: "https://api.fireworks.ai/inference/v1",
|
|
142
147
|
auth: "key",
|
|
148
|
+
defaultModel: "accounts/fireworks/models/deepseek-v4p1-flash",
|
|
149
|
+
models: ["accounts/fireworks/models/deepseek-v4p1-flash", "accounts/fireworks/models/glm-5p3-flash"],
|
|
143
150
|
},
|
|
144
151
|
cerebras: {
|
|
145
152
|
shape: "openai",
|
|
@@ -158,16 +165,119 @@ export const PROVIDER_PRESETS = {
|
|
|
158
165
|
zhipu: {
|
|
159
166
|
shape: "openai",
|
|
160
167
|
baseUrl: "https://api.z.ai/api/paas/v4",
|
|
168
|
+
path: "/chat/completions",
|
|
161
169
|
auth: "key",
|
|
162
170
|
defaultModel: "glm-5.3-flash",
|
|
163
|
-
models: ["glm-5.3-flash", "glm-5.
|
|
171
|
+
models: ["glm-5.3-flash", "glm-5.3", "glm-5.2"],
|
|
164
172
|
},
|
|
165
173
|
minimax: {
|
|
166
174
|
shape: "anthropic",
|
|
167
175
|
baseUrl: "https://api.minimax.io/anthropic",
|
|
168
176
|
auth: "key",
|
|
169
|
-
defaultModel: "MiniMax-
|
|
170
|
-
models: ["MiniMax-M3", "MiniMax-M2.
|
|
177
|
+
defaultModel: "MiniMax-M3",
|
|
178
|
+
models: ["MiniMax-M3", "MiniMax-M2.7", "MiniMax-M2.5"],
|
|
179
|
+
},
|
|
180
|
+
"minimax-openai": {
|
|
181
|
+
shape: "openai",
|
|
182
|
+
baseUrl: "https://api.minimax.io/v1",
|
|
183
|
+
auth: "key",
|
|
184
|
+
defaultModel: "MiniMax-M3",
|
|
185
|
+
models: ["MiniMax-M3", "MiniMax-M2.7", "MiniMax-M2.5"],
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
// ── Popular gateways & cloud providers ──────────────────────────────────
|
|
189
|
+
nvidia: {
|
|
190
|
+
shape: "openai",
|
|
191
|
+
baseUrl: "https://integrate.api.nvidia.com/v1",
|
|
192
|
+
auth: "key",
|
|
193
|
+
defaultModel: "deepseek-ai/deepseek-v4-flash",
|
|
194
|
+
models: ["deepseek-ai/deepseek-v4-flash", "deepseek-ai/deepseek-v4-pro"],
|
|
195
|
+
},
|
|
196
|
+
perplexity: {
|
|
197
|
+
shape: "openai",
|
|
198
|
+
baseUrl: "https://api.perplexity.ai",
|
|
199
|
+
auth: "key",
|
|
200
|
+
defaultModel: "sonar",
|
|
201
|
+
models: ["sonar", "sonar-pro", "sonar-reasoning-pro", "sonar-deep-research"],
|
|
202
|
+
},
|
|
203
|
+
deepinfra: {
|
|
204
|
+
shape: "openai",
|
|
205
|
+
baseUrl: "https://api.deepinfra.com/v1/openai",
|
|
206
|
+
path: "/chat/completions",
|
|
207
|
+
auth: "key",
|
|
208
|
+
defaultModel: "MiniMaxAI/MiniMax-M3",
|
|
209
|
+
models: ["MiniMaxAI/MiniMax-M3", "ByteDance/Seed-2.0-pro"],
|
|
210
|
+
},
|
|
211
|
+
nebius: {
|
|
212
|
+
shape: "openai",
|
|
213
|
+
baseUrl: "https://api.tokenfactory.nebius.com/v1",
|
|
214
|
+
auth: "key",
|
|
215
|
+
defaultModel: "Qwen/Qwen3.5-397B-A17B",
|
|
216
|
+
models: ["Qwen/Qwen3.5-397B-A17B", "deepseek-ai/DeepSeek-V4-Flash-0731"],
|
|
217
|
+
},
|
|
218
|
+
novita: {
|
|
219
|
+
shape: "openai",
|
|
220
|
+
baseUrl: "https://api.novita.ai/openai",
|
|
221
|
+
path: "/v1/chat/completions",
|
|
222
|
+
auth: "key",
|
|
223
|
+
defaultModel: "baichuan/baichuan-m2-32b",
|
|
224
|
+
models: ["baichuan/baichuan-m2-32b", "baidu/ernie-4.5-300b-a47b-paddle"],
|
|
225
|
+
},
|
|
226
|
+
chutes: {
|
|
227
|
+
shape: "openai",
|
|
228
|
+
baseUrl: "https://llm.chutes.ai/v1",
|
|
229
|
+
auth: "key",
|
|
230
|
+
defaultModel: "Qwen/Qwen3.8-27B-TEE",
|
|
231
|
+
models: ["Qwen/Qwen3.8-27B-TEE", "Qwen/Qwen3.5-397B-A17B-TEE"],
|
|
232
|
+
},
|
|
233
|
+
siliconflow: {
|
|
234
|
+
shape: "openai",
|
|
235
|
+
baseUrl: "https://api.siliconflow.com/v1",
|
|
236
|
+
auth: "key",
|
|
237
|
+
defaultModel: "Qwen/Qwen3-235B-A22B-Thinking-2507",
|
|
238
|
+
models: ["Qwen/Qwen3-235B-A22B-Thinking-2507", "MiniMaxAI/MiniMax-M2.5"],
|
|
239
|
+
},
|
|
240
|
+
volcengine: {
|
|
241
|
+
shape: "openai",
|
|
242
|
+
baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
|
243
|
+
auth: "key",
|
|
244
|
+
defaultModel: "doubao-seed-1-8-251228",
|
|
245
|
+
models: ["doubao-seed-1-8-251228", "deepseek-v4-flash-ga-260731"],
|
|
246
|
+
},
|
|
247
|
+
stepfun: {
|
|
248
|
+
shape: "openai",
|
|
249
|
+
baseUrl: "https://api.stepfun.com/v1",
|
|
250
|
+
auth: "key",
|
|
251
|
+
defaultModel: "step-3.7-flash",
|
|
252
|
+
models: ["step-3.7-flash", "step-3.5-flash"],
|
|
253
|
+
},
|
|
254
|
+
digitalocean: {
|
|
255
|
+
shape: "openai",
|
|
256
|
+
baseUrl: "https://inference.do-ai.run/v1",
|
|
257
|
+
auth: "key",
|
|
258
|
+
defaultModel: "anthropic-claude-3.7-sonnet",
|
|
259
|
+
models: ["anthropic-claude-3.7-sonnet", "alibaba-qwen3-32b"],
|
|
260
|
+
},
|
|
261
|
+
crusoe: {
|
|
262
|
+
shape: "openai",
|
|
263
|
+
baseUrl: "https://api.inference.crusoecloud.com/v1",
|
|
264
|
+
auth: "key",
|
|
265
|
+
defaultModel: "Qwen/Qwen3-235B-A22B-Instruct-2507",
|
|
266
|
+
models: ["Qwen/Qwen3-235B-A22B-Instruct-2507", "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B"],
|
|
267
|
+
},
|
|
268
|
+
baseten: {
|
|
269
|
+
shape: "openai",
|
|
270
|
+
baseUrl: "https://inference.baseten.co/v1",
|
|
271
|
+
auth: "key",
|
|
272
|
+
defaultModel: "deepseek-ai/DeepSeek-V4.1-Flash",
|
|
273
|
+
models: ["deepseek-ai/DeepSeek-V4.1-Flash", "MiniMaxAI/MiniMax-M2.5"],
|
|
274
|
+
},
|
|
275
|
+
huggingface: {
|
|
276
|
+
shape: "openai",
|
|
277
|
+
baseUrl: "https://router.huggingface.co/v1",
|
|
278
|
+
auth: "key",
|
|
279
|
+
defaultModel: "MiniMaxAI/MiniMax-M3",
|
|
280
|
+
models: ["MiniMaxAI/MiniMax-M3", "Qwen/Qwen2.5-Coder-32B-Instruct"],
|
|
171
281
|
},
|
|
172
282
|
|
|
173
283
|
// ── Coding plans — Anthropic wire + Bearer (measured family trait) ─────
|
|
@@ -176,25 +286,40 @@ export const PROVIDER_PRESETS = {
|
|
|
176
286
|
baseUrl: "https://api.z.ai/api/anthropic",
|
|
177
287
|
auth: "bearer",
|
|
178
288
|
defaultModel: "glm-5.3-flash",
|
|
179
|
-
models: ["glm-5.3-flash", "glm-5.3-highspeed", "glm-5.
|
|
289
|
+
models: ["glm-5.3-flash", "glm-5.3-highspeed", "glm-5.3", "glm-5.2"],
|
|
180
290
|
// Model ids are BARE here — the gateway-prefixed spelling
|
|
181
291
|
// (`z-ai/glm-5.3-flash`) answers 400 [1211] Unknown Model.
|
|
182
292
|
explicitNone: true,
|
|
183
293
|
},
|
|
294
|
+
"zai-openai": {
|
|
295
|
+
shape: "openai",
|
|
296
|
+
baseUrl: "https://api.z.ai/api/paas/v4",
|
|
297
|
+
path: "/chat/completions",
|
|
298
|
+
auth: "key",
|
|
299
|
+
defaultModel: "glm-5.3-flash",
|
|
300
|
+
models: ["glm-5.3-flash", "glm-5.3", "glm-5.2"],
|
|
301
|
+
},
|
|
184
302
|
kimi: {
|
|
185
303
|
shape: "anthropic",
|
|
186
304
|
baseUrl: "https://api.kimi.ai/coding",
|
|
187
305
|
auth: "bearer",
|
|
188
306
|
defaultModel: "kimi-for-coding",
|
|
189
|
-
models: ["kimi-for-coding", "kimi-for-coding-highspeed", "k3"],
|
|
307
|
+
models: ["kimi-for-coding", "kimi-for-coding-highspeed", "k3", "k3-256k"],
|
|
308
|
+
},
|
|
309
|
+
"kimi-openai": {
|
|
310
|
+
shape: "openai",
|
|
311
|
+
baseUrl: "https://api.moonshot.ai/v1",
|
|
312
|
+
auth: "key",
|
|
313
|
+
defaultModel: "kimi-k2.7-code",
|
|
314
|
+
models: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k3"],
|
|
190
315
|
},
|
|
191
316
|
/** Alibaba's dashscope coding plan — OpenAI-compatible per its registry. */
|
|
192
317
|
"alibaba-coding-plan": {
|
|
193
318
|
shape: "openai",
|
|
194
319
|
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1",
|
|
195
320
|
auth: "bearer",
|
|
196
|
-
defaultModel: "qwen3.
|
|
197
|
-
models: ["qwen3.
|
|
321
|
+
defaultModel: "qwen3.5-plus",
|
|
322
|
+
models: ["qwen3.5-plus", "qwen3-coder-next", "glm-5"],
|
|
198
323
|
},
|
|
199
324
|
/** QwenCloud's token plan — Anthropic wire (measured by tabrunner). */
|
|
200
325
|
qwen: {
|
|
@@ -202,7 +327,31 @@ export const PROVIDER_PRESETS = {
|
|
|
202
327
|
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic",
|
|
203
328
|
auth: "bearer",
|
|
204
329
|
defaultModel: "qwen3.8-max",
|
|
205
|
-
models: ["qwen3.8-max", "qwen3.6-flash"],
|
|
330
|
+
models: ["qwen3.8-max", "qwen3.8-flash", "qwen3.6-flash"],
|
|
331
|
+
},
|
|
332
|
+
/** QwenCloud's token plan — OpenAI-compatible endpoint. */
|
|
333
|
+
"qwen-token-plan-openai": {
|
|
334
|
+
shape: "openai",
|
|
335
|
+
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
|
|
336
|
+
auth: "bearer",
|
|
337
|
+
defaultModel: "qwen3.8-max",
|
|
338
|
+
models: ["qwen3.8-max", "qwen3.8-flash", "qwen3.6-flash"],
|
|
339
|
+
},
|
|
340
|
+
/** Alibaba DashScope Platform API — standard pay-as-you-go API key. */
|
|
341
|
+
alibaba: {
|
|
342
|
+
shape: "openai",
|
|
343
|
+
baseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
|
|
344
|
+
auth: "bearer",
|
|
345
|
+
defaultModel: "qwen-max",
|
|
346
|
+
models: ["qwen-max", "qwen-plus", "qwen-turbo", "qwen-flash"],
|
|
347
|
+
},
|
|
348
|
+
/** Alias for alibaba (DashScope platform API). */
|
|
349
|
+
"qwen-api": {
|
|
350
|
+
shape: "openai",
|
|
351
|
+
baseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
|
|
352
|
+
auth: "bearer",
|
|
353
|
+
defaultModel: "qwen-max",
|
|
354
|
+
models: ["qwen-max", "qwen-plus", "qwen-turbo", "qwen-flash"],
|
|
206
355
|
},
|
|
207
356
|
|
|
208
357
|
// ── Local runtimes ──────────────────────────────────────────────────────
|
|
@@ -227,8 +376,8 @@ export const PROVIDER_PRESETS = {
|
|
|
227
376
|
shape: "anthropic",
|
|
228
377
|
baseUrl: "https://api.anthropic.com",
|
|
229
378
|
auth: "oauth",
|
|
230
|
-
defaultModel: "claude-sonnet-
|
|
231
|
-
models: ["claude-sonnet-
|
|
379
|
+
defaultModel: "claude-sonnet-5",
|
|
380
|
+
models: ["claude-sonnet-5", "claude-opus-5", "claude-fable-5"],
|
|
232
381
|
// The beta flag is what switches the API into OAuth-token mode (measured
|
|
233
382
|
// by tabrunner's Claude plan sign-in).
|
|
234
383
|
headers: { "anthropic-beta": "claude-code-20250219,oauth-2025-04-20" },
|
|
@@ -240,7 +389,7 @@ export const PROVIDER_PRESETS = {
|
|
|
240
389
|
baseUrl: "https://chatgpt.com/backend-api/codex",
|
|
241
390
|
auth: "oauth",
|
|
242
391
|
defaultModel: "gpt-5.3-codex",
|
|
243
|
-
models: ["gpt-5.3-codex", "gpt-5.5", "gpt-5.4-mini"],
|
|
392
|
+
models: ["gpt-5.3-codex", "gpt-5.3-codex-spark", "gpt-5.5", "gpt-5.4-mini"],
|
|
244
393
|
},
|
|
245
394
|
/** Kimi's coding endpoint reached with a subscription token instead of a
|
|
246
395
|
* key — Kimi bills the two separately. */
|
|
@@ -255,7 +404,7 @@ export const PROVIDER_PRESETS = {
|
|
|
255
404
|
baseUrl: "https://api.githubcopilot.com",
|
|
256
405
|
auth: "oauth",
|
|
257
406
|
defaultModel: "gpt-5.6-sol",
|
|
258
|
-
models: ["gpt-5.6-sol", "claude-opus-5", "claude-
|
|
407
|
+
models: ["gpt-5.6-sol", "claude-opus-5", "claude-sonnet-5", "gpt-6-astra"],
|
|
259
408
|
},
|
|
260
409
|
} as const satisfies Record<string, ProviderPreset>;
|
|
261
410
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Anthropic-shape adapter — SSE from POST /v1/messages.
|
|
2
2
|
import { streamError } from "../errors.ts";
|
|
3
|
-
import { schemaPrompt } from "../schema.ts";
|
|
3
|
+
import { schemaPrompt, toAnthropicToolSchema } from "../schema.ts";
|
|
4
4
|
import { parseToolArgs } from "../tool-args.ts";
|
|
5
5
|
import { streamSse, apiUrl } from "../transport.ts";
|
|
6
6
|
import { withConfiguredFallbacks, type ProviderFallbackConfig } from "../fallback.ts";
|
|
@@ -249,7 +249,7 @@ export function createAnthropicProvider(config: AnthropicConfig): Provider {
|
|
|
249
249
|
request.tools = tools.map((tool) => ({
|
|
250
250
|
name: tool.name,
|
|
251
251
|
description: tool.description,
|
|
252
|
-
input_schema: tool.inputSchema,
|
|
252
|
+
input_schema: toAnthropicToolSchema(tool.inputSchema),
|
|
253
253
|
}));
|
|
254
254
|
}
|
|
255
255
|
if (opts.toolChoice && opts.toolChoice !== "auto") {
|
package/src/providers/factory.ts
CHANGED
|
@@ -91,6 +91,7 @@ export function createPresetProvider(id: ProviderPresetId, config: PresetProvide
|
|
|
91
91
|
effort: config.effort,
|
|
92
92
|
maxTokens: config.maxTokens,
|
|
93
93
|
baseUrl: preset.baseUrl,
|
|
94
|
+
...(preset.path ? { path: preset.path } : {}),
|
|
94
95
|
headers,
|
|
95
96
|
...(config.providerOrder ? { providerOrder: config.providerOrder } : {}),
|
|
96
97
|
fetchImpl: config.fetchImpl,
|
package/src/providers/gemini.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// wire rather than @google/genai, because this package ships zero dependencies
|
|
7
7
|
// and the SDK is a Node-shaped one.
|
|
8
8
|
import { streamError } from "../errors.ts";
|
|
9
|
-
import { schemaPrompt } from "../schema.ts";
|
|
9
|
+
import { schemaPrompt, toGeminiToolSchema } from "../schema.ts";
|
|
10
10
|
import { parseToolArgs } from "../tool-args.ts";
|
|
11
11
|
import { streamSse, apiUrl } from "../transport.ts";
|
|
12
12
|
import { withConfiguredFallbacks, type ProviderFallbackConfig } from "../fallback.ts";
|
|
@@ -69,6 +69,19 @@ const THINKING_LEVEL: Record<Effort, string> = {
|
|
|
69
69
|
max: "HIGH",
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Gemini 2.x thinking budget dial (tokens).
|
|
74
|
+
* Gemini 2.5 uses `thinkingBudget` (0 disables thinking), while Gemini 3+ uses
|
|
75
|
+
* `thinkingLevel` (MINIMAL, LOW, MEDIUM, HIGH).
|
|
76
|
+
*/
|
|
77
|
+
const GEMINI_2_THINKING_BUDGET: Record<Effort, number> = {
|
|
78
|
+
none: 0,
|
|
79
|
+
low: 1024,
|
|
80
|
+
medium: 4096,
|
|
81
|
+
high: 8192,
|
|
82
|
+
max: 16384,
|
|
83
|
+
};
|
|
84
|
+
|
|
72
85
|
/** A turn in Gemini's history. `model` is its word for the assistant. */
|
|
73
86
|
export interface GeminiContent {
|
|
74
87
|
role: "user" | "model";
|
|
@@ -261,15 +274,27 @@ export function createGeminiProvider(config: GeminiConfig): Provider {
|
|
|
261
274
|
const maxTokens = opts.maxTokens ?? config.maxTokens;
|
|
262
275
|
const { system, contents } = toGeminiContents(messages);
|
|
263
276
|
|
|
277
|
+
// A model id copied out of Gemini's docs is often already `models/…`, and
|
|
278
|
+
// the doubled segment 404s as "model not found" — a confusing way to
|
|
279
|
+
// learn about a prefix.
|
|
280
|
+
const model = (opts.model ?? config.model).replace(/^models\//, "");
|
|
281
|
+
const isGemini3 = /gemini-3/i.test(model);
|
|
282
|
+
|
|
264
283
|
const generationConfig: Record<string, unknown> = {};
|
|
265
284
|
if (maxTokens !== undefined) generationConfig.maxOutputTokens = maxTokens;
|
|
266
285
|
if (opts.temperature !== undefined) generationConfig.temperature = opts.temperature;
|
|
267
286
|
if (opts.topP !== undefined) generationConfig.topP = opts.topP;
|
|
268
287
|
if (opts.stopSequences?.length) generationConfig.stopSequences = opts.stopSequences;
|
|
269
|
-
// No effort means the model's own dynamic thinking. Sending MINIMAL here
|
|
288
|
+
// No effort means the model's own dynamic thinking. Sending MINIMAL / 0 here
|
|
270
289
|
// would switch that off for a caller who never asked, which is the whole
|
|
271
290
|
// reason the seam treats an absent effort as "never sent".
|
|
272
|
-
|
|
291
|
+
// Gemini 3 uses thinkingLevel (MINIMAL..HIGH), while Gemini 2.5 uses
|
|
292
|
+
// thinkingBudget (0 tokens disables thinking, positive numbers set budget).
|
|
293
|
+
if (effort) {
|
|
294
|
+
generationConfig.thinkingConfig = isGemini3
|
|
295
|
+
? { thinkingLevel: THINKING_LEVEL[effort] }
|
|
296
|
+
: { thinkingBudget: GEMINI_2_THINKING_BUDGET[effort] };
|
|
297
|
+
}
|
|
273
298
|
// A schema that shares its call with tools pins the decoder, and a pinned
|
|
274
299
|
// decoder cannot emit a functionCall — the model narrates the call it
|
|
275
300
|
// could not make and the turn ends, with nothing logged. See
|
|
@@ -281,7 +306,7 @@ export function createGeminiProvider(config: GeminiConfig): Provider {
|
|
|
281
306
|
// `responseJsonSchema` takes JSON Schema as written; `responseSchema`
|
|
282
307
|
// is Gemini's own trimmed dialect and rejects most of what a real
|
|
283
308
|
// schema carries.
|
|
284
|
-
generationConfig.responseJsonSchema = opts.json.schema;
|
|
309
|
+
generationConfig.responseJsonSchema = toGeminiToolSchema(opts.json.schema);
|
|
285
310
|
}
|
|
286
311
|
|
|
287
312
|
const request: Record<string, unknown> = { contents, generationConfig };
|
|
@@ -302,7 +327,7 @@ export function createGeminiProvider(config: GeminiConfig): Provider {
|
|
|
302
327
|
description: tool.description,
|
|
303
328
|
// Same rule as the response schema: `parameters` is the trimmed
|
|
304
329
|
// dialect, `parametersJsonSchema` is the schema we actually wrote.
|
|
305
|
-
parametersJsonSchema: tool.inputSchema,
|
|
330
|
+
parametersJsonSchema: toGeminiToolSchema(tool.inputSchema),
|
|
306
331
|
})),
|
|
307
332
|
},
|
|
308
333
|
];
|
|
@@ -312,11 +337,6 @@ export function createGeminiProvider(config: GeminiConfig): Provider {
|
|
|
312
337
|
if (toolConfig) request.toolConfig = toolConfig;
|
|
313
338
|
}
|
|
314
339
|
|
|
315
|
-
// A model id copied out of Gemini's docs is often already `models/…`, and
|
|
316
|
-
// the doubled segment 404s as "model not found" — a confusing way to
|
|
317
|
-
// learn about a prefix.
|
|
318
|
-
const model = (opts.model ?? config.model).replace(/^models\//, "");
|
|
319
|
-
|
|
320
340
|
// Gemini reports the finish reason on a candidate that can arrive AFTER
|
|
321
341
|
// the chunk carrying the function calls, so a turn's tool use has to be
|
|
322
342
|
// remembered rather than read off the final chunk.
|
package/src/providers/openai.ts
CHANGED
|
@@ -83,10 +83,56 @@ export interface OpenAIConfig extends ProviderFallbackConfig {
|
|
|
83
83
|
* not a lock.
|
|
84
84
|
*/
|
|
85
85
|
providerOrder?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* Automatically pin OpenRouter calls to the first-party model vendor's host
|
|
88
|
+
* when hitting openrouter.ai and providerOrder is not passed. Defaults to true.
|
|
89
|
+
*/
|
|
90
|
+
pinHost?: boolean;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
const DEFAULT_BASE_URL = "https://api.openai.com";
|
|
89
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Known first-party host slugs on OpenRouter for route pinning.
|
|
97
|
+
* Pinning keeps consecutive turns on the vendor's own endpoint, preserving
|
|
98
|
+
* the server-side KV prompt cache across rounds.
|
|
99
|
+
*/
|
|
100
|
+
const OPENROUTER_HOSTS: Record<string, string> = {
|
|
101
|
+
anthropic: "anthropic",
|
|
102
|
+
"arcee-ai": "arcee-ai",
|
|
103
|
+
cohere: "cohere",
|
|
104
|
+
deepseek: "deepseek",
|
|
105
|
+
google: "google-ai-studio",
|
|
106
|
+
meta: "meta",
|
|
107
|
+
minimax: "minimax",
|
|
108
|
+
mistralai: "mistral",
|
|
109
|
+
moonshotai: "moonshotai",
|
|
110
|
+
morph: "morph",
|
|
111
|
+
openai: "openai",
|
|
112
|
+
perplexity: "perplexity",
|
|
113
|
+
qwen: "alibaba",
|
|
114
|
+
stepfun: "stepfun",
|
|
115
|
+
tencent: "tencent",
|
|
116
|
+
upstage: "upstage",
|
|
117
|
+
"x-ai": "xai",
|
|
118
|
+
"z-ai": "z-ai",
|
|
119
|
+
zai: "z-ai",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export function openRouterHostFor(baseUrl: string, model: string): string | undefined {
|
|
123
|
+
let host: string;
|
|
124
|
+
try {
|
|
125
|
+
host = new URL(baseUrl).hostname;
|
|
126
|
+
} catch {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
if (!host.includes("openrouter.ai")) return undefined;
|
|
130
|
+
const slash = model.indexOf("/");
|
|
131
|
+
if (slash <= 0) return undefined;
|
|
132
|
+
const vendor = model.slice(0, slash).toLowerCase();
|
|
133
|
+
return OPENROUTER_HOSTS[vendor];
|
|
134
|
+
}
|
|
135
|
+
|
|
90
136
|
/** The spellings of "think this hard" across the dialects that share this
|
|
91
137
|
* adapter. `off` sends nothing and leaves the model on its own default. */
|
|
92
138
|
export type EffortDialect = "openai" | "openrouter" | "deepseek" | "off";
|
|
@@ -316,10 +362,11 @@ export function createOpenAIProvider(config: OpenAIConfig): Provider {
|
|
|
316
362
|
opts: StreamOptions = {},
|
|
317
363
|
): AsyncIterable<ProviderChunk> {
|
|
318
364
|
const effort = opts.effort ?? config.effort;
|
|
365
|
+
const model = opts.model ?? config.model;
|
|
319
366
|
|
|
320
367
|
const body = toOpenAIMessages(messages);
|
|
321
368
|
const request: Record<string, unknown> = {
|
|
322
|
-
model
|
|
369
|
+
model,
|
|
323
370
|
messages: body,
|
|
324
371
|
stream: true,
|
|
325
372
|
// Without this the usage record never arrives and every call costs
|
|
@@ -398,12 +445,25 @@ export function createOpenAIProvider(config: OpenAIConfig): Provider {
|
|
|
398
445
|
body.push({ role: "system", content: schemaPrompt(opts.json.schema) });
|
|
399
446
|
}
|
|
400
447
|
}
|
|
401
|
-
|
|
402
|
-
|
|
448
|
+
const pinOrder = config.providerOrder?.length
|
|
449
|
+
? config.providerOrder
|
|
450
|
+
: config.pinHost !== false
|
|
451
|
+
? (() => {
|
|
452
|
+
const pinned = openRouterHostFor(baseUrl, model);
|
|
453
|
+
return pinned ? [pinned] : undefined;
|
|
454
|
+
})()
|
|
455
|
+
: undefined;
|
|
456
|
+
|
|
457
|
+
if (pinOrder?.length) {
|
|
458
|
+
request.provider = { order: pinOrder, allow_fallbacks: true };
|
|
403
459
|
}
|
|
404
460
|
|
|
461
|
+
const defaultPath = /\/v\d+[^/]*$/i.test(baseUrl.replace(/\/+$/, ""))
|
|
462
|
+
? "/chat/completions"
|
|
463
|
+
: "/v1/chat/completions";
|
|
464
|
+
|
|
405
465
|
for await (const data of streamSse({
|
|
406
|
-
url: apiUrl(baseUrl, config.path ??
|
|
466
|
+
url: apiUrl(baseUrl, config.path ?? defaultPath),
|
|
407
467
|
headers: { authorization: `Bearer ${config.apiKey}`, ...config.headers },
|
|
408
468
|
body: request,
|
|
409
469
|
provider: id,
|