@codeworksh/aikit 0.11.0 → 0.12.0-canary.47.1.g3f60acd1

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 (32) hide show
  1. package/cli.mjs +3 -3
  2. package/{copilot-JLgwmznn.mjs → copilot-DvUXC6Kx.mjs} +2 -2
  3. package/{copilot-JLgwmznn.mjs.map → copilot-DvUXC6Kx.mjs.map} +1 -1
  4. package/{copilot-headers-BvQ3FFn-.mjs → copilot-headers-BLnBaJyp.mjs} +2 -2
  5. package/{copilot-headers-BvQ3FFn-.mjs.map → copilot-headers-BLnBaJyp.mjs.map} +1 -1
  6. package/{github-copilot-C7oJ2Tfp.mjs → github-copilot-BcmOr0jd.mjs} +2 -2
  7. package/{github-copilot-C7oJ2Tfp.mjs.map → github-copilot-BcmOr0jd.mjs.map} +1 -1
  8. package/github-copilot-llyP2NK4.mjs +3 -0
  9. package/index.d.mts +3 -3
  10. package/index.mjs +5 -5
  11. package/index.mjs.map +1 -1
  12. package/model-Cjp-Dr8V.mjs +13955 -0
  13. package/model-Cjp-Dr8V.mjs.map +1 -0
  14. package/{model-DCfjY0fS.d.mts → model-D3wc9-u4.d.mts} +42 -19
  15. package/modelgen-BdiCqi-R.mjs +730 -0
  16. package/modelgen-BdiCqi-R.mjs.map +1 -0
  17. package/modelgen.d.mts +20 -3
  18. package/modelgen.mjs +61 -2
  19. package/modelgen.mjs.map +1 -0
  20. package/oauth/github/copilot.mjs +1 -1
  21. package/{openai-codex-Dj0oh0UV.mjs → openai-codex-C1Yb02kM.mjs} +2 -2
  22. package/{openai-codex-Dj0oh0UV.mjs.map → openai-codex-C1Yb02kM.mjs.map} +1 -1
  23. package/openai-codex-CHxShIN3.mjs +2 -0
  24. package/package.json +10 -10
  25. package/{registry-CfGgGfSs.mjs → registry-u49t0S4S.mjs} +4 -4
  26. package/{registry-CfGgGfSs.mjs.map → registry-u49t0S4S.mjs.map} +1 -1
  27. package/github-copilot-CLJx-bcP.mjs +0 -3
  28. package/model-D-FoGzVN.mjs +0 -522
  29. package/model-D-FoGzVN.mjs.map +0 -1
  30. package/modelgen-D6UBM1fq.mjs +0 -561
  31. package/modelgen-D6UBM1fq.mjs.map +0 -1
  32. package/openai-codex-CIBVNhQx.mjs +0 -2
@@ -0,0 +1,730 @@
1
+ import { C as readJson, S as path, T as writeJsonAtomic, d as effortThinkingLevels, h as isAnthropicAdaptiveThinkingModel, i as KnownProviderEnum, t as APIMethodEnum, v as normalizeInput } from "./model-Cjp-Dr8V.mjs";
2
+ import { a as protocolForPackage, n as isAISDKPackage } from "./registry-u49t0S4S.mjs";
3
+ import { n as GITHUB_COPILOT_STATIC_HEADERS } from "./copilot-headers-BLnBaJyp.mjs";
4
+ import { resolve } from "node:path";
5
+ import { mapValues, pick, pickBy, pipe } from "remeda";
6
+ //#region src/model/openrouter.ts
7
+ const PROVIDER_ID = "openrouter";
8
+ const BASE_URL = "https://openrouter.ai/api/v1";
9
+ const NPM = "@openrouter/ai-sdk-provider";
10
+ const USER_MODELS_TIMEOUT_MS = 15e3;
11
+ /**
12
+ * $/token to $/1M tokens. Missing, invalid and negative prices use the fallback: OpenRouter
13
+ * reports -1 for router models such as `openrouter/auto`, whose price depends on the model it picks.
14
+ */
15
+ function perMillion(value, fallback) {
16
+ const perToken = Number.parseFloat(value ?? "");
17
+ return perToken >= 0 ? Number((perToken * 1e6).toFixed(6)) : fallback;
18
+ }
19
+ /**
20
+ * Prompt-length overrides become request-wide tiers; a rate a tier does not list keeps the base
21
+ * price. Time-of-day overrides are skipped: the cost model cannot express them.
22
+ */
23
+ function cost(pricing) {
24
+ const base = {
25
+ input: perMillion(pricing?.prompt, 0),
26
+ output: perMillion(pricing?.completion, 0),
27
+ cacheRead: perMillion(pricing?.input_cache_read, 0),
28
+ cacheWrite: perMillion(pricing?.input_cache_write, 0)
29
+ };
30
+ const tiers = (pricing?.overrides ?? []).flatMap((override) => {
31
+ if (override.min_prompt_tokens === void 0 || override.utc_start !== void 0 || override.utc_end !== void 0 || override.utc_days !== void 0) return [];
32
+ return [{
33
+ inputTokensAbove: override.min_prompt_tokens,
34
+ input: perMillion(override.prompt, base.input),
35
+ output: perMillion(override.completion, base.output),
36
+ cacheRead: perMillion(override.input_cache_read, base.cacheRead),
37
+ cacheWrite: perMillion(override.input_cache_write, base.cacheWrite)
38
+ }];
39
+ });
40
+ return tiers.length > 0 ? {
41
+ ...base,
42
+ tiers
43
+ } : base;
44
+ }
45
+ /**
46
+ * `supported_efforts` uses models.dev's effort vocabulary. Off maps to `none` unless reasoning is
47
+ * mandatory; a mandatory model without listed efforts can only refuse off.
48
+ */
49
+ function thinkingLevels(reasoning) {
50
+ if (!reasoning) return void 0;
51
+ const map = effortThinkingLevels(reasoning.supported_efforts ?? []);
52
+ if (!map) return reasoning.mandatory === true ? { off: null } : void 0;
53
+ return {
54
+ ...map,
55
+ off: reasoning.mandatory === true ? null : "none"
56
+ };
57
+ }
58
+ /**
59
+ * The catalog entry for one listed model, or undefined for one an agent cannot use: no tool
60
+ * calling, or a `:batch` variant, whose asynchronous pricing an interactive turn never gets.
61
+ */
62
+ function toModel(entry) {
63
+ if (!entry.supported_parameters?.includes("tools") || entry.id.endsWith(":batch")) return void 0;
64
+ const thinkingLevelMap = thinkingLevels(entry.reasoning);
65
+ return {
66
+ id: entry.id,
67
+ name: entry.name ?? entry.id,
68
+ provider: {
69
+ id: PROVIDER_ID,
70
+ name: "OpenRouter",
71
+ source: "api",
72
+ env: ["OPENROUTER_API_KEY"]
73
+ },
74
+ baseUrl: BASE_URL,
75
+ reasoning: entry.supported_parameters.includes("reasoning"),
76
+ ...thinkingLevelMap ? { thinkingLevelMap } : {},
77
+ input: normalizeInput(entry.architecture?.input_modalities),
78
+ cost: cost(entry.pricing),
79
+ contextWindow: entry.top_provider?.context_length || entry.context_length || 4096,
80
+ maxTokens: entry.top_provider?.max_completion_tokens || 4096,
81
+ npm: NPM,
82
+ api: {
83
+ id: entry.id,
84
+ url: BASE_URL,
85
+ method: APIMethodEnum.languageModel
86
+ },
87
+ providerOptionsKey: PROVIDER_ID,
88
+ protocol: KnownProviderEnum.openrouter
89
+ };
90
+ }
91
+ function entries(body) {
92
+ if (typeof body !== "object" || body === null || !("data" in body) || !Array.isArray(body.data)) throw new Error("invalid OpenRouter model list");
93
+ return body.data.filter((entry) => typeof entry === "object" && entry !== null && "id" in entry && typeof entry.id === "string");
94
+ }
95
+ /** The public `GET /models`, built into catalog entries keyed by id. */
96
+ function catalog(body) {
97
+ const models = {};
98
+ for (const entry of entries(body)) {
99
+ const model = toModel(entry);
100
+ if (model) models[model.id] = model;
101
+ }
102
+ return models;
103
+ }
104
+ /**
105
+ * Restrict OpenRouter models to those `key` may use, with the key's limits and prices from
106
+ * `GET /models/user` (pi). A missing limit keeps the catalog value. When the endpoint rejects the
107
+ * key (401/403) or does not exist (404), or allows nothing, the models come back unchanged.
108
+ */
109
+ async function personalize(models, key, options = {}) {
110
+ const signal = AbortSignal.timeout(USER_MODELS_TIMEOUT_MS);
111
+ const response = await fetch(`${(options.baseUrl ?? "https://openrouter.ai/api/v1").replace(/\/+$/, "")}/models/user`, {
112
+ headers: {
113
+ accept: "application/json",
114
+ authorization: `Bearer ${key}`
115
+ },
116
+ signal: options.signal ? AbortSignal.any([options.signal, signal]) : signal
117
+ });
118
+ if (response.status === 401 || response.status === 403 || response.status === 404) return models;
119
+ if (!response.ok) throw new Error(`OpenRouter model list request failed: ${response.status}`);
120
+ const allowed = new Map(entries(await response.json()).map((entry) => [entry.id, entry]));
121
+ const personal = {};
122
+ for (const [id, model] of Object.entries(models)) {
123
+ const entry = allowed.get(id);
124
+ if (!entry) continue;
125
+ personal[id] = {
126
+ ...model,
127
+ contextWindow: entry.top_provider?.context_length || entry.context_length || model.contextWindow,
128
+ maxTokens: entry.top_provider?.max_completion_tokens || model.maxTokens,
129
+ cost: entry.pricing ? cost(entry.pricing) : model.cost
130
+ };
131
+ }
132
+ return Object.keys(personal).length > 0 ? personal : models;
133
+ }
134
+ //#endregion
135
+ //#region src/cli/modelgen.ts
136
+ const DEFAULT_PROVIDER_BASE_URLS = {
137
+ [KnownProviderEnum.anthropic]: "https://api.anthropic.com/v1",
138
+ [KnownProviderEnum.openai]: "https://api.openai.com/v1",
139
+ [KnownProviderEnum.openrouter]: BASE_URL,
140
+ [KnownProviderEnum.xai]: "https://api.x.ai/v1"
141
+ };
142
+ /** Providers bundled with aikit as the baseline catalog, read until a generated one exists. */
143
+ const BASELINE_PROVIDERS = [
144
+ "anthropic",
145
+ "openai",
146
+ PROVIDER_ID
147
+ ];
148
+ function modelsDevURL() {
149
+ return process.env.CODEWORK_MODELS_DEV_URL || "https://models.dev";
150
+ }
151
+ function modelsDevPath() {
152
+ return process.env.CODEWORK_MODELS_DEV_FILE;
153
+ }
154
+ async function pullModelsDevData() {
155
+ const path = modelsDevPath();
156
+ if (path) {
157
+ const result = await readJson(path).catch(() => void 0);
158
+ if (result) return result;
159
+ }
160
+ const response = await fetch(`${modelsDevURL()}/api.json`);
161
+ if (!response.ok) throw new Error(`models.dev responded ${response.status} ${response.statusText}`);
162
+ return await response.json();
163
+ }
164
+ /** OpenRouter's public listing, or undefined when it cannot be had: models.dev's copy stands in. */
165
+ async function pullOpenRouterData() {
166
+ try {
167
+ const response = await fetch(`${BASE_URL}/models`, { signal: AbortSignal.timeout(3e4) });
168
+ return response.ok ? await response.json() : void 0;
169
+ } catch {
170
+ return;
171
+ }
172
+ }
173
+ function toModelValue(providerId, provider, model) {
174
+ return applyModification(providerId, provider, model);
175
+ }
176
+ const MODELS_DEV_UNSUPPORTED_MODEL_IDS = /* @__PURE__ */ new Set(["openai/gpt-5.6"]);
177
+ function usable(providerId, provider, model) {
178
+ return Boolean(model.tool_call) && !MODELS_DEV_UNSUPPORTED_MODEL_IDS.has(`${providerId}/${model.id}`) && isAISDKPackage(resolveModelNpm(provider, model));
179
+ }
180
+ function loadBuiltInFromModelsDev(catalog) {
181
+ return pipe(catalog, pickBy((provider, providerId) => Object.values(provider.models).some((model) => usable(providerId, provider, model))), mapValues((provider, providerId) => pipe(provider.models, pickBy((model) => usable(providerId, provider, model)), mapValues((model) => toModelValue(providerId, provider, model)), pickBy((model) => model !== void 0))));
182
+ }
183
+ function resolveModelNpm(provider, model) {
184
+ return model.provider?.npm ?? provider.npm ?? "@ai-sdk/openai-compatible";
185
+ }
186
+ function resolveModelBaseUrl(provider, model) {
187
+ const npm = resolveModelNpm(provider, model);
188
+ const protocol = protocolForPackage(npm);
189
+ return model.baseUrl ?? model.provider?.api ?? provider.api ?? DEFAULT_PROVIDER_BASE_URLS[protocol] ?? "";
190
+ }
191
+ function resolveModelAPIMethod(npm) {
192
+ const protocol = protocolForPackage(npm);
193
+ if (protocol === KnownProviderEnum.openai || protocol === KnownProviderEnum.xai) return APIMethodEnum.responses;
194
+ return APIMethodEnum.languageModel;
195
+ }
196
+ function resolveProviderOptionsKey(protocol) {
197
+ if (protocol === KnownProviderEnum.openaiCompatible) return "openai-compatible";
198
+ if (protocol === KnownProviderEnum.googleVertex) return "google-vertex";
199
+ if (protocol === KnownProviderEnum.googleVertexAnthropic) return "google-vertex-anthropic";
200
+ return protocol;
201
+ }
202
+ function roundCost(value) {
203
+ return Number(value.toFixed(6));
204
+ }
205
+ /** models.dev prices, with its context tiers; a rate a tier does not list keeps the base price (pi). */
206
+ function modelsDevCost(cost) {
207
+ const base = {
208
+ input: cost?.input ?? 0,
209
+ output: cost?.output ?? 0,
210
+ cacheRead: cost?.cache_read ?? 0,
211
+ cacheWrite: cost?.cache_write ?? 0
212
+ };
213
+ const tiers = cost?.tiers?.flatMap((tier) => {
214
+ const context = tier.tier;
215
+ if (context?.type !== "context" || context.size === void 0) return [];
216
+ return [{
217
+ inputTokensAbove: context.size,
218
+ input: tier.input ?? base.input,
219
+ output: tier.output ?? base.output,
220
+ cacheRead: tier.cache_read ?? base.cacheRead,
221
+ cacheWrite: tier.cache_write ?? base.cacheWrite
222
+ }];
223
+ });
224
+ return tiers && tiers.length > 0 ? {
225
+ ...base,
226
+ tiers
227
+ } : base;
228
+ }
229
+ /** The efforts models.dev verified for a model, or undefined when it lists none. */
230
+ function modelsDevEfforts(model) {
231
+ const efforts = model?.reasoning_options?.flatMap((option) => option.type === "effort" && Array.isArray(option.values) ? option.values : []);
232
+ return efforts && efforts.length > 0 ? efforts : void 0;
233
+ }
234
+ function mergeThinkingLevelMap(model, map) {
235
+ model.thinkingLevelMap = {
236
+ ...model.thinkingLevelMap,
237
+ ...map
238
+ };
239
+ }
240
+ function mergeCompat(model, compat) {
241
+ model.compat = {
242
+ ...model.compat,
243
+ ...compat
244
+ };
245
+ }
246
+ /** Merge the level map models.dev's verified efforts give, when it lists any. */
247
+ function applyEffortThinkingLevels(model, source) {
248
+ const efforts = modelsDevEfforts(source);
249
+ const map = efforts ? effortThinkingLevels(efforts) : void 0;
250
+ if (map) mergeThinkingLevelMap(model, map);
251
+ }
252
+ const OPENAI_LONG_CONTEXT_INPUT_THRESHOLD = 272e3;
253
+ const OPENAI_MAX_TOKENS = 128e3;
254
+ const OPENAI_RESPONSES_NONE_REASONING_MODELS = /* @__PURE__ */ new Set([
255
+ "gpt-5.1",
256
+ "gpt-5.2",
257
+ "gpt-5.3-codex",
258
+ "gpt-5.4",
259
+ "gpt-5.4-mini",
260
+ "gpt-5.4-nano",
261
+ "gpt-5.5",
262
+ "gpt-5.6-luna",
263
+ "gpt-5.6-sol",
264
+ "gpt-5.6-terra",
265
+ "gpt-6-luna",
266
+ "gpt-6-sol"
267
+ ]);
268
+ const OPENAI_GPT6_MODELS = /* @__PURE__ */ new Set([
269
+ "gpt-6-astra",
270
+ "gpt-6-luna",
271
+ "gpt-6-sol",
272
+ "gpt-6.1-sol"
273
+ ]);
274
+ const OPENAI_GPT6_NO_NONE_MODELS = /* @__PURE__ */ new Set(["gpt-6-astra", "gpt-6.1-sol"]);
275
+ const OPENAI_NO_MINIMAL_REASONING = /gpt-5\.[456]|gpt-6/;
276
+ const OPENAI_SHORT_CONTEXT_CAPPED_MODELS = /* @__PURE__ */ new Set([
277
+ "gpt-5.4",
278
+ "gpt-5.5",
279
+ "gpt-5.6-luna",
280
+ "gpt-5.6-sol",
281
+ "gpt-5.6-terra",
282
+ "gpt-6-astra",
283
+ "gpt-6-luna",
284
+ "gpt-6-sol",
285
+ "gpt-6.1-sol"
286
+ ]);
287
+ const OPENAI_LONG_CONTEXT_PRICING_MODELS = /* @__PURE__ */ new Set([
288
+ ...OPENAI_SHORT_CONTEXT_CAPPED_MODELS,
289
+ "gpt-5.4-pro",
290
+ "gpt-5.5-pro"
291
+ ]);
292
+ function isOpenAiResponses(model) {
293
+ return model.protocol === KnownProviderEnum.openai || model.protocol === KnownProviderEnum.openaiCodex;
294
+ }
295
+ function isOpenAiDirect(model) {
296
+ return model.protocol === KnownProviderEnum.openai && model.provider.id === "openai";
297
+ }
298
+ function supportsOpenAiXhigh(model) {
299
+ return /gpt-5\.[2-6]|gpt-6/.test(model.id);
300
+ }
301
+ function supportsOpenAiMax(model) {
302
+ return isOpenAiResponses(model) && /gpt-5\.6|gpt-6/.test(model.id);
303
+ }
304
+ function withOpenAiLongContextPricing(cost) {
305
+ return {
306
+ ...cost,
307
+ tiers: [{
308
+ inputTokensAbove: OPENAI_LONG_CONTEXT_INPUT_THRESHOLD,
309
+ input: roundCost(cost.input * 2),
310
+ output: roundCost(cost.output * 1.5),
311
+ cacheRead: roundCost(cost.cacheRead * 2),
312
+ cacheWrite: roundCost(cost.cacheWrite * 2)
313
+ }]
314
+ };
315
+ }
316
+ /**
317
+ * Price multipliers per service tier: models.dev's modes that set `service_tier` (fast is
318
+ * `priority`), over OpenAI's flex 0.5x / priority 2x list defaults.
319
+ */
320
+ function serviceTierMultipliers(model, source) {
321
+ const multipliers = {
322
+ flex: .5,
323
+ priority: 2
324
+ };
325
+ for (const mode of Object.values(source?.experimental?.modes ?? {})) {
326
+ const tier = mode.provider?.body?.service_tier;
327
+ const input = mode.cost?.input;
328
+ if (typeof tier !== "string" || input === void 0 || model.cost.input <= 0) continue;
329
+ multipliers[tier] = roundCost(input / model.cost.input);
330
+ }
331
+ return multipliers;
332
+ }
333
+ function applyOpenAiMetadata(model, source) {
334
+ const id = model.id;
335
+ if (isOpenAiDirect(model)) {
336
+ if (OPENAI_SHORT_CONTEXT_CAPPED_MODELS.has(id)) {
337
+ model.contextWindow = OPENAI_LONG_CONTEXT_INPUT_THRESHOLD;
338
+ model.maxTokens = OPENAI_MAX_TOKENS;
339
+ }
340
+ if (OPENAI_LONG_CONTEXT_PRICING_MODELS.has(id) && !model.cost.tiers) model.cost = withOpenAiLongContextPricing(model.cost);
341
+ if (id === "gpt-5-pro") model.maxTokens = OPENAI_MAX_TOKENS;
342
+ }
343
+ model.cost.serviceTierMultipliers = serviceTierMultipliers(model, source);
344
+ applyEffortThinkingLevels(model, source);
345
+ if (model.protocol === KnownProviderEnum.openai && id.startsWith("gpt-5")) mergeThinkingLevelMap(model, { off: null });
346
+ if (OPENAI_GPT6_MODELS.has(id)) mergeThinkingLevelMap(model, {
347
+ off: OPENAI_GPT6_NO_NONE_MODELS.has(id) ? null : "none",
348
+ minimal: null,
349
+ low: "low",
350
+ medium: "medium",
351
+ high: "high",
352
+ xhigh: "xhigh",
353
+ max: "max"
354
+ });
355
+ if (isOpenAiDirect(model) && OPENAI_RESPONSES_NONE_REASONING_MODELS.has(id)) mergeThinkingLevelMap(model, { off: "none" });
356
+ if (supportsOpenAiMax(model)) mergeThinkingLevelMap(model, { max: "max" });
357
+ if (OPENAI_NO_MINIMAL_REASONING.test(id)) mergeThinkingLevelMap(model, { minimal: null });
358
+ if (id.endsWith("gpt-5.5-pro")) mergeThinkingLevelMap(model, {
359
+ off: null,
360
+ minimal: null,
361
+ low: null
362
+ });
363
+ }
364
+ function isAnthropicMessages(model) {
365
+ return model.protocol === KnownProviderEnum.anthropic || model.protocol === KnownProviderEnum.googleVertexAnthropic;
366
+ }
367
+ const ANTHROPIC_NO_TEMPERATURE = /opus-4[-.][78]|opus-5|(sonnet|haiku)-5[-.]5/;
368
+ function applyAnthropicMetadata(model) {
369
+ const id = model.id;
370
+ if (/sonnet-4[-.]5/.test(id)) model.contextWindow = 2e5;
371
+ if (isAnthropicAdaptiveThinkingModel(id)) mergeCompat(model, { forceAdaptiveThinking: true });
372
+ if (/(opus|sonnet)-4[-.]6/.test(id)) mergeThinkingLevelMap(model, { max: "max" });
373
+ if (/opus-4[-.][78]|(opus|sonnet|fable|haiku)-5/.test(id)) mergeThinkingLevelMap(model, {
374
+ xhigh: "xhigh",
375
+ max: "max"
376
+ });
377
+ if (/(opus|sonnet|haiku)-5[-.]5/.test(id)) mergeThinkingLevelMap(model, {
378
+ ...id.includes("haiku") ? {} : { off: null },
379
+ minimal: null,
380
+ low: "low",
381
+ medium: "medium",
382
+ high: "high",
383
+ xhigh: "xhigh",
384
+ max: "max"
385
+ });
386
+ if (/fable-5/.test(id)) mergeThinkingLevelMap(model, { off: null });
387
+ if (ANTHROPIC_NO_TEMPERATURE.test(id)) mergeCompat(model, { supportsTemperature: false });
388
+ }
389
+ function applyGoogleThinkingMetadata(model) {
390
+ const id = model.id.toLowerCase();
391
+ const supportsThinkingLevel = /gemini-3(?:\.\d+)?-(?:pro|flash)|gemma-?4/.test(id) || id === "gemini-flash-latest" || id === "gemini-flash-lite-latest";
392
+ mergeCompat(model, { supportsThinkingLevel });
393
+ if (supportsThinkingLevel) return;
394
+ model.thinkingBudgets = [
395
+ ["2.5-pro", {
396
+ minimal: 128,
397
+ low: 2048,
398
+ medium: 8192,
399
+ high: 32768
400
+ }],
401
+ ["2.5-flash-lite", {
402
+ minimal: 512,
403
+ low: 2048,
404
+ medium: 8192,
405
+ high: 24576
406
+ }],
407
+ ["2.5-flash", {
408
+ minimal: 128,
409
+ low: 2048,
410
+ medium: 8192,
411
+ high: 24576
412
+ }]
413
+ ].find(([family]) => id.includes(family))?.[1] ?? {
414
+ minimal: -1,
415
+ low: -1,
416
+ medium: -1,
417
+ high: -1
418
+ };
419
+ }
420
+ function applyModelMetadata(model, source) {
421
+ if (model.protocol === KnownProviderEnum.google || model.protocol === KnownProviderEnum.googleVertex) {
422
+ applyGoogleThinkingMetadata(model);
423
+ if (/gemini-3(?:\.\d+)?-pro|gemini-3\.[78]-flash/.test(model.id.toLowerCase())) mergeThinkingLevelMap(model, {
424
+ off: "low",
425
+ minimal: null
426
+ });
427
+ }
428
+ if (model.id.includes("opus-4-6") || model.id.includes("opus-4.6")) mergeThinkingLevelMap(model, { xhigh: "max" });
429
+ if (model.id.includes("opus-4-7") || model.id.includes("opus-4.7")) mergeThinkingLevelMap(model, { xhigh: "xhigh" });
430
+ if (isOpenAiResponses(model)) applyOpenAiMetadata(model, source);
431
+ if (supportsOpenAiXhigh(model)) mergeThinkingLevelMap(model, { xhigh: "xhigh" });
432
+ if (isAnthropicMessages(model)) applyAnthropicMetadata(model);
433
+ }
434
+ const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
435
+ const OPENAI_CODEX_NPM = "@codeworksh/ai-sdk-openai-codex";
436
+ const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
437
+ const OPENAI_CODEX_CONTEXT = 272e3;
438
+ const OPENAI_CODEX_MODELS = [
439
+ {
440
+ id: "gpt-5.3-codex-spark",
441
+ contextWindow: 128e3
442
+ },
443
+ { id: "gpt-5.6-luna" },
444
+ { id: "gpt-5.6-sol" },
445
+ { id: "gpt-5.6-terra" },
446
+ { id: "gpt-6-astra" },
447
+ { id: "gpt-6-luna" },
448
+ { id: "gpt-6-sol" },
449
+ {
450
+ id: "gpt-6.1-sol",
451
+ contextWindow: 5e5
452
+ }
453
+ ];
454
+ const OPENAI_CODEX_ADDITIONAL_TOOLS_MODEL_IDS = /* @__PURE__ */ new Set([
455
+ "gpt-5.6-luna",
456
+ "gpt-5.6-sol",
457
+ "gpt-5.6-terra",
458
+ "gpt-6-astra",
459
+ "gpt-6-luna",
460
+ "gpt-6-sol",
461
+ "gpt-6.1-sol"
462
+ ]);
463
+ const OPENAI_CODEX_NONE_REASONING_MODEL_IDS = /* @__PURE__ */ new Set([
464
+ "gpt-5.6-luna",
465
+ "gpt-6-luna",
466
+ "gpt-6-sol"
467
+ ]);
468
+ /** The Codex models, from the generated `openai` entries; one models.dev does not list is skipped. */
469
+ function openAICodexBuiltInModels(openai) {
470
+ const models = {};
471
+ for (const seed of OPENAI_CODEX_MODELS) {
472
+ const base = openai[seed.id];
473
+ if (!base) continue;
474
+ const info = {
475
+ ...base,
476
+ provider: {
477
+ id: OPENAI_CODEX_PROVIDER_ID,
478
+ name: "OpenAI Codex (ChatGPT)",
479
+ source: "custom",
480
+ env: ["OPENAI_CODEX_API_KEY"]
481
+ },
482
+ baseUrl: OPENAI_CODEX_BASE_URL,
483
+ reasoning: true,
484
+ thinkingLevelMap: {
485
+ ...base.thinkingLevelMap,
486
+ off: OPENAI_CODEX_NONE_REASONING_MODEL_IDS.has(seed.id) ? "none" : null,
487
+ ...supportsOpenAiXhigh(base) ? { minimal: "low" } : {}
488
+ },
489
+ contextWindow: seed.contextWindow ?? OPENAI_CODEX_CONTEXT,
490
+ maxTokens: OPENAI_MAX_TOKENS,
491
+ npm: OPENAI_CODEX_NPM,
492
+ api: {
493
+ id: seed.id,
494
+ url: OPENAI_CODEX_BASE_URL,
495
+ method: APIMethodEnum.responses
496
+ },
497
+ providerOptionsKey: OPENAI_CODEX_PROVIDER_ID,
498
+ compat: {
499
+ supportsOpenAIGrammarTools: true,
500
+ ...OPENAI_CODEX_ADDITIONAL_TOOLS_MODEL_IDS.has(seed.id) ? {
501
+ supportsToolSearch: true,
502
+ supportsAdditionalTools: true
503
+ } : {}
504
+ },
505
+ protocol: KnownProviderEnum.openaiCodex
506
+ };
507
+ delete info.headers;
508
+ models[seed.id] = info;
509
+ }
510
+ return models;
511
+ }
512
+ const GITHUB_COPILOT_PROVIDER_ID = "github-copilot";
513
+ const GITHUB_COPILOT_NPM = "@codeworksh/ai-sdk-github-copilot";
514
+ const GITHUB_COPILOT_BASE_URL = "https://api.githubcopilot.com";
515
+ const GITHUB_COPILOT_EXTENDED_CONTEXT = 1e6;
516
+ const GITHUB_COPILOT_EXTENDED_CONTEXT_MODELS = /* @__PURE__ */ new Set([
517
+ "claude-fable-5",
518
+ "claude-opus-4.6",
519
+ "claude-opus-4.7",
520
+ "claude-opus-4.8",
521
+ "claude-opus-5",
522
+ "claude-sonnet-4.6",
523
+ "claude-sonnet-5",
524
+ "gpt-5.3-codex",
525
+ "gpt-5.4",
526
+ "gpt-5.5"
527
+ ]);
528
+ const GITHUB_COPILOT_THINKING_LEVEL_OVERRIDES = {
529
+ "claude-opus-4.7": { minimal: "low" },
530
+ "claude-opus-4.8": { minimal: "low" },
531
+ "claude-opus-5": { minimal: "low" },
532
+ "claude-sonnet-4.6": {
533
+ minimal: "low",
534
+ max: "max"
535
+ }
536
+ };
537
+ /**
538
+ * The offline routing approximation (pi). The live `/models` response's
539
+ * `supported_endpoints` is authoritative at runtime.
540
+ *
541
+ * - `claude-(haiku|sonnet|opus|fable)-[45]` → Anthropic Messages
542
+ * - `gpt-*`, `grok-*`, `oswe*`, `mai-*` → OpenAI Responses
543
+ * - everything else → OpenAI Chat Completions
544
+ */
545
+ function githubCopilotApiMethod(modelId) {
546
+ if (/^claude-(haiku|sonnet|opus|fable)-[45]([.-]|$)/.test(modelId)) return APIMethodEnum.messages;
547
+ const gpt = /^gpt-(\d+)/.exec(modelId);
548
+ if (gpt !== null && Number(gpt[1]) >= 5 || modelId.startsWith("grok-") || modelId.startsWith("oswe") || modelId.startsWith("mai-")) return APIMethodEnum.responses;
549
+ return APIMethodEnum.chat;
550
+ }
551
+ /**
552
+ * Derive the level map from models.dev `reasoning_options`: a level maps to
553
+ * itself only when the endpoint actually advertises it. `minimal` degrades to
554
+ * `low` — the lowest effort every reasoning model accepts.
555
+ */
556
+ function githubCopilotThinkingLevels(model, method) {
557
+ const listed = modelsDevEfforts(model);
558
+ const efforts = listed ? new Set(listed) : void 0;
559
+ const map = {};
560
+ if (method !== APIMethodEnum.messages) map.off = null;
561
+ if (!efforts) return Object.keys(map).length > 0 ? map : void 0;
562
+ if (efforts.has("minimal")) map.minimal = "minimal";
563
+ else if (efforts.has("low")) map.minimal = "low";
564
+ for (const level of [
565
+ "low",
566
+ "medium",
567
+ "high",
568
+ "xhigh",
569
+ "max"
570
+ ]) map[level] = efforts.has(level) ? level : null;
571
+ return map;
572
+ }
573
+ function githubCopilotBuiltInModels(provider) {
574
+ const models = {};
575
+ if (!provider) return models;
576
+ for (const [id, source] of Object.entries(provider.models)) {
577
+ if (source.tool_call !== true) continue;
578
+ if (source.status === "deprecated") continue;
579
+ if (id === "gpt-5-chat-latest") continue;
580
+ const base = applyModification(GITHUB_COPILOT_PROVIDER_ID, provider, source);
581
+ if (!base) continue;
582
+ const method = githubCopilotApiMethod(id);
583
+ const info = {
584
+ ...base,
585
+ provider: {
586
+ id: GITHUB_COPILOT_PROVIDER_ID,
587
+ name: "GitHub Copilot",
588
+ source: "custom",
589
+ env: ["COPILOT_GITHUB_TOKEN"]
590
+ },
591
+ baseUrl: GITHUB_COPILOT_BASE_URL,
592
+ headers: { ...GITHUB_COPILOT_STATIC_HEADERS },
593
+ npm: GITHUB_COPILOT_NPM,
594
+ api: {
595
+ id,
596
+ url: GITHUB_COPILOT_BASE_URL,
597
+ method
598
+ },
599
+ providerOptionsKey: GITHUB_COPILOT_PROVIDER_ID,
600
+ protocol: KnownProviderEnum.githubCopilot,
601
+ ...method === APIMethodEnum.responses ? { providerOptions: {
602
+ ...base.providerOptions,
603
+ [GITHUB_COPILOT_PROVIDER_ID]: {
604
+ store: false,
605
+ include: ["reasoning.encrypted_content"]
606
+ }
607
+ } } : {}
608
+ };
609
+ if (GITHUB_COPILOT_EXTENDED_CONTEXT_MODELS.has(id)) info.contextWindow = GITHUB_COPILOT_EXTENDED_CONTEXT;
610
+ if (method === APIMethodEnum.messages && isAnthropicAdaptiveThinkingModel(id)) mergeCompat(info, { forceAdaptiveThinking: true });
611
+ const gpt = /^gpt-(\d+)/.exec(id);
612
+ if (method === APIMethodEnum.responses && gpt !== null && Number(gpt[1]) >= 5) mergeCompat(info, { supportsOpenAIGrammarTools: true });
613
+ if (id.startsWith("gpt-5")) mergeThinkingLevelMap(info, {
614
+ off: null,
615
+ minimal: "low"
616
+ });
617
+ if (id.includes("fable-5")) mergeThinkingLevelMap(info, {
618
+ off: null,
619
+ xhigh: "xhigh",
620
+ max: "max"
621
+ });
622
+ const levels = githubCopilotThinkingLevels(source, method);
623
+ if (levels) mergeThinkingLevelMap(info, levels);
624
+ const override = GITHUB_COPILOT_THINKING_LEVEL_OVERRIDES[id];
625
+ if (override) mergeThinkingLevelMap(info, override);
626
+ models[id] = info;
627
+ }
628
+ return models;
629
+ }
630
+ /** OpenRouter's listing as catalog entries, or undefined when it is missing or unreadable. */
631
+ function openRouterModels(listing) {
632
+ if (listing === void 0) return void 0;
633
+ try {
634
+ const models = catalog(listing);
635
+ return Object.keys(models).length > 0 ? models : void 0;
636
+ } catch {
637
+ return;
638
+ }
639
+ }
640
+ /** Object keys in order at every level, so identical content serializes to identical bytes. */
641
+ function sortKeys(value) {
642
+ if (Array.isArray(value)) return value.map(sortKeys);
643
+ if (typeof value !== "object" || value === null) return value;
644
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => [key, sortKeys(entry)]));
645
+ }
646
+ /**
647
+ * The catalog from fetched sources, keys sorted so identical content serializes to identical
648
+ * bytes. OpenRouter's own listing replaces models.dev's copy of it.
649
+ */
650
+ function buildCatalog(sources) {
651
+ const modelsDev = loadBuiltInFromModelsDev(sources.modelsDev);
652
+ const openrouter = openRouterModels(sources.openRouter);
653
+ const copilotProvider = sources.modelsDev[GITHUB_COPILOT_PROVIDER_ID];
654
+ return sortKeys({
655
+ ...modelsDev,
656
+ ...openrouter ? { [PROVIDER_ID]: openrouter } : {},
657
+ [OPENAI_CODEX_PROVIDER_ID]: openAICodexBuiltInModels(modelsDev.openai ?? {}),
658
+ ...copilotProvider ? { [GITHUB_COPILOT_PROVIDER_ID]: githubCopilotBuiltInModels(copilotProvider) } : {}
659
+ });
660
+ }
661
+ async function generateModels(args = {}) {
662
+ const path$1 = resolve(args.path ?? path());
663
+ const [modelsDev, openRouter] = await Promise.all([pullModelsDevData(), pullOpenRouterData()]);
664
+ const catalog = buildCatalog({
665
+ modelsDev,
666
+ openRouter
667
+ });
668
+ await writeJsonAtomic(path$1, args.baseline ? pick(catalog, BASELINE_PROVIDERS) : catalog);
669
+ return path$1;
670
+ }
671
+ const ModelgenCommand = {
672
+ command: "modelgen [path]",
673
+ describe: "generate models.gen.json",
674
+ builder: (yargs) => yargs.positional("path", {
675
+ type: "string",
676
+ describe: "output path (defaults to CODEWORK_MODELS_FILE or ./models.gen.json)"
677
+ }).option("baseline", {
678
+ type: "boolean",
679
+ describe: `keep only the bundled baseline providers (${BASELINE_PROVIDERS.join(", ")})`
680
+ }),
681
+ handler: async (args) => {
682
+ const path = await generateModels(args);
683
+ console.log(`Generated model catalog at ${path}`);
684
+ }
685
+ };
686
+ function applyModification(providerId, provider, model) {
687
+ const npm = resolveModelNpm(provider, model);
688
+ if (!isAISDKPackage(npm)) return;
689
+ const protocol = protocolForPackage(npm);
690
+ const baseUrl = resolveModelBaseUrl(provider, model);
691
+ const providerInfo = {
692
+ id: providerId,
693
+ name: provider.name,
694
+ source: "api",
695
+ env: provider.env
696
+ };
697
+ if (provider.key) providerInfo.key = provider.key;
698
+ const api = {
699
+ id: model.id,
700
+ method: resolveModelAPIMethod(npm)
701
+ };
702
+ if (baseUrl) api.url = baseUrl;
703
+ const info = {
704
+ id: model.id,
705
+ name: model.name,
706
+ provider: providerInfo,
707
+ baseUrl,
708
+ reasoning: Boolean(model.reasoning),
709
+ input: normalizeInput(model.modalities.input),
710
+ cost: modelsDevCost(model.cost),
711
+ contextWindow: model.limit?.context ?? 4096,
712
+ maxTokens: model.limit?.output ?? 4096,
713
+ headers: {
714
+ ...provider.headers,
715
+ ...model.provider?.headers,
716
+ ...model.headers
717
+ },
718
+ npm,
719
+ api,
720
+ providerOptionsKey: resolveProviderOptionsKey(protocol),
721
+ protocol
722
+ };
723
+ if (Object.keys(info.headers ?? {}).length === 0) delete info.headers;
724
+ applyModelMetadata(info, model);
725
+ return info;
726
+ }
727
+ //#endregion
728
+ export { personalize as i, generateModels as n, PROVIDER_ID as r, ModelgenCommand as t };
729
+
730
+ //# sourceMappingURL=modelgen-BdiCqi-R.mjs.map