@funkai/models 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +34 -33
- package/CHANGELOG.md +36 -0
- package/dist/alibaba-B6q4Ng1R.mjs.map +1 -1
- package/dist/amazon-bedrock-Cv9AHQBH.mjs.map +1 -1
- package/dist/anthropic-yB7ST97_.mjs.map +1 -1
- package/dist/cerebras-COfl7XM-.mjs.map +1 -1
- package/dist/cohere-B7TgO0hT.mjs.map +1 -1
- package/dist/deepinfra-B0GxUwCG.mjs.map +1 -1
- package/dist/deepseek-D64ZEsvS.mjs.map +1 -1
- package/dist/fireworks-ai-DJYvdAi_.mjs.map +1 -1
- package/dist/google-BypRl349.mjs.map +1 -1
- package/dist/google-vertex-DbS-zTGD.mjs.map +1 -1
- package/dist/groq-ei_PerYi.mjs.map +1 -1
- package/dist/huggingface-DaM1EeLP.mjs.map +1 -1
- package/dist/inception-CspEzqNV.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +14 -2
- package/dist/index.mjs.map +1 -1
- package/dist/llama-Cf3-koap.mjs.map +1 -1
- package/dist/mistral-BI9MdAO4.mjs.map +1 -1
- package/dist/nvidia-COHacuoa.mjs.map +1 -1
- package/dist/openai-C0nCfZUq.mjs.map +1 -1
- package/dist/openrouter-DSFzxKQb.mjs.map +1 -1
- package/dist/perplexity-zeZ2WlBU.mjs.map +1 -1
- package/dist/providers/alibaba.d.mts +1 -1
- package/dist/providers/amazon-bedrock.d.mts +1 -1
- package/dist/providers/anthropic.d.mts +1 -1
- package/dist/providers/cerebras.d.mts +1 -1
- package/dist/providers/cohere.d.mts +1 -1
- package/dist/providers/deepinfra.d.mts +1 -1
- package/dist/providers/deepseek.d.mts +1 -1
- package/dist/providers/fireworks-ai.d.mts +1 -1
- package/dist/providers/google-vertex.d.mts +1 -1
- package/dist/providers/google.d.mts +1 -1
- package/dist/providers/groq.d.mts +1 -1
- package/dist/providers/huggingface.d.mts +1 -1
- package/dist/providers/inception.d.mts +1 -1
- package/dist/providers/llama.d.mts +1 -1
- package/dist/providers/mistral.d.mts +1 -1
- package/dist/providers/nvidia.d.mts +1 -1
- package/dist/providers/openai.d.mts +1 -1
- package/dist/providers/openrouter.d.mts +1 -1
- package/dist/providers/perplexity.d.mts +1 -1
- package/dist/providers/togetherai.d.mts +1 -1
- package/dist/providers/xai.d.mts +1 -1
- package/dist/togetherai-BvcxUfPE.mjs.map +1 -1
- package/dist/{types-DjdaZckF.d.mts → types-DIzolT_s.d.mts} +61 -21
- package/dist/types-DIzolT_s.d.mts.map +1 -0
- package/dist/xai-fSuAkQJo.mjs.map +1 -1
- package/package.json +6 -3
- package/scripts/generate-models.ts +147 -56
- package/src/catalog/index.test.ts +8 -8
- package/src/catalog/index.ts +5 -1
- package/src/catalog/providers/alibaba.ts +91 -91
- package/src/catalog/providers/amazon-bedrock.ts +205 -185
- package/src/catalog/providers/anthropic.ts +87 -62
- package/src/catalog/providers/cerebras.ts +9 -9
- package/src/catalog/providers/cohere.ts +16 -16
- package/src/catalog/providers/deepinfra.ts +71 -71
- package/src/catalog/providers/deepseek.ts +3 -3
- package/src/catalog/providers/fireworks-ai.ts +36 -36
- package/src/catalog/providers/google-vertex.ts +62 -62
- package/src/catalog/providers/google.ts +69 -69
- package/src/catalog/providers/groq.ts +24 -24
- package/src/catalog/providers/huggingface.ts +52 -52
- package/src/catalog/providers/inception.ts +9 -9
- package/src/catalog/providers/index.ts +1 -0
- package/src/catalog/providers/llama.ts +7 -7
- package/src/catalog/providers/mistral.ts +60 -60
- package/src/catalog/providers/nvidia.ts +84 -84
- package/src/catalog/providers/openai.ts +115 -115
- package/src/catalog/providers/openrouter.ts +448 -433
- package/src/catalog/providers/perplexity.ts +9 -9
- package/src/catalog/providers/togetherai.ts +47 -47
- package/src/catalog/providers/xai.ts +49 -49
- package/src/catalog/types.ts +60 -20
- package/src/cost/calculate.test.ts +11 -11
- package/src/provider/registry.ts +21 -2
- package/src/provider/types.ts +1 -1
- package/tsconfig.json +2 -1
- package/tsdown.config.ts +7 -3
- package/dist/types-DjdaZckF.d.mts.map +0 -1
|
@@ -53,29 +53,47 @@ interface ApiProvider {
|
|
|
53
53
|
/**
|
|
54
54
|
* Convert a provider key to a TypeScript constant name.
|
|
55
55
|
* e.g. "openai" → "OPENAI_MODELS", "meta-llama" → "META_LLAMA_MODELS"
|
|
56
|
+
*
|
|
57
|
+
* @private
|
|
56
58
|
*/
|
|
57
59
|
function toConstName(provider: string): string {
|
|
58
|
-
return `${provider.toUpperCase().
|
|
60
|
+
return `${provider.toUpperCase().replaceAll(/[^A-Z0-9]/g, "_")}_MODELS`;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
64
|
* Lowercase the first character of a string, preserving the rest as-is.
|
|
63
65
|
* e.g. "OpenAI" → "openAI", "GoogleVertex" → "googleVertex", "XAI" → "xAI"
|
|
66
|
+
*
|
|
67
|
+
* @private
|
|
64
68
|
*/
|
|
65
69
|
function lowerFirst(s: string): string {
|
|
66
|
-
|
|
70
|
+
if (s.length === 0) {
|
|
71
|
+
return s;
|
|
72
|
+
}
|
|
73
|
+
const [first] = s;
|
|
74
|
+
if (first === undefined) {
|
|
75
|
+
return s;
|
|
76
|
+
}
|
|
77
|
+
return first.toLowerCase() + s.slice(1);
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
/**
|
|
70
81
|
* Return the correct indefinite article ("a" or "an") for a word.
|
|
82
|
+
*
|
|
83
|
+
* @private
|
|
71
84
|
*/
|
|
72
85
|
function article(word: string): string {
|
|
73
|
-
|
|
86
|
+
if (/^[aeiou]/i.test(word)) {
|
|
87
|
+
return "an";
|
|
88
|
+
}
|
|
89
|
+
return "a";
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
/**
|
|
77
93
|
* Convert per-million-token rate to per-token rate, rounding to
|
|
78
94
|
* eliminate floating-point noise (e.g. `8.000000000000001e-7`).
|
|
95
|
+
*
|
|
96
|
+
* @private
|
|
79
97
|
*/
|
|
80
98
|
function toPerToken(perMillion: number): number {
|
|
81
99
|
return parseFloat((perMillion / 1_000_000).toPrecision(6));
|
|
@@ -84,29 +102,56 @@ function toPerToken(perMillion: number): number {
|
|
|
84
102
|
/**
|
|
85
103
|
* Format a number for codegen output, using scientific notation for
|
|
86
104
|
* very small values.
|
|
105
|
+
*
|
|
106
|
+
* @private
|
|
87
107
|
*/
|
|
88
108
|
function fmtNum(n: number): string {
|
|
89
|
-
if (n === 0)
|
|
90
|
-
|
|
109
|
+
if (n === 0) {
|
|
110
|
+
return "0";
|
|
111
|
+
}
|
|
112
|
+
if (n < 0.000_000_1) {
|
|
113
|
+
return n.toExponential();
|
|
114
|
+
}
|
|
91
115
|
return String(n);
|
|
92
116
|
}
|
|
93
117
|
|
|
118
|
+
/** @private */
|
|
119
|
+
function extractExampleId(model: ApiModel | undefined): string {
|
|
120
|
+
if (model !== undefined && model !== null) {
|
|
121
|
+
return model.id;
|
|
122
|
+
}
|
|
123
|
+
return "example-id";
|
|
124
|
+
}
|
|
125
|
+
|
|
94
126
|
/**
|
|
95
127
|
* Build the pricing object literal string for a model.
|
|
128
|
+
*
|
|
129
|
+
* @private
|
|
96
130
|
*/
|
|
131
|
+
function extractCostField(cost: ApiModel["cost"], field: "input" | "output"): number {
|
|
132
|
+
if (cost !== undefined && cost !== null) {
|
|
133
|
+
return cost[field] ?? 0;
|
|
134
|
+
}
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
97
138
|
function buildPricing(cost: ApiModel["cost"]): string {
|
|
98
|
-
const
|
|
99
|
-
const
|
|
139
|
+
const costInput = extractCostField(cost, "input");
|
|
140
|
+
const costOutput = extractCostField(cost, "output");
|
|
141
|
+
const input = toPerToken(costInput);
|
|
142
|
+
const output = toPerToken(costOutput);
|
|
100
143
|
const parts: string[] = [`input: ${fmtNum(input)}`, `output: ${fmtNum(output)}`];
|
|
101
144
|
|
|
102
|
-
if (cost
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
145
|
+
if (cost !== undefined && cost !== null) {
|
|
146
|
+
if (cost.cache_read !== undefined && cost.cache_read !== null && cost.cache_read > 0) {
|
|
147
|
+
parts.push(`cacheRead: ${fmtNum(toPerToken(cost.cache_read))}`);
|
|
148
|
+
}
|
|
149
|
+
if (cost.cache_write !== undefined && cost.cache_write !== null && cost.cache_write > 0) {
|
|
150
|
+
parts.push(`cacheWrite: ${fmtNum(toPerToken(cost.cache_write))}`);
|
|
151
|
+
}
|
|
152
|
+
if (cost.reasoning !== undefined && cost.reasoning !== null && cost.reasoning > 0) {
|
|
153
|
+
parts.push(`reasoning: ${fmtNum(toPerToken(cost.reasoning))}`);
|
|
154
|
+
}
|
|
110
155
|
}
|
|
111
156
|
|
|
112
157
|
return `{ ${parts.join(", ")} }`;
|
|
@@ -114,15 +159,31 @@ function buildPricing(cost: ApiModel["cost"]): string {
|
|
|
114
159
|
|
|
115
160
|
/**
|
|
116
161
|
* Build the modalities object literal string.
|
|
162
|
+
*
|
|
163
|
+
* @private
|
|
117
164
|
*/
|
|
165
|
+
function extractModalityField(
|
|
166
|
+
modalities: ApiModel["modalities"],
|
|
167
|
+
field: "input" | "output",
|
|
168
|
+
): string[] {
|
|
169
|
+
if (modalities !== undefined && modalities !== null) {
|
|
170
|
+
return modalities[field] ?? ["text"];
|
|
171
|
+
}
|
|
172
|
+
return ["text"];
|
|
173
|
+
}
|
|
174
|
+
|
|
118
175
|
function buildModalities(modalities: ApiModel["modalities"]): string {
|
|
119
|
-
const
|
|
120
|
-
const
|
|
176
|
+
const modalInput = extractModalityField(modalities, "input");
|
|
177
|
+
const modalOutput = extractModalityField(modalities, "output");
|
|
178
|
+
const input = JSON.stringify(modalInput);
|
|
179
|
+
const output = JSON.stringify(modalOutput);
|
|
121
180
|
return `{ input: ${input}, output: ${output} }`;
|
|
122
181
|
}
|
|
123
182
|
|
|
124
183
|
/**
|
|
125
184
|
* Build the capabilities object literal string.
|
|
185
|
+
*
|
|
186
|
+
* @private
|
|
126
187
|
*/
|
|
127
188
|
function buildCapabilities(m: ApiModel): string {
|
|
128
189
|
return [
|
|
@@ -133,19 +194,44 @@ function buildCapabilities(m: ApiModel): string {
|
|
|
133
194
|
].join(", ");
|
|
134
195
|
}
|
|
135
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Extract context window and max output from a model's limit field.
|
|
199
|
+
*
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
function getModelLimits(limit: ApiModel["limit"]): { contextWindow: number; maxOutput: number } {
|
|
203
|
+
if (limit === undefined || limit === null) {
|
|
204
|
+
return { contextWindow: 0, maxOutput: 0 };
|
|
205
|
+
}
|
|
206
|
+
const contextWindow = limit.context ?? 0;
|
|
207
|
+
const maxOutput = limit.output ?? 0;
|
|
208
|
+
return { contextWindow, maxOutput };
|
|
209
|
+
}
|
|
210
|
+
|
|
136
211
|
/**
|
|
137
212
|
* Escape a string for use in a TypeScript single-quoted string literal.
|
|
213
|
+
*
|
|
214
|
+
* @private
|
|
138
215
|
*/
|
|
139
216
|
function escapeStr(s: string): string {
|
|
140
|
-
return s
|
|
217
|
+
return s
|
|
218
|
+
.replaceAll("\\", String.raw`\\`)
|
|
219
|
+
.replaceAll("'", String.raw`\'`)
|
|
220
|
+
.replaceAll("\n", String.raw`\n`)
|
|
221
|
+
.replaceAll("\r", String.raw`\r`);
|
|
141
222
|
}
|
|
142
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Check whether the staleness cache file indicates a recent fetch.
|
|
226
|
+
*
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
143
229
|
function isFresh(reqPath: string): boolean {
|
|
144
230
|
if (!existsSync(reqPath)) {
|
|
145
231
|
return false;
|
|
146
232
|
}
|
|
147
233
|
try {
|
|
148
|
-
const timestamp = readFileSync(reqPath, "
|
|
234
|
+
const timestamp = readFileSync(reqPath, "utf8").trim();
|
|
149
235
|
const lastRun = new Date(timestamp).getTime();
|
|
150
236
|
return Date.now() - lastRun < STALE_MS;
|
|
151
237
|
} catch {
|
|
@@ -175,7 +261,7 @@ export default lauf({
|
|
|
175
261
|
|
|
176
262
|
// Read provider config
|
|
177
263
|
const providers: Record<string, ProviderEntry> = JSON.parse(
|
|
178
|
-
readFileSync(PROVIDERS_PATH, "
|
|
264
|
+
readFileSync(PROVIDERS_PATH, "utf8"),
|
|
179
265
|
);
|
|
180
266
|
const providerKeys = Object.keys(providers);
|
|
181
267
|
|
|
@@ -210,30 +296,30 @@ export default lauf({
|
|
|
210
296
|
rmSync(ENTRY_DIR, { recursive: true, force: true });
|
|
211
297
|
mkdirSync(ENTRY_DIR, { recursive: true });
|
|
212
298
|
|
|
213
|
-
const providerFiles
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
299
|
+
const providerFiles = providerKeys.flatMap((providerKey) => {
|
|
300
|
+
const apiProviderEntry = apiData[providerKey];
|
|
301
|
+
const providerEntry = providers[providerKey];
|
|
302
|
+
if (apiProviderEntry === undefined || providerEntry === undefined) {
|
|
303
|
+
return [];
|
|
304
|
+
}
|
|
305
|
+
if (apiProviderEntry.models === undefined || apiProviderEntry.models === null) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`models.dev API returned no models for configured provider: ${providerKey}`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const apiModels = apiProviderEntry.models;
|
|
218
311
|
const constName = toConstName(providerKey);
|
|
219
|
-
const lines
|
|
220
|
-
|
|
221
|
-
for (const [, m] of Object.entries(apiModels)) {
|
|
312
|
+
const lines = Object.values(apiModels).map((m) => {
|
|
222
313
|
const id = escapeStr(m.id);
|
|
223
314
|
const name = escapeStr(m.name ?? m.id);
|
|
224
315
|
const family = escapeStr(m.family ?? "");
|
|
225
316
|
const pricing = buildPricing(m.cost);
|
|
226
|
-
const contextWindow = m.limit
|
|
227
|
-
const maxOutput = m.limit?.output ?? 0;
|
|
317
|
+
const { contextWindow, maxOutput } = getModelLimits(m.limit);
|
|
228
318
|
const modalities = buildModalities(m.modalities);
|
|
229
319
|
const capabilities = buildCapabilities(m);
|
|
230
320
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
`pricing: ${pricing}, contextWindow: ${contextWindow}, maxOutput: ${maxOutput}, ` +
|
|
234
|
-
`modalities: ${modalities}, capabilities: { ${capabilities} } },`,
|
|
235
|
-
);
|
|
236
|
-
}
|
|
321
|
+
return ` { id: '${id}', name: '${name}', provider: '${providerKey}', family: '${family}', pricing: ${pricing}, contextWindow: ${contextWindow}, maxOutput: ${maxOutput}, modalities: ${modalities}, capabilities: { ${capabilities} } },`;
|
|
322
|
+
});
|
|
237
323
|
|
|
238
324
|
// Write catalog provider file
|
|
239
325
|
const catalogContent = `${BANNER}
|
|
@@ -246,14 +332,15 @@ ${lines.join("\n")}
|
|
|
246
332
|
`;
|
|
247
333
|
|
|
248
334
|
const catalogPath = join(CATALOG_DIR, `${providerKey}.ts`);
|
|
249
|
-
writeFileSync(catalogPath, catalogContent, "
|
|
335
|
+
writeFileSync(catalogPath, catalogContent, "utf8");
|
|
250
336
|
|
|
251
337
|
// Write per-provider entry point
|
|
252
|
-
const prefix =
|
|
338
|
+
const { prefix } = providerEntry;
|
|
253
339
|
const camel = lowerFirst(prefix);
|
|
254
|
-
const
|
|
255
|
-
const
|
|
256
|
-
const
|
|
340
|
+
const [firstModel] = Object.values(apiModels);
|
|
341
|
+
const exampleId = escapeStr(extractExampleId(firstModel));
|
|
342
|
+
const providerName = escapeStr(providerEntry.name);
|
|
343
|
+
const art = article(providerEntry.name);
|
|
257
344
|
const entryContent = `${BANNER}
|
|
258
345
|
|
|
259
346
|
import type { LiteralUnion } from 'type-fest'
|
|
@@ -286,6 +373,7 @@ export type ${prefix}ModelId = (typeof ${constName})[number]['id']
|
|
|
286
373
|
*/
|
|
287
374
|
export const ${camel}Models = ${constName}
|
|
288
375
|
|
|
376
|
+
/** @private */
|
|
289
377
|
const MODEL_INDEX = new Map<string, ModelDefinition>(${constName}.map((m) => [m.id, m]))
|
|
290
378
|
|
|
291
379
|
/**
|
|
@@ -310,11 +398,11 @@ export function ${camel}Model(id: LiteralUnion<${prefix}ModelId, string>): Model
|
|
|
310
398
|
`;
|
|
311
399
|
|
|
312
400
|
const entryPath = join(ENTRY_DIR, `${providerKey}.ts`);
|
|
313
|
-
writeFileSync(entryPath, entryContent, "
|
|
401
|
+
writeFileSync(entryPath, entryContent, "utf8");
|
|
314
402
|
|
|
315
403
|
ctx.logger.success(`${providerKey} (${lines.length} models)`);
|
|
316
|
-
|
|
317
|
-
}
|
|
404
|
+
return [{ provider: providerKey, constName, count: lines.length }];
|
|
405
|
+
});
|
|
318
406
|
|
|
319
407
|
// Catalog barrel
|
|
320
408
|
const imports = providerFiles
|
|
@@ -325,6 +413,7 @@ export function ${camel}Model(id: LiteralUnion<${prefix}ModelId, string>): Model
|
|
|
325
413
|
|
|
326
414
|
const catalogBarrel = `${BANNER}
|
|
327
415
|
|
|
416
|
+
// oxlint-disable eslint-plugin-import/max-dependencies
|
|
328
417
|
import type { ModelDefinition } from '../types.js'
|
|
329
418
|
${imports}
|
|
330
419
|
|
|
@@ -333,16 +422,16 @@ ${spreads}
|
|
|
333
422
|
] as const satisfies readonly ModelDefinition[]
|
|
334
423
|
`;
|
|
335
424
|
|
|
336
|
-
writeFileSync(join(CATALOG_DIR, "index.ts"), catalogBarrel, "
|
|
425
|
+
writeFileSync(join(CATALOG_DIR, "index.ts"), catalogBarrel, "utf8");
|
|
337
426
|
ctx.logger.success("catalog/providers/index.ts (barrel)");
|
|
338
427
|
|
|
339
428
|
// Write generated entries list for tsdown config
|
|
340
429
|
const entryPoints = providerFiles.map((p) => `src/providers/${p.provider}.ts`);
|
|
341
|
-
writeFileSync(ENTRIES_PATH, JSON.stringify(entryPoints, null, 2), "
|
|
430
|
+
writeFileSync(ENTRIES_PATH, JSON.stringify(entryPoints, null, 2), "utf8");
|
|
342
431
|
ctx.logger.success(".generated/entries.json");
|
|
343
432
|
|
|
344
433
|
// Update package.json exports map
|
|
345
|
-
const pkgRaw = readFileSync(PACKAGE_JSON_PATH, "
|
|
434
|
+
const pkgRaw = readFileSync(PACKAGE_JSON_PATH, "utf8");
|
|
346
435
|
const pkg = JSON.parse(pkgRaw);
|
|
347
436
|
|
|
348
437
|
const exportsMap: Record<string, { types: string; import: string }> = {
|
|
@@ -350,21 +439,23 @@ ${spreads}
|
|
|
350
439
|
types: "./dist/index.d.mts",
|
|
351
440
|
import: "./dist/index.mjs",
|
|
352
441
|
},
|
|
442
|
+
...Object.fromEntries(
|
|
443
|
+
providerFiles.map((p) => [
|
|
444
|
+
`./${p.provider}`,
|
|
445
|
+
{
|
|
446
|
+
types: `./dist/providers/${p.provider}.d.mts`,
|
|
447
|
+
import: `./dist/providers/${p.provider}.mjs`,
|
|
448
|
+
},
|
|
449
|
+
]),
|
|
450
|
+
),
|
|
353
451
|
};
|
|
354
452
|
|
|
355
|
-
for (const p of providerFiles) {
|
|
356
|
-
exportsMap[`./${p.provider}`] = {
|
|
357
|
-
types: `./dist/providers/${p.provider}.d.mts`,
|
|
358
|
-
import: `./dist/providers/${p.provider}.mjs`,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
453
|
pkg.exports = exportsMap;
|
|
363
|
-
writeFileSync(PACKAGE_JSON_PATH, JSON.stringify(pkg, null, 2)
|
|
454
|
+
writeFileSync(PACKAGE_JSON_PATH, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
364
455
|
ctx.logger.success("package.json exports map updated");
|
|
365
456
|
|
|
366
457
|
// Staleness timestamp
|
|
367
|
-
writeFileSync(REQ_PATH, new Date().toISOString(), "
|
|
458
|
+
writeFileSync(REQ_PATH, new Date().toISOString(), "utf8");
|
|
368
459
|
|
|
369
460
|
const totalModels = providerFiles.reduce((sum, p) => sum + p.count, 0);
|
|
370
461
|
ctx.logger.info(`done (${providerFiles.length} providers, ${totalModels} models)`);
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
|
|
3
3
|
import { model, models, MODELS } from "@/catalog/index.js";
|
|
4
4
|
|
|
5
|
-
describe("MODELS", () => {
|
|
5
|
+
describe("MODELS catalog", () => {
|
|
6
6
|
it("is a non-empty array", () => {
|
|
7
7
|
expect(MODELS.length).toBeGreaterThan(0);
|
|
8
8
|
});
|
|
@@ -14,8 +14,8 @@ describe("MODELS", () => {
|
|
|
14
14
|
expect(typeof m.provider).toBe("string");
|
|
15
15
|
expect(typeof m.pricing.input).toBe("number");
|
|
16
16
|
expect(typeof m.pricing.output).toBe("number");
|
|
17
|
-
expect(Array.isArray(m.modalities.input)).
|
|
18
|
-
expect(Array.isArray(m.modalities.output)).
|
|
17
|
+
expect(Array.isArray(m.modalities.input)).toBeTruthy();
|
|
18
|
+
expect(Array.isArray(m.modalities.output)).toBeTruthy();
|
|
19
19
|
expect(typeof m.capabilities.reasoning).toBe("boolean");
|
|
20
20
|
}
|
|
21
21
|
});
|
|
@@ -30,7 +30,7 @@ describe("MODELS", () => {
|
|
|
30
30
|
const seen = new Map<string, Set<string>>();
|
|
31
31
|
for (const m of MODELS) {
|
|
32
32
|
const providerSet = seen.get(m.provider) ?? new Set<string>();
|
|
33
|
-
expect(providerSet.has(m.id)).
|
|
33
|
+
expect(providerSet.has(m.id)).toBeFalsy();
|
|
34
34
|
providerSet.add(m.id);
|
|
35
35
|
seen.set(m.provider, providerSet);
|
|
36
36
|
}
|
|
@@ -58,7 +58,7 @@ describe("model()", () => {
|
|
|
58
58
|
const result = model("o1");
|
|
59
59
|
|
|
60
60
|
expect(result).not.toBeNull();
|
|
61
|
-
expect(result!.capabilities.reasoning).
|
|
61
|
+
expect(result!.capabilities.reasoning).toBeTruthy();
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
it("returns model with correct modalities", () => {
|
|
@@ -82,7 +82,7 @@ describe("models()", () => {
|
|
|
82
82
|
|
|
83
83
|
expect(reasoningModels.length).toBeGreaterThan(0);
|
|
84
84
|
for (const m of reasoningModels) {
|
|
85
|
-
expect(m.capabilities.reasoning).
|
|
85
|
+
expect(m.capabilities.reasoning).toBeTruthy();
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
|
|
@@ -102,11 +102,11 @@ describe("models()", () => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it("supports arbitrary filter predicates", () => {
|
|
105
|
-
const result = models((m) => m.pricing.input > 0.
|
|
105
|
+
const result = models((m) => m.pricing.input > 0.000_001);
|
|
106
106
|
|
|
107
107
|
expect(result.length).toBeGreaterThan(0);
|
|
108
108
|
for (const m of result) {
|
|
109
|
-
expect(m.pricing.input).toBeGreaterThan(0.
|
|
109
|
+
expect(m.pricing.input).toBeGreaterThan(0.000_001);
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
112
|
});
|
package/src/catalog/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type ModelId = LiteralUnion<KnownModelId, string>;
|
|
|
23
23
|
*/
|
|
24
24
|
export const MODELS = GENERATED_MODELS satisfies readonly ModelDefinition[];
|
|
25
25
|
|
|
26
|
+
/** @private */
|
|
26
27
|
const MODEL_INDEX = new Map<string, ModelDefinition>(MODELS.map((m) => [m.id, m]));
|
|
27
28
|
|
|
28
29
|
/**
|
|
@@ -61,5 +62,8 @@ export function model(id: ModelId): ModelDefinition | null {
|
|
|
61
62
|
* ```
|
|
62
63
|
*/
|
|
63
64
|
export function models(filter?: (m: ModelDefinition) => boolean): readonly ModelDefinition[] {
|
|
64
|
-
|
|
65
|
+
if (filter) {
|
|
66
|
+
return MODELS.filter(filter);
|
|
67
|
+
}
|
|
68
|
+
return MODELS;
|
|
65
69
|
}
|