offrouter-core 0.2.1 → 0.2.2
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/audit.d.ts +137 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +320 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/credential-chain.d.ts +84 -0
- package/dist/auth/credential-chain.d.ts.map +1 -0
- package/dist/auth/credential-chain.js +150 -0
- package/dist/auth/credential-chain.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +60 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +104 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-pkce.d.ts +138 -0
- package/dist/auth/oauth-pkce.d.ts.map +1 -0
- package/dist/auth/oauth-pkce.js +375 -0
- package/dist/auth/oauth-pkce.js.map +1 -0
- package/dist/auth/oauth-server.d.ts +36 -0
- package/dist/auth/oauth-server.d.ts.map +1 -0
- package/dist/auth/oauth-server.js +210 -0
- package/dist/auth/oauth-server.js.map +1 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +355 -0
- package/dist/config.js.map +1 -0
- package/dist/delegation.d.ts +123 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +455 -0
- package/dist/delegation.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +45 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +318 -0
- package/dist/policy.js.map +1 -0
- package/{src/protocol.ts → dist/protocol.d.ts} +2 -2
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +6 -0
- package/dist/protocol.js.map +1 -0
- package/dist/providers/adapter.d.ts +137 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/adapter.js +163 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/catalog-data.d.ts +128 -0
- package/dist/providers/catalog-data.d.ts.map +1 -0
- package/dist/providers/catalog-data.js +397 -0
- package/dist/providers/catalog-data.js.map +1 -0
- package/dist/providers/catalog.d.ts +63 -0
- package/dist/providers/catalog.d.ts.map +1 -0
- package/dist/providers/catalog.js +394 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/fake.d.ts +46 -0
- package/dist/providers/fake.d.ts.map +1 -0
- package/dist/providers/fake.js +234 -0
- package/dist/providers/fake.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +65 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +434 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/proxy/responses-mapper.d.ts +366 -0
- package/dist/proxy/responses-mapper.d.ts.map +1 -0
- package/dist/proxy/responses-mapper.js +517 -0
- package/dist/proxy/responses-mapper.js.map +1 -0
- package/dist/proxy/responses-server.d.ts +29 -0
- package/dist/proxy/responses-server.d.ts.map +1 -0
- package/dist/proxy/responses-server.js +360 -0
- package/dist/proxy/responses-server.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/schemas.d.ts +560 -0
- package/dist/schemas.d.ts.map +1 -0
- package/{src/schemas.ts → dist/schemas.js} +83 -103
- package/dist/schemas.js.map +1 -0
- package/dist/secrets.d.ts +112 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +326 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-file.d.ts +59 -0
- package/dist/usage-file.d.ts.map +1 -0
- package/dist/usage-file.js +316 -0
- package/dist/usage-file.js.map +1 -0
- package/dist/usage.d.ts +235 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +517 -0
- package/dist/usage.js.map +1 -0
- package/package.json +7 -2
- package/src/audit.test.ts +0 -302
- package/src/audit.ts +0 -553
- package/src/auth/credential-chain.test.ts +0 -326
- package/src/auth/credential-chain.ts +0 -235
- package/src/auth/index.ts +0 -45
- package/src/auth/keychain.test.ts +0 -265
- package/src/auth/keychain.ts +0 -168
- package/src/auth/oauth-pkce.test.ts +0 -329
- package/src/auth/oauth-pkce.ts +0 -571
- package/src/auth/oauth-server.test.ts +0 -83
- package/src/auth/oauth-server.ts +0 -296
- package/src/config.test.ts +0 -479
- package/src/config.ts +0 -505
- package/src/delegation.test.ts +0 -368
- package/src/delegation.ts +0 -605
- package/src/index.ts +0 -280
- package/src/policy.test.ts +0 -634
- package/src/policy.ts +0 -420
- package/src/providers/adapter.test.ts +0 -293
- package/src/providers/adapter.ts +0 -328
- package/src/providers/catalog-data.test.ts +0 -258
- package/src/providers/catalog-data.ts +0 -498
- package/src/providers/catalog.test.ts +0 -84
- package/src/providers/catalog.ts +0 -483
- package/src/providers/fake.ts +0 -312
- package/src/providers/openai-compatible.test.ts +0 -366
- package/src/providers/openai-compatible.ts +0 -554
- package/src/proxy/responses-mapper.test.ts +0 -290
- package/src/proxy/responses-mapper.ts +0 -736
- package/src/proxy/responses-server.test.ts +0 -322
- package/src/proxy/responses-server.ts +0 -469
- package/src/router.test.ts +0 -699
- package/src/router.ts +0 -352
- package/src/schemas.test.ts +0 -291
- package/src/secrets.test.ts +0 -271
- package/src/secrets.ts +0 -461
- package/src/types.ts +0 -173
- package/src/usage-file.test.ts +0 -243
- package/src/usage-file.ts +0 -435
- package/src/usage.test.ts +0 -335
- package/src/usage.ts +0 -859
- package/tsconfig.json +0 -9
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Static built-in provider + model family metadata for offline routing.
|
|
3
|
-
*
|
|
4
|
-
* Metadata only: no network calls, no credential reads, no secrets. API-key
|
|
5
|
-
* presence is checked elsewhere by the adapter (checkAuth). Pricing hints are
|
|
6
|
-
* approximate public values in USD per 1M tokens; re-verify against vendor
|
|
7
|
-
* pricing before any billing-sensitive use. Unknown fields are left undefined;
|
|
8
|
-
* nothing here is invented.
|
|
9
|
-
*
|
|
10
|
-
* Subscription-first invariant: this catalog never auto-promotes an api-key
|
|
11
|
-
* candidate over a subscription candidate for the same logical family. The
|
|
12
|
-
* supportsSubscriptionRouting flag only documents which families expose an
|
|
13
|
-
* official subscription surface (official login, token helper, OAuth, or MCP
|
|
14
|
-
* login); subscription routing itself is handled by the harness adapters, not
|
|
15
|
-
* by these OpenAI-compatible provider entries.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { z } from "zod";
|
|
19
|
-
import type { AuthScope, AuthTier } from "../types.js";
|
|
20
|
-
import type { LogicalModelFamily } from "./catalog.js";
|
|
21
|
-
import type { OpenAiCompatibleFlavor } from "./openai-compatible.js";
|
|
22
|
-
|
|
23
|
-
/** Public pricing hint in USD per 1M tokens. Undefined when undocumented. */
|
|
24
|
-
export interface PricingHint {
|
|
25
|
-
/** USD per 1M input tokens, when publicly documented. */
|
|
26
|
-
inputPer1M?: number;
|
|
27
|
-
/** USD per 1M output tokens, when publicly documented. */
|
|
28
|
-
outputPer1M?: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Known model family metadata. Optional fields are unknown, never invented.
|
|
33
|
-
* Local runtime models omit token/capability fields because they are
|
|
34
|
-
* discovered from the running server.
|
|
35
|
-
*/
|
|
36
|
-
export interface BuiltinModelFamily {
|
|
37
|
-
modelId: string;
|
|
38
|
-
displayName?: string;
|
|
39
|
-
/** Logical family used to group candidates across auth tiers. */
|
|
40
|
-
family: LogicalModelFamily;
|
|
41
|
-
contextWindowTokens?: number;
|
|
42
|
-
maxOutputTokens?: number;
|
|
43
|
-
supportsTools?: boolean;
|
|
44
|
-
supportsStreaming?: boolean;
|
|
45
|
-
supportsJson?: boolean;
|
|
46
|
-
supportsImages?: boolean;
|
|
47
|
-
/** Public pricing hint; undefined when undocumented. */
|
|
48
|
-
pricing?: PricingHint;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Static provider entry describing how to build its OpenAI-compatible adapter
|
|
53
|
-
* and which model families it exposes. No secrets.
|
|
54
|
-
*/
|
|
55
|
-
export interface BuiltinProviderEntry {
|
|
56
|
-
providerId: string;
|
|
57
|
-
displayName: string;
|
|
58
|
-
/** Default base URL for the OpenAI-compatible adapter. */
|
|
59
|
-
defaultBaseUrl: string;
|
|
60
|
-
/** Adapter flavor used to construct the OpenAI-compatible client. */
|
|
61
|
-
flavor: OpenAiCompatibleFlavor;
|
|
62
|
-
/** Default auth tier for candidates built from this provider. */
|
|
63
|
-
defaultAuthTier: AuthTier;
|
|
64
|
-
/** Default auth scope for candidates built from this provider. */
|
|
65
|
-
defaultAuthScope: AuthScope;
|
|
66
|
-
/**
|
|
67
|
-
* True only when an official subscription surface exists (official login,
|
|
68
|
-
* token helper, OAuth, or MCP login). Informational only; never promotes a
|
|
69
|
-
* tier. False for pure api-key and local providers.
|
|
70
|
-
*/
|
|
71
|
-
supportsSubscriptionRouting: boolean;
|
|
72
|
-
/** ASCII-only note on the official subscription surface, if any. */
|
|
73
|
-
subscriptionSurfaceNote?: string;
|
|
74
|
-
knownModels: readonly BuiltinModelFamily[];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Per-provider catalog config mirroring the existing config providers map.
|
|
79
|
-
* Users enable/disable providers and override base URLs in TOML; this is not a
|
|
80
|
-
* new config file format.
|
|
81
|
-
*/
|
|
82
|
-
export interface ProviderCatalogConfig {
|
|
83
|
-
/** Whether the provider is enabled. Defaults to true. */
|
|
84
|
-
enabled?: boolean;
|
|
85
|
-
/** Override the built-in default base URL. */
|
|
86
|
-
baseUrl?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Map of providerId to provider catalog config (mirrors config providers map). */
|
|
90
|
-
export type BuiltinCatalogConfig = Record<string, ProviderCatalogConfig>;
|
|
91
|
-
|
|
92
|
-
// ---------------------------------------------------------------------------
|
|
93
|
-
// Runtime validation (Zod). Mirrors the config.ts pattern: a shape object plus
|
|
94
|
-
// a strict object schema so unknown keys are rejected. The TS interfaces above
|
|
95
|
-
// remain the source of truth for the catalog-data public types.
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
|
|
98
|
-
const ProviderCatalogConfigShape = {
|
|
99
|
-
enabled: z.boolean().optional(),
|
|
100
|
-
baseUrl: z.string().optional(),
|
|
101
|
-
} satisfies z.ZodRawShape;
|
|
102
|
-
|
|
103
|
-
export const ProviderCatalogConfigSchema = z
|
|
104
|
-
.object(ProviderCatalogConfigShape)
|
|
105
|
-
.strict();
|
|
106
|
-
|
|
107
|
-
/** Record of providerId -> ProviderCatalogConfig. */
|
|
108
|
-
export const BuiltinCatalogConfigSchema = z.record(
|
|
109
|
-
z.string().min(1),
|
|
110
|
-
ProviderCatalogConfigSchema,
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Parse and validate a BuiltinCatalogConfig, throwing on invalid input.
|
|
115
|
-
* Lets callers validate provider catalog overrides at runtime.
|
|
116
|
-
*/
|
|
117
|
-
export function parseBuiltinCatalogConfig(
|
|
118
|
-
input: unknown,
|
|
119
|
-
): BuiltinCatalogConfig {
|
|
120
|
-
return BuiltinCatalogConfigSchema.parse(input);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// TODO(catalog-config): wire base_url + enable/disable overrides declared in
|
|
124
|
-
// config.toml `[providers]` through BuiltinCatalogConfigSchema. config.ts only
|
|
125
|
-
// validates `enabled` today; integrating `baseUrl` there would change the
|
|
126
|
-
// config contract, so it is tracked as a follow-up (code-review finding 4).
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Shared note for local runtime providers: model list and capabilities are
|
|
130
|
-
* discovered from the running server, not from this static metadata.
|
|
131
|
-
*/
|
|
132
|
-
const LOCAL_RUNTIME_SUBSCRIPTION_NOTE =
|
|
133
|
-
"Local runtime; model list and capabilities are discovered from the running server.";
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Built-in providers. Subscription-backed first-party login happens through the
|
|
137
|
-
* harness adapters (claude/codex/gemini/grok), not these api endpoints, so each
|
|
138
|
-
* api/local entry reports supportsSubscriptionRouting=false with a note.
|
|
139
|
-
*/
|
|
140
|
-
export const BUILTIN_PROVIDERS: readonly BuiltinProviderEntry[] = [
|
|
141
|
-
{
|
|
142
|
-
providerId: "openai",
|
|
143
|
-
displayName: "OpenAI API",
|
|
144
|
-
defaultBaseUrl: "https://api.openai.com/v1",
|
|
145
|
-
flavor: "openai",
|
|
146
|
-
defaultAuthTier: "api-key",
|
|
147
|
-
defaultAuthScope: "first-party",
|
|
148
|
-
supportsSubscriptionRouting: false,
|
|
149
|
-
subscriptionSurfaceNote:
|
|
150
|
-
"Subscription routing for first-party OpenAI models is handled by the codex harness adapter, not this api endpoint.",
|
|
151
|
-
knownModels: [
|
|
152
|
-
{
|
|
153
|
-
modelId: "gpt-4.1",
|
|
154
|
-
displayName: "GPT-4.1",
|
|
155
|
-
family: "frontier-coder",
|
|
156
|
-
contextWindowTokens: 1_047_576,
|
|
157
|
-
maxOutputTokens: 32_768,
|
|
158
|
-
supportsTools: true,
|
|
159
|
-
supportsStreaming: true,
|
|
160
|
-
supportsJson: true,
|
|
161
|
-
supportsImages: true,
|
|
162
|
-
pricing: { inputPer1M: 2.0, outputPer1M: 8.0 },
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
modelId: "gpt-4.1-mini",
|
|
166
|
-
displayName: "GPT-4.1 mini",
|
|
167
|
-
family: "fast-coder",
|
|
168
|
-
contextWindowTokens: 1_047_576,
|
|
169
|
-
maxOutputTokens: 32_768,
|
|
170
|
-
supportsTools: true,
|
|
171
|
-
supportsStreaming: true,
|
|
172
|
-
supportsJson: true,
|
|
173
|
-
supportsImages: true,
|
|
174
|
-
pricing: { inputPer1M: 0.4, outputPer1M: 1.6 },
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
modelId: "o3",
|
|
178
|
-
displayName: "o3",
|
|
179
|
-
family: "frontier-coder",
|
|
180
|
-
contextWindowTokens: 200_000,
|
|
181
|
-
maxOutputTokens: 100_000,
|
|
182
|
-
supportsTools: true,
|
|
183
|
-
supportsStreaming: true,
|
|
184
|
-
supportsJson: true,
|
|
185
|
-
supportsImages: true,
|
|
186
|
-
pricing: { inputPer1M: 2.0, outputPer1M: 8.0 },
|
|
187
|
-
},
|
|
188
|
-
],
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
providerId: "anthropic",
|
|
192
|
-
displayName: "Anthropic API (OpenAI-compatible)",
|
|
193
|
-
defaultBaseUrl: "https://api.anthropic.com/v1/openai",
|
|
194
|
-
flavor: "openai",
|
|
195
|
-
defaultAuthTier: "api-key",
|
|
196
|
-
defaultAuthScope: "first-party",
|
|
197
|
-
supportsSubscriptionRouting: false,
|
|
198
|
-
subscriptionSurfaceNote:
|
|
199
|
-
"Subscription routing for first-party Anthropic models is handled by the claude harness adapter, not this api endpoint.",
|
|
200
|
-
knownModels: [
|
|
201
|
-
{
|
|
202
|
-
modelId: "claude-sonnet-4",
|
|
203
|
-
displayName: "Claude Sonnet 4",
|
|
204
|
-
family: "frontier-coder",
|
|
205
|
-
contextWindowTokens: 200_000,
|
|
206
|
-
maxOutputTokens: 32_000,
|
|
207
|
-
supportsTools: true,
|
|
208
|
-
supportsStreaming: true,
|
|
209
|
-
supportsJson: true,
|
|
210
|
-
supportsImages: true,
|
|
211
|
-
pricing: { inputPer1M: 3.0, outputPer1M: 15.0 },
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
modelId: "claude-opus-4",
|
|
215
|
-
displayName: "Claude Opus 4",
|
|
216
|
-
family: "frontier-coder",
|
|
217
|
-
contextWindowTokens: 200_000,
|
|
218
|
-
maxOutputTokens: 32_000,
|
|
219
|
-
supportsTools: true,
|
|
220
|
-
supportsStreaming: true,
|
|
221
|
-
supportsJson: true,
|
|
222
|
-
supportsImages: true,
|
|
223
|
-
pricing: { inputPer1M: 15.0, outputPer1M: 75.0 },
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
modelId: "claude-haiku-4",
|
|
227
|
-
displayName: "Claude Haiku 4",
|
|
228
|
-
family: "fast-coder",
|
|
229
|
-
contextWindowTokens: 200_000,
|
|
230
|
-
maxOutputTokens: 16_000,
|
|
231
|
-
supportsTools: true,
|
|
232
|
-
supportsStreaming: true,
|
|
233
|
-
supportsJson: true,
|
|
234
|
-
supportsImages: true,
|
|
235
|
-
},
|
|
236
|
-
],
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
providerId: "google",
|
|
240
|
-
displayName: "Google Gemini API (OpenAI-compatible)",
|
|
241
|
-
defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
242
|
-
flavor: "openai",
|
|
243
|
-
defaultAuthTier: "api-key",
|
|
244
|
-
defaultAuthScope: "first-party",
|
|
245
|
-
supportsSubscriptionRouting: false,
|
|
246
|
-
subscriptionSurfaceNote:
|
|
247
|
-
"Subscription routing for first-party Google models is handled by the gemini harness adapter, not this api endpoint.",
|
|
248
|
-
knownModels: [
|
|
249
|
-
{
|
|
250
|
-
modelId: "gemini-2.5-pro",
|
|
251
|
-
displayName: "Gemini 2.5 Pro",
|
|
252
|
-
family: "frontier-coder",
|
|
253
|
-
contextWindowTokens: 1_048_576,
|
|
254
|
-
maxOutputTokens: 65_536,
|
|
255
|
-
supportsTools: true,
|
|
256
|
-
supportsStreaming: true,
|
|
257
|
-
supportsJson: true,
|
|
258
|
-
supportsImages: true,
|
|
259
|
-
pricing: { inputPer1M: 1.25, outputPer1M: 10.0 },
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
modelId: "gemini-2.5-flash",
|
|
263
|
-
displayName: "Gemini 2.5 Flash",
|
|
264
|
-
family: "fast-coder",
|
|
265
|
-
contextWindowTokens: 1_048_576,
|
|
266
|
-
maxOutputTokens: 65_536,
|
|
267
|
-
supportsTools: true,
|
|
268
|
-
supportsStreaming: true,
|
|
269
|
-
supportsJson: true,
|
|
270
|
-
supportsImages: true,
|
|
271
|
-
pricing: { inputPer1M: 0.3, outputPer1M: 2.5 },
|
|
272
|
-
},
|
|
273
|
-
],
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
providerId: "xai",
|
|
277
|
-
displayName: "xAI Grok API",
|
|
278
|
-
defaultBaseUrl: "https://api.x.ai/v1",
|
|
279
|
-
flavor: "xai",
|
|
280
|
-
defaultAuthTier: "api-key",
|
|
281
|
-
defaultAuthScope: "first-party",
|
|
282
|
-
supportsSubscriptionRouting: false,
|
|
283
|
-
subscriptionSurfaceNote:
|
|
284
|
-
"Subscription routing for first-party xAI models is handled by the grok harness adapter, not this api endpoint.",
|
|
285
|
-
knownModels: [
|
|
286
|
-
{
|
|
287
|
-
modelId: "grok-4",
|
|
288
|
-
displayName: "Grok 4",
|
|
289
|
-
family: "frontier-coder",
|
|
290
|
-
contextWindowTokens: 256_000,
|
|
291
|
-
maxOutputTokens: 32_768,
|
|
292
|
-
supportsTools: true,
|
|
293
|
-
supportsStreaming: true,
|
|
294
|
-
supportsJson: true,
|
|
295
|
-
supportsImages: true,
|
|
296
|
-
pricing: { inputPer1M: 3.0, outputPer1M: 15.0 },
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
modelId: "grok-code-fast-1",
|
|
300
|
-
displayName: "Grok Code Fast",
|
|
301
|
-
family: "fast-coder",
|
|
302
|
-
contextWindowTokens: 256_000,
|
|
303
|
-
maxOutputTokens: 16_384,
|
|
304
|
-
supportsTools: true,
|
|
305
|
-
supportsStreaming: true,
|
|
306
|
-
supportsJson: true,
|
|
307
|
-
supportsImages: false,
|
|
308
|
-
},
|
|
309
|
-
],
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
providerId: "openrouter",
|
|
313
|
-
displayName: "OpenRouter",
|
|
314
|
-
defaultBaseUrl: "https://openrouter.ai/api/v1",
|
|
315
|
-
flavor: "openrouter",
|
|
316
|
-
defaultAuthTier: "api-key",
|
|
317
|
-
defaultAuthScope: "third-party",
|
|
318
|
-
supportsSubscriptionRouting: false,
|
|
319
|
-
knownModels: [
|
|
320
|
-
{
|
|
321
|
-
modelId: "anthropic/claude-sonnet-4",
|
|
322
|
-
family: "frontier-coder",
|
|
323
|
-
supportsTools: true,
|
|
324
|
-
supportsStreaming: true,
|
|
325
|
-
supportsJson: true,
|
|
326
|
-
supportsImages: true,
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
modelId: "google/gemini-2.5-flash",
|
|
330
|
-
family: "fast-coder",
|
|
331
|
-
supportsTools: true,
|
|
332
|
-
supportsStreaming: true,
|
|
333
|
-
supportsJson: true,
|
|
334
|
-
supportsImages: true,
|
|
335
|
-
},
|
|
336
|
-
],
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
providerId: "zai",
|
|
340
|
-
displayName: "Z.ai GLM API",
|
|
341
|
-
defaultBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
342
|
-
flavor: "openai",
|
|
343
|
-
defaultAuthTier: "api-key",
|
|
344
|
-
defaultAuthScope: "first-party",
|
|
345
|
-
supportsSubscriptionRouting: false,
|
|
346
|
-
knownModels: [
|
|
347
|
-
{
|
|
348
|
-
modelId: "glm-4.6",
|
|
349
|
-
displayName: "GLM-4.6",
|
|
350
|
-
family: "fast-coder",
|
|
351
|
-
contextWindowTokens: 200_000,
|
|
352
|
-
maxOutputTokens: 16_000,
|
|
353
|
-
supportsTools: true,
|
|
354
|
-
supportsStreaming: true,
|
|
355
|
-
supportsJson: true,
|
|
356
|
-
supportsImages: false,
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
modelId: "glm-4.5",
|
|
360
|
-
displayName: "GLM-4.5",
|
|
361
|
-
family: "fast-coder",
|
|
362
|
-
supportsTools: true,
|
|
363
|
-
supportsStreaming: true,
|
|
364
|
-
supportsJson: true,
|
|
365
|
-
supportsImages: false,
|
|
366
|
-
},
|
|
367
|
-
],
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
providerId: "moonshot",
|
|
371
|
-
displayName: "Moonshot Kimi API",
|
|
372
|
-
defaultBaseUrl: "https://api.moonshot.cn/v1",
|
|
373
|
-
flavor: "openai",
|
|
374
|
-
defaultAuthTier: "api-key",
|
|
375
|
-
defaultAuthScope: "first-party",
|
|
376
|
-
supportsSubscriptionRouting: false,
|
|
377
|
-
knownModels: [
|
|
378
|
-
{
|
|
379
|
-
modelId: "kimi-k2",
|
|
380
|
-
displayName: "Kimi K2",
|
|
381
|
-
family: "fast-coder",
|
|
382
|
-
contextWindowTokens: 200_000,
|
|
383
|
-
maxOutputTokens: 8_192,
|
|
384
|
-
supportsTools: true,
|
|
385
|
-
supportsStreaming: true,
|
|
386
|
-
supportsJson: true,
|
|
387
|
-
supportsImages: false,
|
|
388
|
-
},
|
|
389
|
-
],
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
providerId: "ollama",
|
|
393
|
-
displayName: "Ollama (local)",
|
|
394
|
-
defaultBaseUrl: "http://localhost:11434/v1",
|
|
395
|
-
flavor: "ollama",
|
|
396
|
-
defaultAuthTier: "local",
|
|
397
|
-
defaultAuthScope: "first-party",
|
|
398
|
-
supportsSubscriptionRouting: false,
|
|
399
|
-
subscriptionSurfaceNote: LOCAL_RUNTIME_SUBSCRIPTION_NOTE,
|
|
400
|
-
knownModels: [
|
|
401
|
-
{
|
|
402
|
-
modelId: "qwen2.5-coder:32b",
|
|
403
|
-
family: "local-coder",
|
|
404
|
-
supportsTools: true,
|
|
405
|
-
supportsStreaming: true,
|
|
406
|
-
supportsJson: true,
|
|
407
|
-
supportsImages: false,
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
modelId: "llava:latest",
|
|
411
|
-
family: "vision-helper",
|
|
412
|
-
supportsTools: false,
|
|
413
|
-
supportsStreaming: true,
|
|
414
|
-
supportsJson: false,
|
|
415
|
-
supportsImages: true,
|
|
416
|
-
},
|
|
417
|
-
],
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
providerId: "lm-studio",
|
|
421
|
-
displayName: "LM Studio (local)",
|
|
422
|
-
defaultBaseUrl: "http://localhost:1234/v1",
|
|
423
|
-
flavor: "openai",
|
|
424
|
-
defaultAuthTier: "local",
|
|
425
|
-
defaultAuthScope: "first-party",
|
|
426
|
-
supportsSubscriptionRouting: false,
|
|
427
|
-
subscriptionSurfaceNote: LOCAL_RUNTIME_SUBSCRIPTION_NOTE,
|
|
428
|
-
knownModels: [
|
|
429
|
-
{
|
|
430
|
-
modelId: "local-coder",
|
|
431
|
-
family: "local-coder",
|
|
432
|
-
supportsTools: true,
|
|
433
|
-
supportsStreaming: true,
|
|
434
|
-
supportsJson: true,
|
|
435
|
-
supportsImages: false,
|
|
436
|
-
},
|
|
437
|
-
],
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
providerId: "custom",
|
|
441
|
-
displayName: "Custom OpenAI-compatible endpoint",
|
|
442
|
-
defaultBaseUrl: "",
|
|
443
|
-
flavor: "openai",
|
|
444
|
-
defaultAuthTier: "local",
|
|
445
|
-
defaultAuthScope: "unknown",
|
|
446
|
-
supportsSubscriptionRouting: false,
|
|
447
|
-
subscriptionSurfaceNote:
|
|
448
|
-
"User-defined OpenAI-compatible gateway. base URL and model ids must be supplied via provider config.",
|
|
449
|
-
knownModels: [
|
|
450
|
-
{
|
|
451
|
-
modelId: "custom-model",
|
|
452
|
-
family: "local-coder",
|
|
453
|
-
supportsTools: true,
|
|
454
|
-
supportsStreaming: true,
|
|
455
|
-
supportsJson: true,
|
|
456
|
-
supportsImages: false,
|
|
457
|
-
},
|
|
458
|
-
],
|
|
459
|
-
},
|
|
460
|
-
];
|
|
461
|
-
|
|
462
|
-
/** Provider ids covered by the built-in catalog. */
|
|
463
|
-
export const BUILTIN_PROVIDER_IDS: readonly string[] = BUILTIN_PROVIDERS.map(
|
|
464
|
-
(provider) => provider.providerId,
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
/** Look up a built-in provider by id. */
|
|
468
|
-
export function getBuiltinProvider(
|
|
469
|
-
providerId: string,
|
|
470
|
-
): BuiltinProviderEntry | undefined {
|
|
471
|
-
return BUILTIN_PROVIDERS.find((p) => p.providerId === providerId);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Resolve built-in providers applying enable/disable and base URL overrides.
|
|
476
|
-
* Returns copies so callers cannot mutate the static source. Disabled providers
|
|
477
|
-
* are excluded. This never adds or removes a subscription tier; it only filters
|
|
478
|
-
* api-key/local provider metadata, preserving the subscription-first invariant.
|
|
479
|
-
*/
|
|
480
|
-
export function selectBuiltinProviders(
|
|
481
|
-
config: BuiltinCatalogConfig = {},
|
|
482
|
-
): BuiltinProviderEntry[] {
|
|
483
|
-
return BUILTIN_PROVIDERS.filter((provider) => {
|
|
484
|
-
const override = config[provider.providerId];
|
|
485
|
-
return override?.enabled ?? true;
|
|
486
|
-
}).map((provider) => {
|
|
487
|
-
const override = config[provider.providerId];
|
|
488
|
-
const overrideUrl = override?.baseUrl?.trim();
|
|
489
|
-
return {
|
|
490
|
-
...provider,
|
|
491
|
-
defaultBaseUrl:
|
|
492
|
-
overrideUrl && overrideUrl.length > 0
|
|
493
|
-
? overrideUrl
|
|
494
|
-
: provider.defaultBaseUrl,
|
|
495
|
-
knownModels: provider.knownModels.map((model) => ({ ...model })),
|
|
496
|
-
};
|
|
497
|
-
});
|
|
498
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import type { AuthTier } from "../types.js";
|
|
3
|
-
import {
|
|
4
|
-
MULTI_TIER_FAMILIES,
|
|
5
|
-
STATIC_CATALOG,
|
|
6
|
-
catalogToCandidates,
|
|
7
|
-
familyAuthTiers,
|
|
8
|
-
loadStaticCatalog,
|
|
9
|
-
type LogicalModelFamily,
|
|
10
|
-
} from "./catalog.js";
|
|
11
|
-
|
|
12
|
-
describe("static provider catalog", () => {
|
|
13
|
-
it("loads a non-empty static catalog offline", () => {
|
|
14
|
-
const entries = loadStaticCatalog();
|
|
15
|
-
expect(entries.length).toBeGreaterThan(0);
|
|
16
|
-
expect(entries).toEqual(
|
|
17
|
-
expect.arrayContaining(STATIC_CATALOG.map((e) => ({ ...e }))),
|
|
18
|
-
);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("returns copies so callers cannot mutate the static source", () => {
|
|
22
|
-
const [first] = loadStaticCatalog();
|
|
23
|
-
first.modelId = "mutated";
|
|
24
|
-
expect(STATIC_CATALOG[0]?.modelId).not.toBe("mutated");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("exposes subscription, local, and api-key candidates for key families", () => {
|
|
28
|
-
const required: AuthTier[] = ["subscription", "local", "api-key"];
|
|
29
|
-
for (const family of MULTI_TIER_FAMILIES) {
|
|
30
|
-
const tiers = familyAuthTiers(family);
|
|
31
|
-
for (const tier of required) {
|
|
32
|
-
expect(tiers.has(tier), `${family} missing tier ${tier}`).toBe(true);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("catalog candidates carry auth scope and subscription status for sub tiers", () => {
|
|
38
|
-
const candidates = catalogToCandidates();
|
|
39
|
-
expect(candidates.every((c) => c.authScope)).toBe(true);
|
|
40
|
-
|
|
41
|
-
const subs = candidates.filter((c) => c.authTier === "subscription");
|
|
42
|
-
expect(subs.length).toBeGreaterThan(0);
|
|
43
|
-
for (const s of subs) {
|
|
44
|
-
expect(s.subscriptionStatus).toBeDefined();
|
|
45
|
-
expect(["first-party", "third-party", "unknown"]).toContain(s.authScope);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("filters by family and auth tier", () => {
|
|
50
|
-
const family: LogicalModelFamily = "frontier-coder";
|
|
51
|
-
const onlySub = loadStaticCatalog({
|
|
52
|
-
families: [family],
|
|
53
|
-
authTiers: ["subscription"],
|
|
54
|
-
});
|
|
55
|
-
expect(onlySub.length).toBeGreaterThan(0);
|
|
56
|
-
expect(
|
|
57
|
-
onlySub.every(
|
|
58
|
-
(e) => e.family === family && e.authTier === "subscription",
|
|
59
|
-
),
|
|
60
|
-
).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("can exclude unhealthy entries", () => {
|
|
64
|
-
const healthy = loadStaticCatalog({ includeUnhealthy: false });
|
|
65
|
-
expect(healthy.every((e) => e.health === "healthy")).toBe(true);
|
|
66
|
-
expect(STATIC_CATALOG.some((e) => e.health !== "healthy")).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("maps catalog entries into ProviderCandidate rows usable by routing", () => {
|
|
70
|
-
const candidates = catalogToCandidates(
|
|
71
|
-
loadStaticCatalog({ families: ["fast-coder"], includeUnhealthy: false }),
|
|
72
|
-
);
|
|
73
|
-
expect(candidates.length).toBeGreaterThanOrEqual(3);
|
|
74
|
-
const tiers = new Set(candidates.map((c) => c.authTier));
|
|
75
|
-
expect(tiers.has("subscription")).toBe(true);
|
|
76
|
-
expect(tiers.has("local")).toBe(true);
|
|
77
|
-
expect(tiers.has("api-key")).toBe(true);
|
|
78
|
-
for (const c of candidates) {
|
|
79
|
-
expect(c.providerId).toBeTruthy();
|
|
80
|
-
expect(c.modelId).toBeTruthy();
|
|
81
|
-
expect(typeof c.supportsTools).toBe("boolean");
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
});
|