@funkai/models 0.2.1 → 0.3.1
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 +35 -36
- package/CHANGELOG.md +18 -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 +41 -87
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +20 -115
- 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 +7 -6
- package/scripts/generate-models.ts +152 -83
- package/src/catalog/index.test.ts +8 -20
- package/src/catalog/index.ts +4 -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 -19
- package/src/index.ts +2 -8
- package/src/provider/index.ts +2 -8
- package/src/provider/registry.test.ts +87 -0
- package/src/provider/registry.ts +93 -0
- package/src/provider/types.ts +1 -1
- package/tsconfig.json +2 -1
- package/tsdown.config.ts +7 -4
- package/dist/types-DjdaZckF.d.mts.map +0 -1
- package/src/provider/openrouter.test.ts +0 -125
- package/src/provider/openrouter.ts +0 -110
- package/src/provider/resolver.test.ts +0 -138
- package/src/provider/resolver.ts +0 -125
|
@@ -6,10 +6,6 @@ import { lauf, z } from "laufen";
|
|
|
6
6
|
const API_URL = "https://models.dev/api.json";
|
|
7
7
|
const STALE_MS = 24 * 60 * 60 * 1000;
|
|
8
8
|
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Banner
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
|
|
13
9
|
const BANNER = `// ──────────────────────────────────────────────────────────────
|
|
14
10
|
// ███████╗██╗ ██╗███╗ ██╗██╗ ██╗ █████╗ ██╗
|
|
15
11
|
// ██╔════╝██║ ██║████╗ ██║██║ ██╔╝██╔══██╗██║
|
|
@@ -23,10 +19,6 @@ const BANNER = `// ────────────────────
|
|
|
23
19
|
// Update: pnpm --filter=@funkai/models generate:models
|
|
24
20
|
// ──────────────────────────────────────────────────────────────`;
|
|
25
21
|
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Types
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
30
22
|
interface ProviderEntry {
|
|
31
23
|
name: string;
|
|
32
24
|
prefix: string;
|
|
@@ -58,16 +50,12 @@ interface ApiProvider {
|
|
|
58
50
|
models: Record<string, ApiModel>;
|
|
59
51
|
}
|
|
60
52
|
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// Helpers
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
|
|
65
53
|
/**
|
|
66
54
|
* Convert a provider key to a TypeScript constant name.
|
|
67
55
|
* e.g. "openai" → "OPENAI_MODELS", "meta-llama" → "META_LLAMA_MODELS"
|
|
68
56
|
*/
|
|
69
57
|
function toConstName(provider: string): string {
|
|
70
|
-
return `${provider.toUpperCase().
|
|
58
|
+
return `${provider.toUpperCase().replaceAll(/[^A-Z0-9]/g, "_")}_MODELS`;
|
|
71
59
|
}
|
|
72
60
|
|
|
73
61
|
/**
|
|
@@ -75,14 +63,24 @@ function toConstName(provider: string): string {
|
|
|
75
63
|
* e.g. "OpenAI" → "openAI", "GoogleVertex" → "googleVertex", "XAI" → "xAI"
|
|
76
64
|
*/
|
|
77
65
|
function lowerFirst(s: string): string {
|
|
78
|
-
|
|
66
|
+
if (s.length === 0) {
|
|
67
|
+
return s;
|
|
68
|
+
}
|
|
69
|
+
const [first] = s;
|
|
70
|
+
if (first === undefined) {
|
|
71
|
+
return s;
|
|
72
|
+
}
|
|
73
|
+
return first.toLowerCase() + s.slice(1);
|
|
79
74
|
}
|
|
80
75
|
|
|
81
76
|
/**
|
|
82
77
|
* Return the correct indefinite article ("a" or "an") for a word.
|
|
83
78
|
*/
|
|
84
79
|
function article(word: string): string {
|
|
85
|
-
|
|
80
|
+
if (/^[aeiou]/i.test(word)) {
|
|
81
|
+
return "an";
|
|
82
|
+
}
|
|
83
|
+
return "a";
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
/**
|
|
@@ -98,8 +96,12 @@ function toPerToken(perMillion: number): number {
|
|
|
98
96
|
* very small values.
|
|
99
97
|
*/
|
|
100
98
|
function fmtNum(n: number): string {
|
|
101
|
-
if (n === 0)
|
|
102
|
-
|
|
99
|
+
if (n === 0) {
|
|
100
|
+
return "0";
|
|
101
|
+
}
|
|
102
|
+
if (n < 0.000_000_1) {
|
|
103
|
+
return n.toExponential();
|
|
104
|
+
}
|
|
103
105
|
return String(n);
|
|
104
106
|
}
|
|
105
107
|
|
|
@@ -107,18 +109,32 @@ function fmtNum(n: number): string {
|
|
|
107
109
|
* Build the pricing object literal string for a model.
|
|
108
110
|
*/
|
|
109
111
|
function buildPricing(cost: ApiModel["cost"]): string {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
+
const costInput: number = (() => {
|
|
113
|
+
if (cost !== undefined && cost !== null && cost.input !== undefined && cost.input !== null) {
|
|
114
|
+
return cost.input;
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
})();
|
|
118
|
+
const costOutput: number = (() => {
|
|
119
|
+
if (cost !== undefined && cost !== null && cost.output !== undefined && cost.output !== null) {
|
|
120
|
+
return cost.output;
|
|
121
|
+
}
|
|
122
|
+
return 0;
|
|
123
|
+
})();
|
|
124
|
+
const input = toPerToken(costInput);
|
|
125
|
+
const output = toPerToken(costOutput);
|
|
112
126
|
const parts: string[] = [`input: ${fmtNum(input)}`, `output: ${fmtNum(output)}`];
|
|
113
127
|
|
|
114
|
-
if (cost
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
if (cost !== undefined && cost !== null) {
|
|
129
|
+
if (cost.cache_read !== undefined && cost.cache_read !== null && cost.cache_read > 0) {
|
|
130
|
+
parts.push(`cacheRead: ${fmtNum(toPerToken(cost.cache_read))}`);
|
|
131
|
+
}
|
|
132
|
+
if (cost.cache_write !== undefined && cost.cache_write !== null && cost.cache_write > 0) {
|
|
133
|
+
parts.push(`cacheWrite: ${fmtNum(toPerToken(cost.cache_write))}`);
|
|
134
|
+
}
|
|
135
|
+
if (cost.reasoning !== undefined && cost.reasoning !== null && cost.reasoning > 0) {
|
|
136
|
+
parts.push(`reasoning: ${fmtNum(toPerToken(cost.reasoning))}`);
|
|
137
|
+
}
|
|
122
138
|
}
|
|
123
139
|
|
|
124
140
|
return `{ ${parts.join(", ")} }`;
|
|
@@ -128,8 +144,30 @@ function buildPricing(cost: ApiModel["cost"]): string {
|
|
|
128
144
|
* Build the modalities object literal string.
|
|
129
145
|
*/
|
|
130
146
|
function buildModalities(modalities: ApiModel["modalities"]): string {
|
|
131
|
-
const
|
|
132
|
-
|
|
147
|
+
const modalInput: string[] = (() => {
|
|
148
|
+
if (
|
|
149
|
+
modalities !== undefined &&
|
|
150
|
+
modalities !== null &&
|
|
151
|
+
modalities.input !== undefined &&
|
|
152
|
+
modalities.input !== null
|
|
153
|
+
) {
|
|
154
|
+
return modalities.input;
|
|
155
|
+
}
|
|
156
|
+
return ["text"];
|
|
157
|
+
})();
|
|
158
|
+
const modalOutput: string[] = (() => {
|
|
159
|
+
if (
|
|
160
|
+
modalities !== undefined &&
|
|
161
|
+
modalities !== null &&
|
|
162
|
+
modalities.output !== undefined &&
|
|
163
|
+
modalities.output !== null
|
|
164
|
+
) {
|
|
165
|
+
return modalities.output;
|
|
166
|
+
}
|
|
167
|
+
return ["text"];
|
|
168
|
+
})();
|
|
169
|
+
const input = JSON.stringify(modalInput);
|
|
170
|
+
const output = JSON.stringify(modalOutput);
|
|
133
171
|
return `{ input: ${input}, output: ${output} }`;
|
|
134
172
|
}
|
|
135
173
|
|
|
@@ -145,23 +183,45 @@ function buildCapabilities(m: ApiModel): string {
|
|
|
145
183
|
].join(", ");
|
|
146
184
|
}
|
|
147
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Extract context window and max output from a model's limit field.
|
|
188
|
+
*/
|
|
189
|
+
function getModelLimits(limit: ApiModel["limit"]): { contextWindow: number; maxOutput: number } {
|
|
190
|
+
if (limit === undefined || limit === null) {
|
|
191
|
+
return { contextWindow: 0, maxOutput: 0 };
|
|
192
|
+
}
|
|
193
|
+
const contextWindow: number = (() => {
|
|
194
|
+
if (limit.context !== undefined && limit.context !== null) {
|
|
195
|
+
return limit.context;
|
|
196
|
+
}
|
|
197
|
+
return 0;
|
|
198
|
+
})();
|
|
199
|
+
const maxOutput: number = (() => {
|
|
200
|
+
if (limit.output !== undefined && limit.output !== null) {
|
|
201
|
+
return limit.output;
|
|
202
|
+
}
|
|
203
|
+
return 0;
|
|
204
|
+
})();
|
|
205
|
+
return { contextWindow, maxOutput };
|
|
206
|
+
}
|
|
207
|
+
|
|
148
208
|
/**
|
|
149
209
|
* Escape a string for use in a TypeScript single-quoted string literal.
|
|
150
210
|
*/
|
|
151
211
|
function escapeStr(s: string): string {
|
|
152
|
-
return s
|
|
212
|
+
return s
|
|
213
|
+
.replaceAll("\\", String.raw`\\`)
|
|
214
|
+
.replaceAll("'", String.raw`\'`)
|
|
215
|
+
.replaceAll("\n", String.raw`\n`)
|
|
216
|
+
.replaceAll("\r", String.raw`\r`);
|
|
153
217
|
}
|
|
154
218
|
|
|
155
|
-
// ---------------------------------------------------------------------------
|
|
156
|
-
// Staleness check
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
|
|
159
219
|
function isFresh(reqPath: string): boolean {
|
|
160
220
|
if (!existsSync(reqPath)) {
|
|
161
221
|
return false;
|
|
162
222
|
}
|
|
163
223
|
try {
|
|
164
|
-
const timestamp = readFileSync(reqPath, "
|
|
224
|
+
const timestamp = readFileSync(reqPath, "utf8").trim();
|
|
165
225
|
const lastRun = new Date(timestamp).getTime();
|
|
166
226
|
return Date.now() - lastRun < STALE_MS;
|
|
167
227
|
} catch {
|
|
@@ -169,10 +229,6 @@ function isFresh(reqPath: string): boolean {
|
|
|
169
229
|
}
|
|
170
230
|
}
|
|
171
231
|
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
// Script
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
|
|
176
232
|
export default lauf({
|
|
177
233
|
description: "Fetch model data from models.dev and generate TypeScript catalog files",
|
|
178
234
|
args: {
|
|
@@ -195,7 +251,7 @@ export default lauf({
|
|
|
195
251
|
|
|
196
252
|
// Read provider config
|
|
197
253
|
const providers: Record<string, ProviderEntry> = JSON.parse(
|
|
198
|
-
readFileSync(PROVIDERS_PATH, "
|
|
254
|
+
readFileSync(PROVIDERS_PATH, "utf8"),
|
|
199
255
|
);
|
|
200
256
|
const providerKeys = Object.keys(providers);
|
|
201
257
|
|
|
@@ -233,30 +289,34 @@ export default lauf({
|
|
|
233
289
|
const providerFiles: { provider: string; constName: string; count: number }[] = [];
|
|
234
290
|
|
|
235
291
|
for (const providerKey of providerKeys) {
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
292
|
+
const apiProviderEntry = apiData[providerKey];
|
|
293
|
+
const providerEntry = providers[providerKey];
|
|
294
|
+
if (apiProviderEntry !== undefined && providerEntry !== undefined) {
|
|
295
|
+
if (apiProviderEntry.models === undefined || apiProviderEntry.models === null) {
|
|
296
|
+
throw new Error(
|
|
297
|
+
`models.dev API returned no models for configured provider: ${providerKey}`,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
const apiModels = apiProviderEntry.models;
|
|
301
|
+
const constName = toConstName(providerKey);
|
|
302
|
+
const lines: string[] = [];
|
|
303
|
+
|
|
304
|
+
for (const [, m] of Object.entries(apiModels)) {
|
|
305
|
+
const id = escapeStr(m.id);
|
|
306
|
+
const name = escapeStr(m.name ?? m.id);
|
|
307
|
+
const family = escapeStr(m.family ?? "");
|
|
308
|
+
const pricing = buildPricing(m.cost);
|
|
309
|
+
const { contextWindow, maxOutput } = getModelLimits(m.limit);
|
|
310
|
+
const modalities = buildModalities(m.modalities);
|
|
311
|
+
const capabilities = buildCapabilities(m);
|
|
312
|
+
|
|
313
|
+
lines.push(
|
|
314
|
+
` { id: '${id}', name: '${name}', provider: '${providerKey}', family: '${family}', pricing: ${pricing}, contextWindow: ${contextWindow}, maxOutput: ${maxOutput}, modalities: ${modalities}, capabilities: { ${capabilities} } },`,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Write catalog provider file
|
|
319
|
+
const catalogContent = `${BANNER}
|
|
260
320
|
|
|
261
321
|
import type { ModelDefinition } from '../types.js'
|
|
262
322
|
|
|
@@ -265,16 +325,24 @@ ${lines.join("\n")}
|
|
|
265
325
|
] as const satisfies readonly ModelDefinition[]
|
|
266
326
|
`;
|
|
267
327
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
328
|
+
const catalogPath = join(CATALOG_DIR, `${providerKey}.ts`);
|
|
329
|
+
writeFileSync(catalogPath, catalogContent, "utf8");
|
|
330
|
+
|
|
331
|
+
// Write per-provider entry point
|
|
332
|
+
const { prefix } = providerEntry;
|
|
333
|
+
const camel = lowerFirst(prefix);
|
|
334
|
+
const [firstModel] = Object.values(apiModels);
|
|
335
|
+
const exampleId = escapeStr(
|
|
336
|
+
(() => {
|
|
337
|
+
if (firstModel !== undefined) {
|
|
338
|
+
return firstModel.id;
|
|
339
|
+
}
|
|
340
|
+
return "example-id";
|
|
341
|
+
})(),
|
|
342
|
+
);
|
|
343
|
+
const providerName = escapeStr(providerEntry.name);
|
|
344
|
+
const art = article(providerEntry.name);
|
|
345
|
+
const entryContent = `${BANNER}
|
|
278
346
|
|
|
279
347
|
import type { LiteralUnion } from 'type-fest'
|
|
280
348
|
import type { ModelDefinition } from '../catalog/types.js'
|
|
@@ -329,11 +397,12 @@ export function ${camel}Model(id: LiteralUnion<${prefix}ModelId, string>): Model
|
|
|
329
397
|
}
|
|
330
398
|
`;
|
|
331
399
|
|
|
332
|
-
|
|
333
|
-
|
|
400
|
+
const entryPath = join(ENTRY_DIR, `${providerKey}.ts`);
|
|
401
|
+
writeFileSync(entryPath, entryContent, "utf8");
|
|
334
402
|
|
|
335
|
-
|
|
336
|
-
|
|
403
|
+
ctx.logger.success(`${providerKey} (${lines.length} models)`);
|
|
404
|
+
providerFiles.push({ provider: providerKey, constName, count: lines.length });
|
|
405
|
+
}
|
|
337
406
|
}
|
|
338
407
|
|
|
339
408
|
// Catalog barrel
|
|
@@ -353,16 +422,16 @@ ${spreads}
|
|
|
353
422
|
] as const satisfies readonly ModelDefinition[]
|
|
354
423
|
`;
|
|
355
424
|
|
|
356
|
-
writeFileSync(join(CATALOG_DIR, "index.ts"), catalogBarrel, "
|
|
425
|
+
writeFileSync(join(CATALOG_DIR, "index.ts"), catalogBarrel, "utf8");
|
|
357
426
|
ctx.logger.success("catalog/providers/index.ts (barrel)");
|
|
358
427
|
|
|
359
428
|
// Write generated entries list for tsdown config
|
|
360
429
|
const entryPoints = providerFiles.map((p) => `src/providers/${p.provider}.ts`);
|
|
361
|
-
writeFileSync(ENTRIES_PATH, JSON.stringify(entryPoints, null, 2), "
|
|
430
|
+
writeFileSync(ENTRIES_PATH, JSON.stringify(entryPoints, null, 2), "utf8");
|
|
362
431
|
ctx.logger.success(".generated/entries.json");
|
|
363
432
|
|
|
364
433
|
// Update package.json exports map
|
|
365
|
-
const pkgRaw = readFileSync(PACKAGE_JSON_PATH, "
|
|
434
|
+
const pkgRaw = readFileSync(PACKAGE_JSON_PATH, "utf8");
|
|
366
435
|
const pkg = JSON.parse(pkgRaw);
|
|
367
436
|
|
|
368
437
|
const exportsMap: Record<string, { types: string; import: string }> = {
|
|
@@ -380,11 +449,11 @@ ${spreads}
|
|
|
380
449
|
}
|
|
381
450
|
|
|
382
451
|
pkg.exports = exportsMap;
|
|
383
|
-
writeFileSync(PACKAGE_JSON_PATH, JSON.stringify(pkg, null, 2)
|
|
452
|
+
writeFileSync(PACKAGE_JSON_PATH, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
384
453
|
ctx.logger.success("package.json exports map updated");
|
|
385
454
|
|
|
386
455
|
// Staleness timestamp
|
|
387
|
-
writeFileSync(REQ_PATH, new Date().toISOString(), "
|
|
456
|
+
writeFileSync(REQ_PATH, new Date().toISOString(), "utf8");
|
|
388
457
|
|
|
389
458
|
const totalModels = providerFiles.reduce((sum, p) => sum + p.count, 0);
|
|
390
459
|
ctx.logger.info(`done (${providerFiles.length} providers, ${totalModels} models)`);
|
|
@@ -2,11 +2,7 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
|
|
3
3
|
import { model, models, MODELS } from "@/catalog/index.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
// MODELS constant
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
describe("MODELS", () => {
|
|
5
|
+
describe("MODELS catalog", () => {
|
|
10
6
|
it("is a non-empty array", () => {
|
|
11
7
|
expect(MODELS.length).toBeGreaterThan(0);
|
|
12
8
|
});
|
|
@@ -18,8 +14,8 @@ describe("MODELS", () => {
|
|
|
18
14
|
expect(typeof m.provider).toBe("string");
|
|
19
15
|
expect(typeof m.pricing.input).toBe("number");
|
|
20
16
|
expect(typeof m.pricing.output).toBe("number");
|
|
21
|
-
expect(Array.isArray(m.modalities.input)).
|
|
22
|
-
expect(Array.isArray(m.modalities.output)).
|
|
17
|
+
expect(Array.isArray(m.modalities.input)).toBeTruthy();
|
|
18
|
+
expect(Array.isArray(m.modalities.output)).toBeTruthy();
|
|
23
19
|
expect(typeof m.capabilities.reasoning).toBe("boolean");
|
|
24
20
|
}
|
|
25
21
|
});
|
|
@@ -34,17 +30,13 @@ describe("MODELS", () => {
|
|
|
34
30
|
const seen = new Map<string, Set<string>>();
|
|
35
31
|
for (const m of MODELS) {
|
|
36
32
|
const providerSet = seen.get(m.provider) ?? new Set<string>();
|
|
37
|
-
expect(providerSet.has(m.id)).
|
|
33
|
+
expect(providerSet.has(m.id)).toBeFalsy();
|
|
38
34
|
providerSet.add(m.id);
|
|
39
35
|
seen.set(m.provider, providerSet);
|
|
40
36
|
}
|
|
41
37
|
});
|
|
42
38
|
});
|
|
43
39
|
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// model()
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
|
|
48
40
|
describe("model()", () => {
|
|
49
41
|
it("returns the model definition for a known ID", () => {
|
|
50
42
|
const result = model("gpt-4o-mini");
|
|
@@ -66,7 +58,7 @@ describe("model()", () => {
|
|
|
66
58
|
const result = model("o1");
|
|
67
59
|
|
|
68
60
|
expect(result).not.toBeNull();
|
|
69
|
-
expect(result!.capabilities.reasoning).
|
|
61
|
+
expect(result!.capabilities.reasoning).toBeTruthy();
|
|
70
62
|
});
|
|
71
63
|
|
|
72
64
|
it("returns model with correct modalities", () => {
|
|
@@ -78,10 +70,6 @@ describe("model()", () => {
|
|
|
78
70
|
});
|
|
79
71
|
});
|
|
80
72
|
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
// models()
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
|
|
85
73
|
describe("models()", () => {
|
|
86
74
|
it("returns all models when called without filter", () => {
|
|
87
75
|
const result = models();
|
|
@@ -94,7 +82,7 @@ describe("models()", () => {
|
|
|
94
82
|
|
|
95
83
|
expect(reasoningModels.length).toBeGreaterThan(0);
|
|
96
84
|
for (const m of reasoningModels) {
|
|
97
|
-
expect(m.capabilities.reasoning).
|
|
85
|
+
expect(m.capabilities.reasoning).toBeTruthy();
|
|
98
86
|
}
|
|
99
87
|
});
|
|
100
88
|
|
|
@@ -114,11 +102,11 @@ describe("models()", () => {
|
|
|
114
102
|
});
|
|
115
103
|
|
|
116
104
|
it("supports arbitrary filter predicates", () => {
|
|
117
|
-
const result = models((m) => m.pricing.input > 0.
|
|
105
|
+
const result = models((m) => m.pricing.input > 0.000_001);
|
|
118
106
|
|
|
119
107
|
expect(result.length).toBeGreaterThan(0);
|
|
120
108
|
for (const m of result) {
|
|
121
|
-
expect(m.pricing.input).toBeGreaterThan(0.
|
|
109
|
+
expect(m.pricing.input).toBeGreaterThan(0.000_001);
|
|
122
110
|
}
|
|
123
111
|
});
|
|
124
112
|
});
|
package/src/catalog/index.ts
CHANGED
|
@@ -61,5 +61,8 @@ export function model(id: ModelId): ModelDefinition | null {
|
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
63
|
export function models(filter?: (m: ModelDefinition) => boolean): readonly ModelDefinition[] {
|
|
64
|
-
|
|
64
|
+
if (filter) {
|
|
65
|
+
return MODELS.filter(filter);
|
|
66
|
+
}
|
|
67
|
+
return MODELS;
|
|
65
68
|
}
|