@x12i/ai-tools 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +146 -204
  3. package/dist/AiModelsCatalogClient-B5FMI9gj.d.cts +58 -0
  4. package/dist/AiModelsCatalogClient-CPPNI6Ry.d.ts +58 -0
  5. package/dist/aliases/index.cjs +4 -3
  6. package/dist/aliases/index.cjs.map +1 -1
  7. package/dist/aliases/index.d.cts +3 -5
  8. package/dist/aliases/index.d.ts +3 -5
  9. package/dist/aliases/index.js +2 -2
  10. package/dist/catalog/index.cjs +18 -9
  11. package/dist/catalog/index.cjs.map +1 -1
  12. package/dist/catalog/index.d.cts +13 -100
  13. package/dist/catalog/index.d.ts +13 -100
  14. package/dist/catalog/index.js +31 -23
  15. package/dist/{chunk-AJEKEWWB.js → chunk-2PTCWPHV.js} +17 -3
  16. package/dist/chunk-2PTCWPHV.js.map +1 -0
  17. package/dist/chunk-56R4XA2S.js +1 -0
  18. package/dist/chunk-5GUKLOEK.cjs +1 -0
  19. package/dist/chunk-5GUKLOEK.cjs.map +1 -0
  20. package/dist/chunk-5XAAMBDO.cjs +1988 -0
  21. package/dist/chunk-5XAAMBDO.cjs.map +1 -0
  22. package/dist/chunk-6BQBKROR.js +95 -0
  23. package/dist/chunk-6BQBKROR.js.map +1 -0
  24. package/dist/chunk-AB5GNXJ4.js +46 -0
  25. package/dist/chunk-AB5GNXJ4.js.map +1 -0
  26. package/dist/{chunk-O2A6OVEH.js → chunk-ANVONYJF.js} +2 -2
  27. package/dist/{chunk-O2A6OVEH.js.map → chunk-ANVONYJF.js.map} +1 -1
  28. package/dist/chunk-B3V2EHRY.js +225 -0
  29. package/dist/chunk-B3V2EHRY.js.map +1 -0
  30. package/dist/{chunk-QWAX7VQO.cjs → chunk-BAHBDADJ.cjs} +11 -11
  31. package/dist/{chunk-QWAX7VQO.cjs.map → chunk-BAHBDADJ.cjs.map} +1 -1
  32. package/dist/{chunk-TF4L2NEC.cjs → chunk-DXZOL3VN.cjs} +62 -313
  33. package/dist/chunk-DXZOL3VN.cjs.map +1 -0
  34. package/dist/chunk-EDMCKHO6.cjs +225 -0
  35. package/dist/chunk-EDMCKHO6.cjs.map +1 -0
  36. package/dist/{chunk-DJ5SWJDY.js → chunk-EYHMQVAL.js} +48 -299
  37. package/dist/chunk-EYHMQVAL.js.map +1 -0
  38. package/dist/chunk-GS7T56RP.cjs +8 -0
  39. package/dist/chunk-GS7T56RP.cjs.map +1 -0
  40. package/dist/chunk-NF2SKQR7.cjs +973 -0
  41. package/dist/chunk-NF2SKQR7.cjs.map +1 -0
  42. package/dist/chunk-OPN6BGNH.js +1985 -0
  43. package/dist/chunk-OPN6BGNH.js.map +1 -0
  44. package/dist/{chunk-7Q742NI3.cjs → chunk-PADNCGZB.cjs} +17 -3
  45. package/dist/chunk-PADNCGZB.cjs.map +1 -0
  46. package/dist/chunk-PRCICORG.cjs +95 -0
  47. package/dist/chunk-PRCICORG.cjs.map +1 -0
  48. package/dist/{chunk-6QGDZTGH.js → chunk-SIH4GPV4.js} +4 -29
  49. package/dist/chunk-SIH4GPV4.js.map +1 -0
  50. package/dist/chunk-U2YDDUVP.js +970 -0
  51. package/dist/chunk-U2YDDUVP.js.map +1 -0
  52. package/dist/{chunk-4NAY6HRP.js → chunk-VJHLO2R3.js} +7 -58
  53. package/dist/chunk-VJHLO2R3.js.map +1 -0
  54. package/dist/chunk-XAWBTX3N.cjs +46 -0
  55. package/dist/chunk-XAWBTX3N.cjs.map +1 -0
  56. package/dist/{chunk-AV6OE2YQ.cjs → chunk-XOKUDUUI.cjs} +14 -39
  57. package/dist/chunk-XOKUDUUI.cjs.map +1 -0
  58. package/dist/chunk-YQDSN6R6.cjs +86 -0
  59. package/dist/chunk-YQDSN6R6.cjs.map +1 -0
  60. package/dist/cli/index.cjs +59 -201
  61. package/dist/cli/index.cjs.map +1 -1
  62. package/dist/cli/index.js +53 -198
  63. package/dist/cli/index.js.map +1 -1
  64. package/dist/cost/index.cjs +19 -3
  65. package/dist/cost/index.cjs.map +1 -1
  66. package/dist/cost/index.d.cts +10 -50
  67. package/dist/cost/index.d.ts +10 -50
  68. package/dist/cost/index.js +18 -3
  69. package/dist/index.cjs +24 -16
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +13 -13
  72. package/dist/index.d.ts +13 -13
  73. package/dist/index.js +44 -37
  74. package/dist/modelCache-BzRn6t_C.d.ts +113 -0
  75. package/dist/modelCache-CJftI-Ko.d.cts +113 -0
  76. package/dist/{modelNameResolver-DqFt7g6W.d.ts → modelNameResolver-5XkBMctP.d.ts} +2 -6
  77. package/dist/{modelNameResolver-D9V_GfUK.d.cts → modelNameResolver-C5CSTGFF.d.cts} +2 -6
  78. package/dist/models/index.cjs +9 -6
  79. package/dist/models/index.cjs.map +1 -1
  80. package/dist/models/index.d.cts +9 -31
  81. package/dist/models/index.d.ts +9 -31
  82. package/dist/models/index.js +9 -7
  83. package/dist/resolveUsageModel-BFwf80Hz.d.ts +140 -0
  84. package/dist/resolveUsageModel-C_YmGR1M.d.cts +140 -0
  85. package/dist/sync/index.cjs +7 -9
  86. package/dist/sync/index.cjs.map +1 -1
  87. package/dist/sync/index.d.cts +3 -8
  88. package/dist/sync/index.d.ts +3 -8
  89. package/dist/sync/index.js +8 -11
  90. package/dist/toolbox/index.cjs +1 -0
  91. package/dist/toolbox/index.cjs.map +1 -1
  92. package/dist/types-BrzJWsTU.d.cts +277 -0
  93. package/dist/types-BrzJWsTU.d.ts +277 -0
  94. package/package.json +9 -20
  95. package/src/data/models-catalog.json +670 -0
  96. package/src/data/openrouter-models-catalog.json +857 -0
  97. package/dist/AiModelsCatalogClient-4RF5BCDL.cjs +0 -9
  98. package/dist/AiModelsCatalogClient-4RF5BCDL.cjs.map +0 -1
  99. package/dist/AiModelsCatalogClient-CNeqFiFs.d.cts +0 -30
  100. package/dist/AiModelsCatalogClient-NUF3CBLW.js +0 -9
  101. package/dist/AiModelsCatalogClient-nwFoEaqL.d.ts +0 -30
  102. package/dist/catalox/index.cjs +0 -21
  103. package/dist/catalox/index.cjs.map +0 -1
  104. package/dist/catalox/index.d.cts +0 -11
  105. package/dist/catalox/index.d.ts +0 -11
  106. package/dist/catalox/index.js +0 -21
  107. package/dist/catalox/index.js.map +0 -1
  108. package/dist/chunk-4NAY6HRP.js.map +0 -1
  109. package/dist/chunk-6QGDZTGH.js.map +0 -1
  110. package/dist/chunk-7Q742NI3.cjs.map +0 -1
  111. package/dist/chunk-AJEKEWWB.js.map +0 -1
  112. package/dist/chunk-AV6OE2YQ.cjs.map +0 -1
  113. package/dist/chunk-C3H7RTFR.cjs +0 -1
  114. package/dist/chunk-C3H7RTFR.cjs.map +0 -1
  115. package/dist/chunk-DJ5SWJDY.js.map +0 -1
  116. package/dist/chunk-DKHGWHXP.cjs +0 -169
  117. package/dist/chunk-DKHGWHXP.cjs.map +0 -1
  118. package/dist/chunk-F2F4UEFD.cjs +0 -75
  119. package/dist/chunk-F2F4UEFD.cjs.map +0 -1
  120. package/dist/chunk-FGP3QXWL.cjs +0 -163
  121. package/dist/chunk-FGP3QXWL.cjs.map +0 -1
  122. package/dist/chunk-G2G4KSC5.js +0 -30
  123. package/dist/chunk-G2G4KSC5.js.map +0 -1
  124. package/dist/chunk-HN6UAQAE.cjs +0 -83
  125. package/dist/chunk-HN6UAQAE.cjs.map +0 -1
  126. package/dist/chunk-HS74X2OJ.cjs +0 -172
  127. package/dist/chunk-HS74X2OJ.cjs.map +0 -1
  128. package/dist/chunk-HYGXZY25.js +0 -163
  129. package/dist/chunk-HYGXZY25.js.map +0 -1
  130. package/dist/chunk-KQOALKKX.js +0 -75
  131. package/dist/chunk-KQOALKKX.js.map +0 -1
  132. package/dist/chunk-LYOU7CA2.cjs +0 -30
  133. package/dist/chunk-LYOU7CA2.cjs.map +0 -1
  134. package/dist/chunk-M5TMA73F.js +0 -1
  135. package/dist/chunk-M5TMA73F.js.map +0 -1
  136. package/dist/chunk-MX3AMQFC.js +0 -172
  137. package/dist/chunk-MX3AMQFC.js.map +0 -1
  138. package/dist/chunk-QCRLKVB3.cjs +0 -137
  139. package/dist/chunk-QCRLKVB3.cjs.map +0 -1
  140. package/dist/chunk-TF4L2NEC.cjs.map +0 -1
  141. package/dist/chunk-VRFVF5RH.js +0 -169
  142. package/dist/chunk-VRFVF5RH.js.map +0 -1
  143. package/dist/chunk-YHO57D2V.js +0 -83
  144. package/dist/chunk-YHO57D2V.js.map +0 -1
  145. package/dist/syncAiModelsCatalog-CnXRLm2c.d.cts +0 -32
  146. package/dist/syncAiModelsCatalog-DpkN_w7S.d.ts +0 -32
  147. package/dist/types-BYXnCvKx.d.cts +0 -137
  148. package/dist/types-BYXnCvKx.d.ts +0 -137
  149. package/dist/types-CX6QFNNy.d.cts +0 -144
  150. package/dist/types-CuiPDcVs.d.ts +0 -144
  151. package/dist/upsertAiModelRecord-C831wOIF.d.ts +0 -35
  152. package/dist/upsertAiModelRecord-CjY-sny0.d.cts +0 -35
  153. /package/dist/{AiModelsCatalogClient-NUF3CBLW.js.map → chunk-56R4XA2S.js.map} +0 -0
@@ -0,0 +1,1985 @@
1
+ import {
2
+ ModelNameResolver,
3
+ computeSupportsReasoning
4
+ } from "./chunk-EYHMQVAL.js";
5
+ import {
6
+ normalizeProvider,
7
+ normalizeString
8
+ } from "./chunk-B3V2EHRY.js";
9
+
10
+ // src/cache/modelCache.ts
11
+ import { createNxCache } from "nx-cache";
12
+ var cache = createNxCache();
13
+ var DEFAULT_CATALOG_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
14
+ function resolveCatalogCacheTtlMs(override) {
15
+ if (override !== void 0) return override;
16
+ const raw = process.env.AI_TOOLS_CACHE_TTL_MS;
17
+ if (!raw) return DEFAULT_CATALOG_CACHE_TTL_MS;
18
+ const n = Number.parseInt(raw, 10);
19
+ return Number.isFinite(n) && n > 0 ? n : DEFAULT_CATALOG_CACHE_TTL_MS;
20
+ }
21
+ function writeCachedModels(scopeKey, models, ttlMs = DEFAULT_CATALOG_CACHE_TTL_MS) {
22
+ cache.write("ai-models", models, {
23
+ scope: { appId: scopeKey },
24
+ ttlMs,
25
+ metadata: { cachedAt: (/* @__PURE__ */ new Date()).toISOString(), count: models.size }
26
+ });
27
+ }
28
+
29
+ // src/catalog/catalogSources.ts
30
+ var DEFAULT_DIRECT_CATALOG_URL = "https://open-assets.x12i.com/models-catalog.json";
31
+ var DEFAULT_OPENROUTER_CATALOG_URL = "https://open-assets.x12i.com/openrouter-models-catalog.json";
32
+
33
+ // src/catalog/normalizeX12iCatalogModel.ts
34
+ var TOKENS_PER_UNIT = 1e6;
35
+ function usdPerToken(perMillion) {
36
+ if (perMillion == null || !Number.isFinite(perMillion)) return 0;
37
+ return perMillion / TOKENS_PER_UNIT;
38
+ }
39
+ function firstRate(raw, ...keys) {
40
+ for (const key of keys) {
41
+ const v = raw[key];
42
+ if (typeof v === "number" && Number.isFinite(v)) return v;
43
+ }
44
+ return void 0;
45
+ }
46
+ function canonicalCatalogModelId(provider, modelId) {
47
+ const m = normalizeString(modelId);
48
+ if (m.includes("/")) return m;
49
+ const p = normalizeProvider(provider) ?? normalizeString(provider);
50
+ return `${p}/${m}`;
51
+ }
52
+ function mapStatus(status) {
53
+ if (!status) return "active";
54
+ const s = status.toLowerCase();
55
+ if (s === "deprecated" || s === "legacy") return "deprecated";
56
+ if (s === "active" || s === "preview" || s === "announced") return "active";
57
+ return "unknown";
58
+ }
59
+ function mapModalities(modalities) {
60
+ if (Array.isArray(modalities)) {
61
+ return {
62
+ input: modalities,
63
+ output: modalities.includes("text") ? ["text"] : modalities.slice(0, 1),
64
+ modality: modalities.join("+")
65
+ };
66
+ }
67
+ if (modalities && typeof modalities === "object") {
68
+ const input = modalities.input ?? ["text"];
69
+ const output = modalities.output ?? ["text"];
70
+ return { input, output, modality: `${input.join("+")}->${output.join("+")}` };
71
+ }
72
+ return { input: ["text"], output: ["text"], modality: "text->text" };
73
+ }
74
+ function pricingToOpenRouterApi(raw, perMillion = true) {
75
+ const scale = perMillion ? (n) => (n ?? 0) / TOKENS_PER_UNIT : (n) => n ?? 0;
76
+ return {
77
+ prompt: String(scale(firstRate(raw, "input", "textInput"))),
78
+ completion: String(scale(firstRate(raw, "output", "textOutput"))),
79
+ request: "0",
80
+ image: String(scale(firstRate(raw, "imageInput"))),
81
+ input_cache_read: raw.cachedInput != null ? String(scale(raw.cachedInput)) : void 0,
82
+ input_cache_write: raw.cacheWrite != null ? String(scale(raw.cacheWrite)) : void 0,
83
+ internal_reasoning: raw.internalReasoning != null ? String(scale(raw.internalReasoning)) : void 0,
84
+ web_search: raw.webSearchPerRequest != null ? String(raw.webSearchPerRequest) : void 0
85
+ };
86
+ }
87
+ function pricingFromX12i(raw, pricedAt, source) {
88
+ return {
89
+ promptUsdPerToken: usdPerToken(firstRate(raw, "input", "textInput")),
90
+ completionUsdPerToken: usdPerToken(firstRate(raw, "output", "textOutput")),
91
+ imageUsdPerUnit: usdPerToken(firstRate(raw, "imageInput")),
92
+ requestUsdPerRequest: usdPerToken(
93
+ typeof raw.webSearchPerRequest === "number" ? raw.webSearchPerRequest * 1e3 : 0
94
+ ),
95
+ cacheReadUsdPerToken: usdPerToken(
96
+ firstRate(raw, "cachedInput", "cacheHit")
97
+ ),
98
+ cacheWriteUsdPerToken: usdPerToken(
99
+ firstRate(raw, "cacheWrite", "cacheWrite5m", "cacheWrite1h")
100
+ ),
101
+ reasoningUsdPerToken: usdPerToken(raw.internalReasoning),
102
+ webSearchUsdPerRequest: typeof raw.webSearchPerRequest === "number" ? raw.webSearchPerRequest : void 0,
103
+ pricedAt,
104
+ source
105
+ };
106
+ }
107
+ function stubOpenRouterApi(modelId, entry, modalities, pricing) {
108
+ return {
109
+ id: modelId,
110
+ canonical_slug: entry.canonicalSlug ?? modelId,
111
+ name: entry.displayName ?? modelId,
112
+ created: 0,
113
+ description: entry.displayName ?? "",
114
+ context_length: entry.contextWindow ?? 0,
115
+ architecture: {
116
+ modality: modalities.modality,
117
+ input_modalities: modalities.input,
118
+ output_modalities: modalities.output,
119
+ tokenizer: "",
120
+ instruct_type: null
121
+ },
122
+ pricing,
123
+ top_provider: {
124
+ context_length: entry.contextWindow ?? 0,
125
+ max_completion_tokens: entry.maxCompletionTokens ?? null,
126
+ is_moderated: false
127
+ },
128
+ per_request_limits: null,
129
+ supported_parameters: entry.capabilities?.toolCalling ? ["tools"] : [],
130
+ default_parameters: null
131
+ };
132
+ }
133
+ function normalizeX12iCatalogModel(entry, kind, verifiedAt) {
134
+ const pricingRaw = entry.pricing;
135
+ if (!pricingRaw) return null;
136
+ const inputRate = firstRate(pricingRaw, "input", "textInput", "inputBelow200k");
137
+ const outputRate = firstRate(pricingRaw, "output", "textOutput", "outputBelow200k");
138
+ if (inputRate == null && outputRate == null) return null;
139
+ const modelId = canonicalCatalogModelId(entry.provider, entry.modelId);
140
+ const providerId = kind === "openrouter" && modelId.includes("/") ? modelId.split("/")[0] : normalizeProvider(entry.provider) ?? entry.provider;
141
+ const modalities = mapModalities(entry.modalities);
142
+ const pricedAt = verifiedAt || (/* @__PURE__ */ new Date()).toISOString();
143
+ const pricingSource = kind === "openrouter" ? "openrouter" : "direct";
144
+ const pricing = pricingFromX12i(pricingRaw, pricedAt, pricingSource);
145
+ const openRouterPricing = pricingToOpenRouterApi(pricingRaw, true);
146
+ const openRouter = stubOpenRouterApi(modelId, entry, modalities, openRouterPricing);
147
+ const supportedParameters = openRouter.supported_parameters ?? [];
148
+ const supportsReasoning = entry.capabilities?.reasoning === true || computeSupportsReasoning({
149
+ supportedParameters,
150
+ pricing,
151
+ openRouterPricing
152
+ });
153
+ const aliases = new Set(entry.aliases ?? []);
154
+ aliases.add(entry.modelId);
155
+ if (entry.displayName) aliases.add(entry.displayName);
156
+ const slash = modelId.indexOf("/");
157
+ if (slash > 0) aliases.add(modelId.slice(slash + 1));
158
+ return {
159
+ modelId,
160
+ name: entry.displayName ?? modelId,
161
+ providerId,
162
+ canonicalSlug: entry.canonicalSlug ?? modelId,
163
+ status: mapStatus(entry.status),
164
+ description: entry.displayName ?? "",
165
+ created: 0,
166
+ expirationDate: null,
167
+ contextLength: entry.contextWindow ?? 0,
168
+ maxCompletionTokens: entry.maxCompletionTokens ?? null,
169
+ isModerated: false,
170
+ modality: modalities.modality,
171
+ inputModalities: modalities.input,
172
+ outputModalities: modalities.output,
173
+ tokenizer: "",
174
+ instructType: null,
175
+ supportedParameters,
176
+ defaultParameters: null,
177
+ perRequestLimits: null,
178
+ pricing,
179
+ openRouterPricing,
180
+ architecture: openRouter.architecture,
181
+ topProvider: openRouter.top_provider,
182
+ openRouter,
183
+ aliases: [...aliases],
184
+ availableOnOpenRouter: kind === "openrouter",
185
+ supportsStreaming: modalities.output.includes("text"),
186
+ supportsTools: entry.capabilities?.toolCalling === true,
187
+ supportsReasoning,
188
+ primaryOutputModality: modalities.output[0] ?? "text",
189
+ syncedAt: pricedAt,
190
+ syncSource: kind === "openrouter" ? "openrouter" : "manual"
191
+ };
192
+ }
193
+ function modelsFromX12iCatalogFile(file, kind) {
194
+ const map = /* @__PURE__ */ new Map();
195
+ const verifiedAt = file.verifiedAt ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
196
+ for (const entry of file.models) {
197
+ const record = normalizeX12iCatalogModel(entry, kind, verifiedAt);
198
+ if (record) map.set(record.modelId, record);
199
+ }
200
+ return map;
201
+ }
202
+
203
+ // src/catalog/loadCatalogSources.ts
204
+ import { readFile } from "fs/promises";
205
+ import { dirname, join } from "path";
206
+ import { fileURLToPath } from "url";
207
+
208
+ // src/data/models-catalog.json
209
+ var models_catalog_default = {
210
+ schema: "x12i.ai-models.catalog",
211
+ version: "0.1.0",
212
+ currency: "USD",
213
+ pricingUnit: "1M_tokens",
214
+ verifiedAt: "2026-05-27",
215
+ notes: [
216
+ "All token prices are listed per 1M tokens unless otherwise stated.",
217
+ "Null means pricing was not found or not public in the checked official source.",
218
+ "Some providers price audio, image, video, search, batch, cache, and regional processing separately.",
219
+ "This catalog intentionally does not include OpenRouter pricing."
220
+ ],
221
+ models: [
222
+ {
223
+ provider: "openai",
224
+ modelId: "gpt-5.5",
225
+ displayName: "GPT-5.5",
226
+ family: "gpt",
227
+ status: "active",
228
+ modalities: ["text", "vision"],
229
+ contextWindow: 1e6,
230
+ pricing: {
231
+ input: 5,
232
+ cachedInput: 0.5,
233
+ output: 30,
234
+ batchDiscount: "50%",
235
+ priorityMultiplier: 2.5,
236
+ dataResidencyMultiplier: 1.1
237
+ },
238
+ sourceUrl: "https://openai.com/api/pricing/"
239
+ },
240
+ {
241
+ provider: "openai",
242
+ modelId: "gpt-5.5-pro",
243
+ displayName: "GPT-5.5 Pro",
244
+ family: "gpt",
245
+ status: "announced",
246
+ modalities: ["text", "vision"],
247
+ contextWindow: 1e6,
248
+ pricing: {
249
+ input: 30,
250
+ output: 180
251
+ },
252
+ sourceUrl: "https://openai.com/index/introducing-gpt-5-5/"
253
+ },
254
+ {
255
+ provider: "openai",
256
+ modelId: "gpt-5.4",
257
+ displayName: "GPT-5.4",
258
+ family: "gpt",
259
+ status: "active",
260
+ modalities: ["text", "vision"],
261
+ pricing: {
262
+ input: 2.5,
263
+ cachedInput: 0.25,
264
+ output: 15,
265
+ batchDiscount: "50%",
266
+ dataResidencyMultiplier: 1.1
267
+ },
268
+ sourceUrl: "https://openai.com/api/pricing/"
269
+ },
270
+ {
271
+ provider: "openai",
272
+ modelId: "gpt-5.4-mini",
273
+ displayName: "GPT-5.4 Mini",
274
+ family: "gpt",
275
+ status: "active",
276
+ modalities: ["text", "vision"],
277
+ pricing: {
278
+ input: 0.75,
279
+ cachedInput: 0.075,
280
+ output: 4.5,
281
+ batchDiscount: "50%",
282
+ dataResidencyMultiplier: 1.1
283
+ },
284
+ sourceUrl: "https://openai.com/api/pricing/"
285
+ },
286
+ {
287
+ provider: "openai",
288
+ modelId: "gpt-realtime-2",
289
+ displayName: "GPT Realtime 2",
290
+ family: "gpt-realtime",
291
+ status: "active",
292
+ modalities: ["text", "audio", "image"],
293
+ pricing: {
294
+ textInput: 4,
295
+ textCachedInput: 0.4,
296
+ textOutput: 24,
297
+ audioInput: 32,
298
+ audioCachedInput: 0.4,
299
+ audioOutput: 64,
300
+ imageInput: 5,
301
+ imageCachedInput: 0.5
302
+ },
303
+ sourceUrl: "https://openai.com/api/pricing/"
304
+ },
305
+ {
306
+ provider: "openai",
307
+ modelId: "gpt-image-2",
308
+ displayName: "GPT Image 2",
309
+ family: "gpt-image",
310
+ status: "active",
311
+ modalities: ["image", "text"],
312
+ pricing: {
313
+ imageInput: 8,
314
+ imageCachedInput: 2,
315
+ imageOutput: 30,
316
+ textInput: 5,
317
+ textCachedInput: 1.25
318
+ },
319
+ sourceUrl: "https://openai.com/api/pricing/"
320
+ },
321
+ {
322
+ provider: "anthropic",
323
+ modelId: "claude-opus-4-7",
324
+ displayName: "Claude Opus 4.7",
325
+ family: "claude",
326
+ status: "active",
327
+ modalities: ["text", "vision"],
328
+ contextWindow: 1e6,
329
+ pricing: {
330
+ input: 5,
331
+ cacheWrite5m: 6.25,
332
+ cacheWrite1h: 10,
333
+ cacheHit: 0.5,
334
+ output: 25,
335
+ batchInput: 2.5,
336
+ batchOutput: 12.5,
337
+ fastModeInput: 30,
338
+ fastModeOutput: 150
339
+ },
340
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing"
341
+ },
342
+ {
343
+ provider: "anthropic",
344
+ modelId: "claude-opus-4-6",
345
+ displayName: "Claude Opus 4.6",
346
+ family: "claude",
347
+ status: "active",
348
+ modalities: ["text", "vision"],
349
+ contextWindow: 1e6,
350
+ pricing: {
351
+ input: 5,
352
+ cacheWrite5m: 6.25,
353
+ cacheWrite1h: 10,
354
+ cacheHit: 0.5,
355
+ output: 25,
356
+ batchInput: 2.5,
357
+ batchOutput: 12.5,
358
+ fastModeInput: 30,
359
+ fastModeOutput: 150
360
+ },
361
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing"
362
+ },
363
+ {
364
+ provider: "anthropic",
365
+ modelId: "claude-opus-4-5",
366
+ displayName: "Claude Opus 4.5",
367
+ family: "claude",
368
+ status: "active",
369
+ modalities: ["text", "vision"],
370
+ pricing: {
371
+ input: 5,
372
+ cacheWrite5m: 6.25,
373
+ cacheWrite1h: 10,
374
+ cacheHit: 0.5,
375
+ output: 25,
376
+ batchInput: 2.5,
377
+ batchOutput: 12.5
378
+ },
379
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing"
380
+ },
381
+ {
382
+ provider: "anthropic",
383
+ modelId: "claude-sonnet-4-6",
384
+ displayName: "Claude Sonnet 4.6",
385
+ family: "claude",
386
+ status: "active",
387
+ modalities: ["text", "vision"],
388
+ contextWindow: 1e6,
389
+ pricing: {
390
+ input: 3,
391
+ cacheWrite5m: 3.75,
392
+ cacheWrite1h: 6,
393
+ cacheHit: 0.3,
394
+ output: 15,
395
+ batchInput: 1.5,
396
+ batchOutput: 7.5
397
+ },
398
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing"
399
+ },
400
+ {
401
+ provider: "anthropic",
402
+ modelId: "claude-sonnet-4-5",
403
+ displayName: "Claude Sonnet 4.5",
404
+ family: "claude",
405
+ status: "active",
406
+ modalities: ["text", "vision"],
407
+ pricing: {
408
+ input: 3,
409
+ cacheWrite5m: 3.75,
410
+ cacheWrite1h: 6,
411
+ cacheHit: 0.3,
412
+ output: 15,
413
+ batchInput: 1.5,
414
+ batchOutput: 7.5
415
+ },
416
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing"
417
+ },
418
+ {
419
+ provider: "anthropic",
420
+ modelId: "claude-haiku-4-5",
421
+ displayName: "Claude Haiku 4.5",
422
+ family: "claude",
423
+ status: "active",
424
+ modalities: ["text", "vision"],
425
+ pricing: {
426
+ input: 1,
427
+ cacheWrite5m: 1.25,
428
+ cacheWrite1h: 2,
429
+ cacheHit: 0.1,
430
+ output: 5,
431
+ batchInput: 0.5,
432
+ batchOutput: 2.5
433
+ },
434
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing"
435
+ },
436
+ {
437
+ provider: "google",
438
+ modelId: "gemini-3.5-flash",
439
+ displayName: "Gemini 3.5 Flash",
440
+ family: "gemini",
441
+ status: "active",
442
+ modalities: ["text", "image", "video", "audio"],
443
+ pricing: {
444
+ input: 0.25,
445
+ audioInput: 0.5,
446
+ output: 1.5,
447
+ cachedInput: 0.025,
448
+ audioCachedInput: 0.05,
449
+ cacheStoragePerHour: 1,
450
+ batchInput: 0.125,
451
+ batchAudioInput: 0.25,
452
+ batchOutput: 0.75,
453
+ googleSearchAfterFreePer1kQueries: 14,
454
+ googleMapsAfterFreePer1kQueries: 14
455
+ },
456
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
457
+ },
458
+ {
459
+ provider: "google",
460
+ modelId: "gemini-3.1-pro-preview",
461
+ displayName: "Gemini 3.1 Pro Preview",
462
+ family: "gemini",
463
+ status: "preview",
464
+ modalities: ["text", "image", "video", "audio"],
465
+ pricing: {
466
+ input: 0.45,
467
+ audioInput: 0.9,
468
+ output: 2.7,
469
+ cachedInput: 0.045,
470
+ audioCachedInput: 0.09,
471
+ cacheStoragePerHour: 1.8,
472
+ googleSearchAfterFreePer1kQueries: 14,
473
+ googleMapsAfterFreePer1kQueries: 14
474
+ },
475
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
476
+ },
477
+ {
478
+ provider: "google",
479
+ modelId: "gemini-3.1-flash-lite-preview",
480
+ displayName: "Gemini 3.1 Flash-Lite Preview",
481
+ family: "gemini",
482
+ status: "preview",
483
+ modalities: ["text", "image", "video", "audio"],
484
+ pricing: {
485
+ input: 0.25,
486
+ audioInput: 0.5,
487
+ output: 1.5,
488
+ cachedInput: 0.025,
489
+ audioCachedInput: 0.05,
490
+ cacheStoragePerHour: 1,
491
+ batchInput: 0.125,
492
+ batchAudioInput: 0.25,
493
+ batchOutput: 0.75,
494
+ googleSearchAfterFreePer1kQueries: 14
495
+ },
496
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
497
+ },
498
+ {
499
+ provider: "google",
500
+ modelId: "gemini-2.5-flash",
501
+ displayName: "Gemini 2.5 Flash",
502
+ family: "gemini",
503
+ status: "active",
504
+ modalities: ["text", "image", "video", "audio"],
505
+ pricing: {
506
+ input: 0.1,
507
+ audioInput: 0.7,
508
+ output: 0.4,
509
+ cachedInput: 0.025,
510
+ audioCachedInput: 0.175,
511
+ cacheStoragePerHour: 1
512
+ },
513
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
514
+ },
515
+ {
516
+ provider: "google",
517
+ modelId: "gemini-2.5-flash-lite",
518
+ displayName: "Gemini 2.5 Flash-Lite",
519
+ family: "gemini",
520
+ status: "active",
521
+ modalities: ["text", "image", "video", "audio"],
522
+ pricing: {
523
+ input: 0.05,
524
+ audioInput: 0.35,
525
+ output: 0.2,
526
+ cachedInput: 0.025,
527
+ audioCachedInput: 0.175,
528
+ cacheStoragePerHour: 1
529
+ },
530
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
531
+ },
532
+ {
533
+ provider: "google",
534
+ modelId: "gemini-2.5-computer-use-preview-10-2025",
535
+ displayName: "Gemini 2.5 Computer Use Preview",
536
+ family: "gemini",
537
+ status: "preview",
538
+ modalities: ["text", "image", "browser-control"],
539
+ pricing: {
540
+ inputBelow200k: 1.25,
541
+ inputAbove200k: 2.5,
542
+ outputBelow200k: 10,
543
+ outputAbove200k: 15
544
+ },
545
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
546
+ },
547
+ {
548
+ provider: "google",
549
+ modelId: "gemini-embedding-2",
550
+ displayName: "Gemini Embedding 2",
551
+ family: "gemini-embedding",
552
+ status: "active",
553
+ modalities: ["text", "image", "video", "audio", "pdf"],
554
+ pricing: {
555
+ textInput: 0.2,
556
+ imageInput: 0.45,
557
+ audioInput: 6.5,
558
+ videoInput: 12
559
+ },
560
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
561
+ },
562
+ {
563
+ provider: "google",
564
+ modelId: "gemini-embedding-001",
565
+ displayName: "Gemini Embedding",
566
+ family: "gemini-embedding",
567
+ status: "active",
568
+ modalities: ["text"],
569
+ pricing: {
570
+ input: 0.15,
571
+ batchInput: 0.075
572
+ },
573
+ sourceUrl: "https://ai.google.dev/gemini-api/docs/pricing"
574
+ },
575
+ {
576
+ provider: "deepseek",
577
+ modelId: "deepseek-v4-flash",
578
+ displayName: "DeepSeek V4 Flash",
579
+ family: "deepseek-v4",
580
+ status: "active",
581
+ modalities: ["text"],
582
+ contextWindow: 1e6,
583
+ maxOutputTokens: 384e3,
584
+ pricing: {
585
+ inputCacheHit: 28e-4,
586
+ inputCacheMiss: 0.14,
587
+ output: 0.28
588
+ },
589
+ capabilities: {
590
+ thinkingMode: true,
591
+ jsonOutput: true,
592
+ toolCalls: true,
593
+ fimCompletion: true
594
+ },
595
+ sourceUrl: "https://api-docs.deepseek.com/quick_start/pricing"
596
+ },
597
+ {
598
+ provider: "deepseek",
599
+ modelId: "deepseek-v4-pro",
600
+ displayName: "DeepSeek V4 Pro",
601
+ family: "deepseek-v4",
602
+ status: "active",
603
+ modalities: ["text"],
604
+ contextWindow: 1e6,
605
+ maxOutputTokens: 384e3,
606
+ pricing: {
607
+ inputCacheHit: 3625e-6,
608
+ inputCacheMiss: 0.435,
609
+ output: 0.87,
610
+ note: "DeepSeek states the 75% reduction becomes official pricing after the promotion ends on 2026-05-31 15:59 UTC."
611
+ },
612
+ capabilities: {
613
+ thinkingMode: true,
614
+ jsonOutput: true,
615
+ toolCalls: true,
616
+ fimCompletion: true
617
+ },
618
+ sourceUrl: "https://api-docs.deepseek.com/quick_start/pricing"
619
+ },
620
+ {
621
+ provider: "xai",
622
+ modelId: "grok-4.3",
623
+ displayName: "Grok 4.3",
624
+ family: "grok",
625
+ status: "active",
626
+ modalities: ["text"],
627
+ contextWindow: 1e6,
628
+ pricing: {
629
+ input: 1.25,
630
+ cachedInput: 0.2,
631
+ output: 2.5
632
+ },
633
+ capabilities: {
634
+ reasoning: "configurable",
635
+ toolCalling: true
636
+ },
637
+ sourceUrl: "https://docs.x.ai/developers/pricing"
638
+ },
639
+ {
640
+ provider: "xai",
641
+ modelId: "grok-build-0.1",
642
+ displayName: "Grok Build 0.1",
643
+ family: "grok-build",
644
+ status: "active",
645
+ modalities: ["text", "code"],
646
+ contextWindow: 256e3,
647
+ pricing: {
648
+ input: 1,
649
+ cachedInput: 0.2,
650
+ output: 2
651
+ },
652
+ sourceUrl: "https://docs.x.ai/developers/pricing"
653
+ },
654
+ {
655
+ provider: "xai",
656
+ modelId: "grok-4.20-multi-agent-0309",
657
+ displayName: "Grok 4.20 Multi-Agent",
658
+ family: "grok",
659
+ status: "active",
660
+ modalities: ["text"],
661
+ contextWindow: 1e6,
662
+ pricing: {
663
+ input: 1.25,
664
+ cachedInput: 0.2,
665
+ output: 2.5
666
+ },
667
+ sourceUrl: "https://docs.x.ai/developers/pricing"
668
+ },
669
+ {
670
+ provider: "xai",
671
+ modelId: "grok-4.20-0309-reasoning",
672
+ displayName: "Grok 4.20 Reasoning",
673
+ family: "grok",
674
+ status: "active",
675
+ modalities: ["text"],
676
+ contextWindow: 1e6,
677
+ pricing: {
678
+ input: 1.25,
679
+ cachedInput: 0.2,
680
+ output: 2.5
681
+ },
682
+ sourceUrl: "https://docs.x.ai/developers/pricing"
683
+ },
684
+ {
685
+ provider: "xai",
686
+ modelId: "grok-4.20-0309-non-reasoning",
687
+ displayName: "Grok 4.20 Non-Reasoning",
688
+ family: "grok",
689
+ status: "active",
690
+ modalities: ["text"],
691
+ contextWindow: 1e6,
692
+ pricing: {
693
+ input: 1.25,
694
+ cachedInput: 0.2,
695
+ output: 2.5
696
+ },
697
+ sourceUrl: "https://docs.x.ai/developers/pricing"
698
+ },
699
+ {
700
+ provider: "cohere",
701
+ modelId: "command-a",
702
+ displayName: "Command A",
703
+ family: "command",
704
+ status: "active",
705
+ modalities: ["text"],
706
+ pricing: {
707
+ input: 1,
708
+ output: 2
709
+ },
710
+ sourceUrl: "https://cohere.com/pricing"
711
+ },
712
+ {
713
+ provider: "cohere",
714
+ modelId: "command",
715
+ displayName: "Command",
716
+ family: "command",
717
+ status: "legacy",
718
+ modalities: ["text"],
719
+ pricing: {
720
+ input: 1,
721
+ output: 2
722
+ },
723
+ sourceUrl: "https://cohere.com/pricing"
724
+ },
725
+ {
726
+ provider: "cohere",
727
+ modelId: "command-light",
728
+ displayName: "Command Light",
729
+ family: "command",
730
+ status: "legacy",
731
+ modalities: ["text"],
732
+ pricing: {
733
+ input: 0.3,
734
+ output: 0.6
735
+ },
736
+ sourceUrl: "https://cohere.com/pricing"
737
+ },
738
+ {
739
+ provider: "cohere",
740
+ modelId: "command-r-03-2024",
741
+ displayName: "Command R 03-2024",
742
+ family: "command-r",
743
+ status: "legacy",
744
+ modalities: ["text"],
745
+ pricing: {
746
+ input: 0.5,
747
+ output: 1.5
748
+ },
749
+ sourceUrl: "https://cohere.com/pricing"
750
+ },
751
+ {
752
+ provider: "cohere",
753
+ modelId: "command-r-plus-08-2024",
754
+ displayName: "Command R+ 08-2024",
755
+ family: "command-r",
756
+ status: "legacy",
757
+ modalities: ["text"],
758
+ pricing: {
759
+ input: 2.5,
760
+ output: 10
761
+ },
762
+ sourceUrl: "https://cohere.com/pricing"
763
+ },
764
+ {
765
+ provider: "perplexity",
766
+ modelId: "sonar-pro",
767
+ displayName: "Sonar Pro",
768
+ family: "sonar",
769
+ status: "active",
770
+ modalities: ["text", "search"],
771
+ pricing: {
772
+ input: 3,
773
+ output: 15,
774
+ searchRequestFeePer1k: {
775
+ fastLow: 6,
776
+ fastMedium: 10,
777
+ fastHigh: 14,
778
+ proLow: 14,
779
+ proMedium: 18,
780
+ proHigh: 22
781
+ }
782
+ },
783
+ sourceUrl: "https://docs.perplexity.ai/docs/getting-started/pricing"
784
+ },
785
+ {
786
+ provider: "groq",
787
+ modelId: "openai/gpt-oss-20b",
788
+ displayName: "GPT OSS 20B 128k",
789
+ family: "gpt-oss",
790
+ status: "active",
791
+ modalities: ["text"],
792
+ contextWindow: 128e3,
793
+ pricing: {
794
+ input: 0.075,
795
+ output: 0.3
796
+ },
797
+ sourceUrl: "https://groq.com/pricing"
798
+ },
799
+ {
800
+ provider: "groq",
801
+ modelId: "openai/gpt-oss-120b",
802
+ displayName: "GPT OSS 120B 128k",
803
+ family: "gpt-oss",
804
+ status: "active",
805
+ modalities: ["text"],
806
+ contextWindow: 128e3,
807
+ pricing: {
808
+ input: 0.15,
809
+ output: 0.6
810
+ },
811
+ sourceUrl: "https://groq.com/pricing"
812
+ },
813
+ {
814
+ provider: "groq",
815
+ modelId: "meta-llama/llama-4-scout-17b-16e-instruct",
816
+ displayName: "Llama 4 Scout 17Bx16E 128k",
817
+ family: "llama",
818
+ status: "active",
819
+ modalities: ["text"],
820
+ contextWindow: 128e3,
821
+ pricing: {
822
+ input: 0.11,
823
+ output: 0.34
824
+ },
825
+ sourceUrl: "https://groq.com/pricing"
826
+ },
827
+ {
828
+ provider: "groq",
829
+ modelId: "qwen/qwen3-32b",
830
+ displayName: "Qwen3 32B 131k",
831
+ family: "qwen",
832
+ status: "active",
833
+ modalities: ["text"],
834
+ contextWindow: 131e3,
835
+ pricing: {
836
+ input: 0.29,
837
+ output: 0.59
838
+ },
839
+ sourceUrl: "https://groq.com/pricing"
840
+ },
841
+ {
842
+ provider: "mistral",
843
+ modelId: "mistral-medium-3",
844
+ displayName: "Mistral Medium 3",
845
+ family: "mistral",
846
+ status: "active",
847
+ modalities: ["text", "vision", "tools"],
848
+ pricing: {
849
+ input: 0.4,
850
+ output: 2
851
+ },
852
+ sourceUrl: "https://mistral.ai/news/mistral-medium-3"
853
+ },
854
+ {
855
+ provider: "ai21",
856
+ modelId: "jamba-large-1.7",
857
+ displayName: "Jamba Large 1.7",
858
+ family: "jamba",
859
+ status: "needs_verification",
860
+ modalities: ["text"],
861
+ pricing: {
862
+ input: null,
863
+ output: null
864
+ },
865
+ notes: "AI21 docs say pricing depends on platform/endpoints and links to account/billing; public token prices were not exposed clearly in the checked official docs.",
866
+ sourceUrl: "https://docs.ai21.com/docs/usage-cost"
867
+ }
868
+ ]
869
+ };
870
+
871
+ // src/data/openrouter-models-catalog.json
872
+ var openrouter_models_catalog_default = {
873
+ schema: "x12i.openrouter-models.catalog",
874
+ version: "0.1.0",
875
+ currency: "USD",
876
+ pricingUnit: "1M_tokens",
877
+ source: {
878
+ provider: "openrouter",
879
+ catalogUrl: "https://openrouter.ai/api/v1/models",
880
+ pricingUrl: "https://openrouter.ai/pricing",
881
+ verifiedAt: "2026-05-27",
882
+ normalization: "OpenRouter pricing fields are per-token decimal strings. Values below are normalized to USD per 1M tokens."
883
+ },
884
+ notes: [
885
+ "This catalog is OpenRouter-specific and should not be merged with direct-provider pricing.",
886
+ "Router models beginning with ~ are dynamic aliases that redirect to latest model families.",
887
+ "Models ending with :free are free OpenRouter variants and may have rate limits or availability limits.",
888
+ "webSearch is kept as a separate field because OpenRouter exposes it separately from token pricing.",
889
+ "This is a seed catalog. In production, generate the full file from https://openrouter.ai/api/v1/models."
890
+ ],
891
+ models: [
892
+ {
893
+ provider: "openrouter",
894
+ modelId: "qwen/qwen3.7-max",
895
+ canonicalSlug: "qwen/qwen3.7-max-20260520",
896
+ displayName: "Qwen: Qwen3.7 Max",
897
+ family: "qwen",
898
+ status: "active",
899
+ modalities: {
900
+ input: ["text"],
901
+ output: ["text"]
902
+ },
903
+ contextWindow: 1e6,
904
+ maxCompletionTokens: 65536,
905
+ pricing: {
906
+ input: 1.25,
907
+ output: 3.75,
908
+ cachedInput: 0.25,
909
+ cacheWrite: 1.5625
910
+ },
911
+ capabilities: {
912
+ reasoning: true,
913
+ toolCalling: true,
914
+ structuredOutputs: true
915
+ }
916
+ },
917
+ {
918
+ provider: "openrouter",
919
+ modelId: "x-ai/grok-build-0.1",
920
+ canonicalSlug: "x-ai/grok-build-0.1-20260520",
921
+ displayName: "xAI: Grok Build 0.1",
922
+ family: "grok",
923
+ status: "active",
924
+ modalities: {
925
+ input: ["text", "image"],
926
+ output: ["text"]
927
+ },
928
+ contextWindow: 256e3,
929
+ pricing: {
930
+ input: 1,
931
+ output: 2,
932
+ cachedInput: 0.2,
933
+ webSearchPerRequest: 5e-3
934
+ },
935
+ capabilities: {
936
+ reasoning: true,
937
+ toolCalling: true,
938
+ structuredOutputs: true
939
+ }
940
+ },
941
+ {
942
+ provider: "openrouter",
943
+ modelId: "google/gemini-3.5-flash",
944
+ canonicalSlug: "google/gemini-3.5-flash-20260519",
945
+ displayName: "Google: Gemini 3.5 Flash",
946
+ family: "gemini",
947
+ status: "active",
948
+ modalities: {
949
+ input: ["text", "image", "video", "file", "audio"],
950
+ output: ["text"]
951
+ },
952
+ contextWindow: 1048576,
953
+ maxCompletionTokens: 65536,
954
+ knowledgeCutoff: "2025-01-01",
955
+ pricing: {
956
+ input: 1.5,
957
+ output: 9,
958
+ imageInput: 1.5,
959
+ audioInput: 3,
960
+ cachedInput: 0.15,
961
+ cacheWrite: 0.08333333333333334,
962
+ internalReasoning: 9,
963
+ webSearchPerRequest: 0.014
964
+ },
965
+ capabilities: {
966
+ reasoning: true,
967
+ toolCalling: true,
968
+ structuredOutputs: true
969
+ }
970
+ },
971
+ {
972
+ provider: "openrouter",
973
+ modelId: "anthropic/claude-opus-4.7-fast",
974
+ canonicalSlug: "anthropic/claude-4.7-opus-fast-20260512",
975
+ displayName: "Anthropic: Claude Opus 4.7 Fast",
976
+ family: "claude",
977
+ status: "active",
978
+ modalities: {
979
+ input: ["text", "image", "file"],
980
+ output: ["text"]
981
+ },
982
+ contextWindow: 1e6,
983
+ maxCompletionTokens: 128e3,
984
+ pricing: {
985
+ input: 30,
986
+ output: 150,
987
+ cachedInput: 3,
988
+ cacheWrite: 37.5,
989
+ webSearchPerRequest: 0.01
990
+ },
991
+ capabilities: {
992
+ reasoning: true,
993
+ toolCalling: true,
994
+ structuredOutputs: true
995
+ }
996
+ },
997
+ {
998
+ provider: "openrouter",
999
+ modelId: "perceptron/perceptron-mk1",
1000
+ canonicalSlug: "perceptron/perceptron-mk1-20260512",
1001
+ displayName: "Perceptron: Perceptron Mk1",
1002
+ family: "perceptron",
1003
+ status: "active",
1004
+ modalities: {
1005
+ input: ["text", "image", "video"],
1006
+ output: ["text"]
1007
+ },
1008
+ contextWindow: 32768,
1009
+ maxCompletionTokens: 8192,
1010
+ pricing: {
1011
+ input: 0.15,
1012
+ output: 1.5
1013
+ },
1014
+ capabilities: {
1015
+ reasoning: true,
1016
+ structuredOutputs: true
1017
+ }
1018
+ },
1019
+ {
1020
+ provider: "openrouter",
1021
+ modelId: "inclusionai/ring-2.6-1t",
1022
+ canonicalSlug: "inclusionai/ring-2.6-1t-20260508",
1023
+ displayName: "inclusionAI: Ring-2.6-1T",
1024
+ family: "ring",
1025
+ status: "active",
1026
+ modalities: {
1027
+ input: ["text"],
1028
+ output: ["text"]
1029
+ },
1030
+ contextWindow: 262144,
1031
+ maxCompletionTokens: 65536,
1032
+ pricing: {
1033
+ input: 0.075,
1034
+ output: 0.625,
1035
+ cachedInput: 0.015
1036
+ },
1037
+ capabilities: {
1038
+ reasoning: true,
1039
+ toolCalling: true,
1040
+ structuredOutputs: true
1041
+ }
1042
+ },
1043
+ {
1044
+ provider: "openrouter",
1045
+ modelId: "google/gemini-3.1-flash-lite",
1046
+ canonicalSlug: "google/gemini-3.1-flash-lite-20260507",
1047
+ displayName: "Google: Gemini 3.1 Flash Lite",
1048
+ family: "gemini",
1049
+ status: "active",
1050
+ modalities: {
1051
+ input: ["text", "image", "video", "file", "audio"],
1052
+ output: ["text"]
1053
+ },
1054
+ contextWindow: 1048576,
1055
+ maxCompletionTokens: 65536,
1056
+ pricing: {
1057
+ input: 0.25,
1058
+ output: 1.5,
1059
+ imageInput: 0.25,
1060
+ audioInput: 0.5,
1061
+ cachedInput: 0.025,
1062
+ cacheWrite: 0.08333333333333334,
1063
+ internalReasoning: 1.5,
1064
+ webSearchPerRequest: 0.014
1065
+ },
1066
+ capabilities: {
1067
+ reasoning: true,
1068
+ toolCalling: true,
1069
+ structuredOutputs: true
1070
+ }
1071
+ },
1072
+ {
1073
+ provider: "openrouter",
1074
+ modelId: "openai/gpt-chat-latest",
1075
+ canonicalSlug: "openai/gpt-chat-latest-20260505",
1076
+ displayName: "OpenAI: GPT Chat Latest",
1077
+ family: "gpt",
1078
+ status: "active",
1079
+ modalities: {
1080
+ input: ["text", "image", "file"],
1081
+ output: ["text"]
1082
+ },
1083
+ contextWindow: 4e5,
1084
+ maxCompletionTokens: 128e3,
1085
+ pricing: {
1086
+ input: 5,
1087
+ output: 30,
1088
+ cachedInput: 0.5,
1089
+ webSearchPerRequest: 0.01
1090
+ },
1091
+ capabilities: {
1092
+ toolCalling: true,
1093
+ structuredOutputs: true
1094
+ }
1095
+ },
1096
+ {
1097
+ provider: "openrouter",
1098
+ modelId: "x-ai/grok-4.3",
1099
+ canonicalSlug: "x-ai/grok-4.3-20260430",
1100
+ displayName: "xAI: Grok 4.3",
1101
+ family: "grok",
1102
+ status: "active",
1103
+ modalities: {
1104
+ input: ["text", "image"],
1105
+ output: ["text"]
1106
+ },
1107
+ contextWindow: 1e6,
1108
+ pricing: {
1109
+ input: 1.25,
1110
+ output: 2.5,
1111
+ cachedInput: 0.2,
1112
+ webSearchPerRequest: 5e-3
1113
+ },
1114
+ capabilities: {
1115
+ reasoning: true,
1116
+ toolCalling: true,
1117
+ structuredOutputs: true
1118
+ }
1119
+ },
1120
+ {
1121
+ provider: "openrouter",
1122
+ modelId: "ibm-granite/granite-4.1-8b",
1123
+ canonicalSlug: "ibm-granite/granite-4.1-8b-20260429",
1124
+ displayName: "IBM: Granite 4.1 8B",
1125
+ family: "granite",
1126
+ status: "active",
1127
+ modalities: {
1128
+ input: ["text"],
1129
+ output: ["text"]
1130
+ },
1131
+ contextWindow: 131072,
1132
+ maxCompletionTokens: 131072,
1133
+ pricing: {
1134
+ input: 0.05,
1135
+ output: 0.1,
1136
+ cachedInput: 0.05
1137
+ },
1138
+ capabilities: {
1139
+ toolCalling: true,
1140
+ structuredOutputs: true
1141
+ }
1142
+ },
1143
+ {
1144
+ provider: "openrouter",
1145
+ modelId: "mistralai/mistral-medium-3-5",
1146
+ canonicalSlug: "mistralai/mistral-medium-3.5-20260430",
1147
+ displayName: "Mistral: Mistral Medium 3.5",
1148
+ family: "mistral",
1149
+ status: "active",
1150
+ modalities: {
1151
+ input: ["text", "image", "file"],
1152
+ output: ["text"]
1153
+ },
1154
+ contextWindow: 262144,
1155
+ pricing: {
1156
+ input: 1.5,
1157
+ output: 7.5
1158
+ },
1159
+ capabilities: {
1160
+ reasoning: true,
1161
+ toolCalling: true,
1162
+ structuredOutputs: true
1163
+ }
1164
+ },
1165
+ {
1166
+ provider: "openrouter",
1167
+ modelId: "openrouter/owl-alpha",
1168
+ canonicalSlug: "openrouter/owl-alpha",
1169
+ displayName: "Owl Alpha",
1170
+ family: "owl",
1171
+ status: "active",
1172
+ modalities: {
1173
+ input: ["text"],
1174
+ output: ["text"]
1175
+ },
1176
+ contextWindow: 1048756,
1177
+ maxCompletionTokens: 262144,
1178
+ pricing: {
1179
+ input: 0,
1180
+ output: 0
1181
+ },
1182
+ capabilities: {
1183
+ toolCalling: true,
1184
+ structuredOutputs: true
1185
+ }
1186
+ },
1187
+ {
1188
+ provider: "openrouter",
1189
+ modelId: "~anthropic/claude-haiku-latest",
1190
+ canonicalSlug: "~anthropic/claude-haiku-latest",
1191
+ displayName: "Anthropic Claude Haiku Latest",
1192
+ family: "claude-router",
1193
+ status: "router",
1194
+ modalities: {
1195
+ input: ["text", "image", "file"],
1196
+ output: ["text"]
1197
+ },
1198
+ contextWindow: 2e5,
1199
+ maxCompletionTokens: 64e3,
1200
+ pricing: {
1201
+ input: 1,
1202
+ output: 5,
1203
+ cachedInput: 0.1,
1204
+ cacheWrite: 1.25,
1205
+ webSearchPerRequest: 0.01
1206
+ },
1207
+ capabilities: {
1208
+ reasoning: true,
1209
+ toolCalling: true,
1210
+ structuredOutputs: true
1211
+ }
1212
+ },
1213
+ {
1214
+ provider: "openrouter",
1215
+ modelId: "~openai/gpt-mini-latest",
1216
+ canonicalSlug: "~openai/gpt-mini-latest",
1217
+ displayName: "OpenAI GPT Mini Latest",
1218
+ family: "gpt-router",
1219
+ status: "router",
1220
+ modalities: {
1221
+ input: ["text", "image", "file"],
1222
+ output: ["text"]
1223
+ },
1224
+ contextWindow: 4e5,
1225
+ maxCompletionTokens: 128e3,
1226
+ knowledgeCutoff: "2025-08-31",
1227
+ pricing: {
1228
+ input: 0.75,
1229
+ output: 4.5,
1230
+ cachedInput: 0.075,
1231
+ webSearchPerRequest: 0.01
1232
+ },
1233
+ capabilities: {
1234
+ reasoning: true,
1235
+ toolCalling: true,
1236
+ structuredOutputs: true
1237
+ }
1238
+ },
1239
+ {
1240
+ provider: "openrouter",
1241
+ modelId: "~google/gemini-pro-latest",
1242
+ canonicalSlug: "~google/gemini-pro-latest",
1243
+ displayName: "Google Gemini Pro Latest",
1244
+ family: "gemini-router",
1245
+ status: "router",
1246
+ modalities: {
1247
+ input: ["text", "image", "file", "audio", "video"],
1248
+ output: ["text"]
1249
+ },
1250
+ contextWindow: 1048576,
1251
+ maxCompletionTokens: 65536,
1252
+ pricing: {
1253
+ input: 2,
1254
+ output: 12,
1255
+ imageInput: 2,
1256
+ audioInput: 2,
1257
+ cachedInput: 0.2,
1258
+ cacheWrite: 0.375,
1259
+ internalReasoning: 12,
1260
+ webSearchPerRequest: 0.014
1261
+ },
1262
+ capabilities: {
1263
+ reasoning: true,
1264
+ toolCalling: true,
1265
+ structuredOutputs: true
1266
+ }
1267
+ },
1268
+ {
1269
+ provider: "openrouter",
1270
+ modelId: "~moonshotai/kimi-latest",
1271
+ canonicalSlug: "~moonshotai/kimi-latest",
1272
+ displayName: "MoonshotAI Kimi Latest",
1273
+ family: "kimi-router",
1274
+ status: "router",
1275
+ modalities: {
1276
+ input: ["text", "image"],
1277
+ output: ["text"]
1278
+ },
1279
+ contextWindow: 262144,
1280
+ maxCompletionTokens: 262142,
1281
+ pricing: {
1282
+ input: 0.73,
1283
+ output: 3.49,
1284
+ cachedInput: 0.25
1285
+ },
1286
+ capabilities: {
1287
+ reasoning: true,
1288
+ toolCalling: true,
1289
+ structuredOutputs: true
1290
+ }
1291
+ },
1292
+ {
1293
+ provider: "openrouter",
1294
+ modelId: "~google/gemini-flash-latest",
1295
+ canonicalSlug: "~google/gemini-flash-latest",
1296
+ displayName: "Google Gemini Flash Latest",
1297
+ family: "gemini-router",
1298
+ status: "router",
1299
+ modalities: {
1300
+ input: ["text", "image", "video", "file", "audio"],
1301
+ output: ["text"]
1302
+ },
1303
+ contextWindow: 1048576,
1304
+ maxCompletionTokens: 65536,
1305
+ knowledgeCutoff: "2025-01-01",
1306
+ pricing: {
1307
+ input: 1.5,
1308
+ output: 9,
1309
+ imageInput: 1.5,
1310
+ audioInput: 3,
1311
+ cachedInput: 0.15,
1312
+ cacheWrite: 0.08333333333333334,
1313
+ internalReasoning: 9,
1314
+ webSearchPerRequest: 0.014
1315
+ },
1316
+ capabilities: {
1317
+ reasoning: true,
1318
+ toolCalling: true,
1319
+ structuredOutputs: true
1320
+ }
1321
+ },
1322
+ {
1323
+ provider: "openrouter",
1324
+ modelId: "~anthropic/claude-sonnet-latest",
1325
+ canonicalSlug: "~anthropic/claude-sonnet-latest",
1326
+ displayName: "Anthropic Claude Sonnet Latest",
1327
+ family: "claude-router",
1328
+ status: "router",
1329
+ modalities: {
1330
+ input: ["text", "image", "file"],
1331
+ output: ["text"]
1332
+ },
1333
+ contextWindow: 1e6,
1334
+ maxCompletionTokens: 128e3,
1335
+ pricing: {
1336
+ input: 3,
1337
+ output: 15,
1338
+ cachedInput: 0.3,
1339
+ cacheWrite: 3.75,
1340
+ webSearchPerRequest: 0.01
1341
+ },
1342
+ capabilities: {
1343
+ reasoning: true,
1344
+ toolCalling: true,
1345
+ structuredOutputs: true
1346
+ }
1347
+ },
1348
+ {
1349
+ provider: "openrouter",
1350
+ modelId: "~openai/gpt-latest",
1351
+ canonicalSlug: "~openai/gpt-latest",
1352
+ displayName: "OpenAI GPT Latest",
1353
+ family: "gpt-router",
1354
+ status: "router",
1355
+ modalities: {
1356
+ input: ["text", "image", "file"],
1357
+ output: ["text"]
1358
+ },
1359
+ contextWindow: 105e4,
1360
+ maxCompletionTokens: 128e3,
1361
+ knowledgeCutoff: "2025-12-01",
1362
+ pricing: {
1363
+ input: 5,
1364
+ output: 30,
1365
+ cachedInput: 0.5,
1366
+ webSearchPerRequest: 0.01
1367
+ },
1368
+ capabilities: {
1369
+ reasoning: true,
1370
+ toolCalling: true,
1371
+ structuredOutputs: true
1372
+ }
1373
+ },
1374
+ {
1375
+ provider: "openrouter",
1376
+ modelId: "qwen/qwen3.5-plus-20260420",
1377
+ canonicalSlug: "qwen/qwen3.5-plus-20260420",
1378
+ displayName: "Qwen: Qwen3.5 Plus 2026-04-20",
1379
+ family: "qwen",
1380
+ status: "active",
1381
+ modalities: {
1382
+ input: ["text", "image", "video"],
1383
+ output: ["text"]
1384
+ },
1385
+ contextWindow: 1e6,
1386
+ maxCompletionTokens: 65536,
1387
+ pricing: {
1388
+ input: 0.3,
1389
+ output: 1.8,
1390
+ cacheWrite: 0.375
1391
+ },
1392
+ capabilities: {
1393
+ reasoning: true,
1394
+ toolCalling: true,
1395
+ structuredOutputs: true
1396
+ }
1397
+ },
1398
+ {
1399
+ provider: "openrouter",
1400
+ modelId: "qwen/qwen3.6-flash",
1401
+ canonicalSlug: "qwen/qwen3.6-flash",
1402
+ displayName: "Qwen: Qwen3.6 Flash",
1403
+ family: "qwen",
1404
+ status: "active",
1405
+ modalities: {
1406
+ input: ["text", "image", "video"],
1407
+ output: ["text"]
1408
+ },
1409
+ contextWindow: 1e6,
1410
+ maxCompletionTokens: 65536,
1411
+ pricing: {
1412
+ input: 0.1875,
1413
+ output: 1.125,
1414
+ cacheWrite: 0.234375
1415
+ },
1416
+ capabilities: {
1417
+ reasoning: true,
1418
+ toolCalling: true,
1419
+ structuredOutputs: true
1420
+ }
1421
+ },
1422
+ {
1423
+ provider: "openrouter",
1424
+ modelId: "qwen/qwen3.6-35b-a3b",
1425
+ canonicalSlug: "qwen/qwen3.6-35b-a3b-20260415",
1426
+ displayName: "Qwen: Qwen3.6 35B A3B",
1427
+ family: "qwen",
1428
+ status: "active",
1429
+ modalities: {
1430
+ input: ["text", "image", "video"],
1431
+ output: ["text"]
1432
+ },
1433
+ contextWindow: 262144,
1434
+ maxCompletionTokens: 262140,
1435
+ pricing: {
1436
+ input: 0.14,
1437
+ output: 1
1438
+ },
1439
+ capabilities: {
1440
+ reasoning: true,
1441
+ toolCalling: true,
1442
+ structuredOutputs: true
1443
+ }
1444
+ },
1445
+ {
1446
+ provider: "openrouter",
1447
+ modelId: "qwen/qwen3.6-max-preview",
1448
+ canonicalSlug: "qwen/qwen3.6-max-preview-20260420",
1449
+ displayName: "Qwen: Qwen3.6 Max Preview",
1450
+ family: "qwen",
1451
+ status: "preview",
1452
+ modalities: {
1453
+ input: ["text"],
1454
+ output: ["text"]
1455
+ },
1456
+ contextWindow: 262144,
1457
+ maxCompletionTokens: 65536,
1458
+ pricing: {
1459
+ input: 1.04,
1460
+ output: 6.24,
1461
+ cacheWrite: 1.3
1462
+ },
1463
+ capabilities: {
1464
+ reasoning: true,
1465
+ toolCalling: true,
1466
+ structuredOutputs: true
1467
+ }
1468
+ },
1469
+ {
1470
+ provider: "openrouter",
1471
+ modelId: "qwen/qwen3.6-27b",
1472
+ canonicalSlug: "qwen/qwen3.6-27b-20260422",
1473
+ displayName: "Qwen: Qwen3.6 27B",
1474
+ family: "qwen",
1475
+ status: "active",
1476
+ modalities: {
1477
+ input: ["text", "image", "video"],
1478
+ output: ["text"]
1479
+ },
1480
+ contextWindow: 262144,
1481
+ maxCompletionTokens: 262140,
1482
+ pricing: {
1483
+ input: 0.29,
1484
+ output: 3.2
1485
+ },
1486
+ capabilities: {
1487
+ reasoning: true,
1488
+ toolCalling: true,
1489
+ structuredOutputs: true
1490
+ }
1491
+ },
1492
+ {
1493
+ provider: "openrouter",
1494
+ modelId: "openai/gpt-5.5-pro",
1495
+ canonicalSlug: "openai/gpt-5.5-pro-20260423",
1496
+ displayName: "OpenAI: GPT-5.5 Pro",
1497
+ family: "gpt",
1498
+ status: "active",
1499
+ modalities: {
1500
+ input: ["text", "image", "file"],
1501
+ output: ["text"]
1502
+ },
1503
+ contextWindow: 105e4,
1504
+ maxCompletionTokens: 128e3,
1505
+ knowledgeCutoff: "2025-12-01",
1506
+ pricing: {
1507
+ input: 30,
1508
+ output: 180,
1509
+ webSearchPerRequest: 0.01
1510
+ },
1511
+ capabilities: {
1512
+ reasoning: true,
1513
+ toolCalling: true,
1514
+ structuredOutputs: true
1515
+ }
1516
+ },
1517
+ {
1518
+ provider: "openrouter",
1519
+ modelId: "openai/gpt-5.5",
1520
+ canonicalSlug: "openai/gpt-5.5-20260423",
1521
+ displayName: "OpenAI: GPT-5.5",
1522
+ family: "gpt",
1523
+ status: "active",
1524
+ modalities: {
1525
+ input: ["text", "image", "file"],
1526
+ output: ["text"]
1527
+ },
1528
+ contextWindow: 105e4,
1529
+ maxCompletionTokens: 128e3,
1530
+ knowledgeCutoff: "2025-12-01",
1531
+ pricing: {
1532
+ input: 5,
1533
+ output: 30,
1534
+ cachedInput: 0.5,
1535
+ webSearchPerRequest: 0.01
1536
+ },
1537
+ capabilities: {
1538
+ reasoning: true,
1539
+ toolCalling: true,
1540
+ structuredOutputs: true
1541
+ }
1542
+ },
1543
+ {
1544
+ provider: "openrouter",
1545
+ modelId: "deepseek/deepseek-v4-pro",
1546
+ canonicalSlug: "deepseek/deepseek-v4-pro-20260423",
1547
+ displayName: "DeepSeek: DeepSeek V4 Pro",
1548
+ family: "deepseek",
1549
+ status: "active",
1550
+ modalities: {
1551
+ input: ["text"],
1552
+ output: ["text"]
1553
+ },
1554
+ contextWindow: 1048576,
1555
+ maxCompletionTokens: 384e3,
1556
+ pricing: {
1557
+ input: 0.435,
1558
+ output: 0.87,
1559
+ cachedInput: 3625e-6
1560
+ },
1561
+ capabilities: {
1562
+ reasoning: true,
1563
+ toolCalling: true,
1564
+ structuredOutputs: true
1565
+ }
1566
+ },
1567
+ {
1568
+ provider: "openrouter",
1569
+ modelId: "deepseek/deepseek-v4-flash",
1570
+ canonicalSlug: "deepseek/deepseek-v4-flash-20260423",
1571
+ displayName: "DeepSeek: DeepSeek V4 Flash",
1572
+ family: "deepseek",
1573
+ status: "active",
1574
+ modalities: {
1575
+ input: ["text"],
1576
+ output: ["text"]
1577
+ },
1578
+ contextWindow: 1048576,
1579
+ maxCompletionTokens: 16384,
1580
+ pricing: {
1581
+ input: 0.1,
1582
+ output: 0.2,
1583
+ cachedInput: 0.02
1584
+ },
1585
+ capabilities: {
1586
+ reasoning: true,
1587
+ toolCalling: true,
1588
+ structuredOutputs: true
1589
+ }
1590
+ },
1591
+ {
1592
+ provider: "openrouter",
1593
+ modelId: "deepseek/deepseek-v4-flash:free",
1594
+ canonicalSlug: "deepseek/deepseek-v4-flash-20260423",
1595
+ displayName: "DeepSeek: DeepSeek V4 Flash Free",
1596
+ family: "deepseek",
1597
+ status: "free",
1598
+ modalities: {
1599
+ input: ["text"],
1600
+ output: ["text"]
1601
+ },
1602
+ contextWindow: 1048576,
1603
+ maxCompletionTokens: 384e3,
1604
+ pricing: {
1605
+ input: 0,
1606
+ output: 0
1607
+ },
1608
+ capabilities: {
1609
+ reasoning: true,
1610
+ toolCalling: true
1611
+ }
1612
+ },
1613
+ {
1614
+ provider: "openrouter",
1615
+ modelId: "inclusionai/ling-2.6-1t",
1616
+ canonicalSlug: "inclusionai/ling-2.6-1t-20260423",
1617
+ displayName: "inclusionAI: Ling-2.6-1T",
1618
+ family: "ling",
1619
+ status: "active",
1620
+ modalities: {
1621
+ input: ["text"],
1622
+ output: ["text"]
1623
+ },
1624
+ contextWindow: 262144,
1625
+ maxCompletionTokens: 32768,
1626
+ pricing: {
1627
+ input: 0.075,
1628
+ output: 0.625,
1629
+ cachedInput: 0.015
1630
+ },
1631
+ capabilities: {
1632
+ toolCalling: true,
1633
+ structuredOutputs: true
1634
+ }
1635
+ },
1636
+ {
1637
+ provider: "openrouter",
1638
+ modelId: "tencent/hy3-preview",
1639
+ canonicalSlug: "tencent/hy3-preview-20260421",
1640
+ displayName: "Tencent: Hy3 Preview",
1641
+ family: "hy3",
1642
+ status: "preview",
1643
+ modalities: {
1644
+ input: ["text"],
1645
+ output: ["text"]
1646
+ },
1647
+ contextWindow: 262144,
1648
+ maxCompletionTokens: 262144,
1649
+ pricing: {
1650
+ input: 0.066,
1651
+ output: 0.26,
1652
+ cachedInput: 0.029
1653
+ },
1654
+ capabilities: {
1655
+ reasoning: true,
1656
+ toolCalling: true
1657
+ }
1658
+ },
1659
+ {
1660
+ provider: "openrouter",
1661
+ modelId: "xiaomi/mimo-v2.5-pro",
1662
+ canonicalSlug: "xiaomi/mimo-v2.5-pro-20260422",
1663
+ displayName: "Xiaomi: MiMo-V2.5-Pro",
1664
+ family: "mimo",
1665
+ status: "active",
1666
+ modalities: {
1667
+ input: ["text"],
1668
+ output: ["text"]
1669
+ },
1670
+ contextWindow: 1048576,
1671
+ maxCompletionTokens: 131072,
1672
+ pricing: {
1673
+ input: 0.435,
1674
+ output: 0.87,
1675
+ cachedInput: 36e-4
1676
+ },
1677
+ capabilities: {
1678
+ reasoning: true,
1679
+ toolCalling: true,
1680
+ structuredOutputs: true
1681
+ }
1682
+ },
1683
+ {
1684
+ provider: "openrouter",
1685
+ modelId: "xiaomi/mimo-v2.5",
1686
+ canonicalSlug: "xiaomi/mimo-v2.5-20260422",
1687
+ displayName: "Xiaomi: MiMo-V2.5",
1688
+ family: "mimo",
1689
+ status: "active",
1690
+ modalities: {
1691
+ input: ["text", "image", "audio", "video"],
1692
+ output: ["text"]
1693
+ },
1694
+ contextWindow: 1048576,
1695
+ maxCompletionTokens: 131072,
1696
+ pricing: {
1697
+ input: 0.14,
1698
+ output: 0.28,
1699
+ cachedInput: 28e-4
1700
+ },
1701
+ capabilities: {
1702
+ reasoning: true,
1703
+ toolCalling: true
1704
+ }
1705
+ },
1706
+ {
1707
+ provider: "openrouter",
1708
+ modelId: "openai/gpt-5.4-image-2",
1709
+ canonicalSlug: "openai/gpt-5.4-image-2-20260421",
1710
+ displayName: "OpenAI: GPT-5.4 Image 2",
1711
+ family: "gpt-image",
1712
+ status: "active",
1713
+ modalities: {
1714
+ input: ["text", "image", "file"],
1715
+ output: ["text", "image"]
1716
+ },
1717
+ contextWindow: 272e3,
1718
+ pricing: {
1719
+ input: 8,
1720
+ output: 15
1721
+ },
1722
+ capabilities: {
1723
+ imageGeneration: true,
1724
+ structuredOutputs: true
1725
+ }
1726
+ }
1727
+ ]
1728
+ };
1729
+
1730
+ // src/catalog/loadCatalogSources.ts
1731
+ var dataDir = dirname(fileURLToPath(import.meta.url));
1732
+ async function fetchCatalogJson(url, timeoutMs) {
1733
+ const res = await fetch(url, {
1734
+ signal: AbortSignal.timeout(timeoutMs),
1735
+ headers: { accept: "application/json" }
1736
+ });
1737
+ if (!res.ok) {
1738
+ throw new Error(`HTTP ${res.status} fetching ${url}`);
1739
+ }
1740
+ return await res.json();
1741
+ }
1742
+ async function loadOneSource(source, options) {
1743
+ if (options.bundledOnly) {
1744
+ return { file: source.bundled, from: "bundled" };
1745
+ }
1746
+ try {
1747
+ const file = await fetchCatalogJson(source.url, options.fetchTimeoutMs ?? 3e4);
1748
+ return { file, from: "remote" };
1749
+ } catch (err) {
1750
+ console.warn(
1751
+ `[ai-tools] Failed to load ${source.kind} catalog from ${source.url} (${err instanceof Error ? err.message : err}); using bundled fallback.`
1752
+ );
1753
+ return { file: source.bundled, from: "bundled" };
1754
+ }
1755
+ }
1756
+ async function loadCatalogSources(options = {}) {
1757
+ const directUrl = options.directCatalogUrl ?? DEFAULT_DIRECT_CATALOG_URL;
1758
+ const openRouterUrl = options.openRouterCatalogUrl ?? DEFAULT_OPENROUTER_CATALOG_URL;
1759
+ const sources = [
1760
+ {
1761
+ kind: "direct",
1762
+ url: directUrl,
1763
+ bundled: models_catalog_default
1764
+ },
1765
+ {
1766
+ kind: "openrouter",
1767
+ url: openRouterUrl,
1768
+ bundled: openrouter_models_catalog_default
1769
+ }
1770
+ ];
1771
+ const [directLoad, orLoad] = await Promise.all(
1772
+ sources.map((s) => loadOneSource(s, options))
1773
+ );
1774
+ const direct = modelsFromX12iCatalogFile(directLoad.file, "direct");
1775
+ const openrouter = modelsFromX12iCatalogFile(orLoad.file, "openrouter");
1776
+ return {
1777
+ direct,
1778
+ openrouter,
1779
+ meta: {
1780
+ directSource: directLoad.from,
1781
+ openRouterSource: orLoad.from,
1782
+ directUrl,
1783
+ openRouterUrl,
1784
+ directCount: direct.size,
1785
+ openRouterCount: openrouter.size
1786
+ }
1787
+ };
1788
+ }
1789
+ async function readBundledCatalogFiles() {
1790
+ const directPath = join(dataDir, "../data/models-catalog.json");
1791
+ const orPath = join(dataDir, "../data/openrouter-models-catalog.json");
1792
+ const [directRaw, orRaw] = await Promise.all([
1793
+ readFile(directPath, "utf8"),
1794
+ readFile(orPath, "utf8")
1795
+ ]);
1796
+ const directFile = JSON.parse(directRaw);
1797
+ const orFile = JSON.parse(orRaw);
1798
+ return {
1799
+ direct: modelsFromX12iCatalogFile(directFile, "direct"),
1800
+ openrouter: modelsFromX12iCatalogFile(orFile, "openrouter"),
1801
+ meta: {
1802
+ directSource: "bundled",
1803
+ openRouterSource: "bundled",
1804
+ directUrl: DEFAULT_DIRECT_CATALOG_URL,
1805
+ openRouterUrl: DEFAULT_OPENROUTER_CATALOG_URL,
1806
+ directCount: directFile.models.length,
1807
+ openRouterCount: orFile.models.length
1808
+ }
1809
+ };
1810
+ }
1811
+
1812
+ // src/catalog/catalogLoadCache.ts
1813
+ var slots = /* @__PURE__ */ new Map();
1814
+ var inflight = /* @__PURE__ */ new Map();
1815
+ function slotKey(options, cacheKey) {
1816
+ return [
1817
+ cacheKey,
1818
+ options.bundledOnly ? "bundled" : "remote",
1819
+ options.directCatalogUrl ?? "",
1820
+ options.openRouterCatalogUrl ?? ""
1821
+ ].join("\0");
1822
+ }
1823
+ function invalidateCatalogLoadCache(cacheKey = "default") {
1824
+ for (const key of [...slots.keys()]) {
1825
+ if (key.startsWith(`${cacheKey}\0`)) slots.delete(key);
1826
+ }
1827
+ for (const key of [...inflight.keys()]) {
1828
+ if (key.startsWith(`${cacheKey}\0`)) inflight.delete(key);
1829
+ }
1830
+ }
1831
+ async function loadCatalogSourcesCached(options = {}, cacheOptions = {}) {
1832
+ if (options.bundledOnly) {
1833
+ return loadCatalogSources(options);
1834
+ }
1835
+ const ttlMs = resolveCatalogCacheTtlMs(cacheOptions.ttlMs);
1836
+ const key = slotKey(options, cacheOptions.cacheKey ?? "default");
1837
+ if (!cacheOptions.forceRefresh) {
1838
+ const slot = slots.get(key);
1839
+ if (slot && Date.now() - slot.loadedAt < ttlMs) {
1840
+ return slot.data;
1841
+ }
1842
+ } else {
1843
+ slots.delete(key);
1844
+ inflight.delete(key);
1845
+ }
1846
+ const pending = inflight.get(key);
1847
+ if (pending) return pending;
1848
+ const promise = loadCatalogSources(options).then((data) => {
1849
+ slots.set(key, { loadedAt: Date.now(), data });
1850
+ inflight.delete(key);
1851
+ return data;
1852
+ }).catch((err) => {
1853
+ inflight.delete(key);
1854
+ throw err;
1855
+ });
1856
+ inflight.set(key, promise);
1857
+ return promise;
1858
+ }
1859
+
1860
+ // src/catalog/AiModelsCatalogClient.ts
1861
+ function isOpenRouterProvider(provider) {
1862
+ return normalizeProvider(provider) === "openrouter";
1863
+ }
1864
+ var AiModelsCatalogClient = class {
1865
+ cacheTtlMs;
1866
+ cacheKey;
1867
+ loadOptions;
1868
+ resolverOptions;
1869
+ directModels = null;
1870
+ openRouterModels = null;
1871
+ loadedAt = 0;
1872
+ loadPromise = null;
1873
+ constructor(options = {}) {
1874
+ this.cacheTtlMs = resolveCatalogCacheTtlMs(options.cacheTtlMs);
1875
+ this.cacheKey = options.cacheKey ?? "default";
1876
+ this.loadOptions = {
1877
+ directCatalogUrl: options.directCatalogUrl,
1878
+ openRouterCatalogUrl: options.openRouterCatalogUrl,
1879
+ bundledOnly: options.bundledOnly,
1880
+ fetchTimeoutMs: options.fetchTimeoutMs
1881
+ };
1882
+ this.resolverOptions = options.resolverOptions;
1883
+ }
1884
+ isInstanceCacheValid() {
1885
+ return this.directModels !== null && this.openRouterModels !== null && Date.now() - this.loadedAt < this.cacheTtlMs;
1886
+ }
1887
+ applyLoaded(direct, openrouter) {
1888
+ this.directModels = direct;
1889
+ this.openRouterModels = openrouter;
1890
+ this.loadedAt = Date.now();
1891
+ writeCachedModels(`${this.cacheKey}:direct`, direct, this.cacheTtlMs);
1892
+ writeCachedModels(`${this.cacheKey}:openrouter`, openrouter, this.cacheTtlMs);
1893
+ }
1894
+ async ensureLoaded(forceRefresh = false) {
1895
+ if (!forceRefresh && this.isInstanceCacheValid()) return;
1896
+ if (this.loadPromise) return this.loadPromise;
1897
+ this.loadPromise = (async () => {
1898
+ const loaded = await loadCatalogSourcesCached(this.loadOptions, {
1899
+ cacheKey: this.cacheKey,
1900
+ ttlMs: this.cacheTtlMs,
1901
+ forceRefresh
1902
+ });
1903
+ this.applyLoaded(loaded.direct, loaded.openrouter);
1904
+ })().finally(() => {
1905
+ this.loadPromise = null;
1906
+ });
1907
+ await this.loadPromise;
1908
+ }
1909
+ mergedModels() {
1910
+ const map = /* @__PURE__ */ new Map();
1911
+ for (const r of this.directModels?.values() ?? []) {
1912
+ map.set(r.modelId, r);
1913
+ }
1914
+ for (const r of this.openRouterModels?.values() ?? []) {
1915
+ map.set(r.modelId, r);
1916
+ }
1917
+ return map;
1918
+ }
1919
+ catalogForProvider(provider) {
1920
+ if (isOpenRouterProvider(provider)) {
1921
+ return this.openRouterModels ?? /* @__PURE__ */ new Map();
1922
+ }
1923
+ return this.directModels ?? /* @__PURE__ */ new Map();
1924
+ }
1925
+ resolver(models, options) {
1926
+ return new ModelNameResolver(models, { ...this.resolverOptions, ...options });
1927
+ }
1928
+ async getAllModels() {
1929
+ await this.ensureLoaded();
1930
+ return this.mergedModels();
1931
+ }
1932
+ async getDirectModels() {
1933
+ await this.ensureLoaded();
1934
+ return new Map(this.directModels);
1935
+ }
1936
+ async getOpenRouterModels() {
1937
+ await this.ensureLoaded();
1938
+ return new Map(this.openRouterModels);
1939
+ }
1940
+ async resolveModel(input, options) {
1941
+ await this.ensureLoaded();
1942
+ const primary = this.catalogForProvider(input.provider);
1943
+ const secondary = isOpenRouterProvider(input.provider) ? this.directModels ?? /* @__PURE__ */ new Map() : this.openRouterModels ?? /* @__PURE__ */ new Map();
1944
+ let result = this.resolver(primary, options).resolve(input);
1945
+ if (result.found && result.record) return result;
1946
+ if (secondary.size > 0) {
1947
+ const fallback = this.resolver(secondary, options).resolve(input);
1948
+ if (fallback.found && fallback.record) return fallback;
1949
+ if (!result.found && fallback.found) result = fallback;
1950
+ }
1951
+ if (!result.found) {
1952
+ result = this.resolver(this.mergedModels(), options).resolve(input);
1953
+ }
1954
+ return result;
1955
+ }
1956
+ async getModel(modelId, provider, options) {
1957
+ const result = await this.resolveModel({ model: modelId, provider }, options);
1958
+ return result.found ? result.record : null;
1959
+ }
1960
+ /** Clear caches and fetch catalogs again immediately. */
1961
+ async refresh() {
1962
+ invalidateCatalogLoadCache(this.cacheKey);
1963
+ this.directModels = null;
1964
+ this.openRouterModels = null;
1965
+ this.loadedAt = 0;
1966
+ this.loadPromise = null;
1967
+ await this.ensureLoaded(true);
1968
+ }
1969
+ };
1970
+
1971
+ export {
1972
+ DEFAULT_CATALOG_CACHE_TTL_MS,
1973
+ resolveCatalogCacheTtlMs,
1974
+ DEFAULT_DIRECT_CATALOG_URL,
1975
+ DEFAULT_OPENROUTER_CATALOG_URL,
1976
+ canonicalCatalogModelId,
1977
+ normalizeX12iCatalogModel,
1978
+ modelsFromX12iCatalogFile,
1979
+ loadCatalogSources,
1980
+ readBundledCatalogFiles,
1981
+ invalidateCatalogLoadCache,
1982
+ loadCatalogSourcesCached,
1983
+ AiModelsCatalogClient
1984
+ };
1985
+ //# sourceMappingURL=chunk-OPN6BGNH.js.map