@funkai/models 0.3.0 → 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 +33 -33
- package/CHANGELOG.md +6 -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 +2 -1
- 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 +152 -63
- package/src/catalog/index.test.ts +8 -8
- 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 -11
- package/src/provider/registry.ts +1 -1
- 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
|
@@ -55,7 +55,7 @@ interface ApiProvider {
|
|
|
55
55
|
* e.g. "openai" → "OPENAI_MODELS", "meta-llama" → "META_LLAMA_MODELS"
|
|
56
56
|
*/
|
|
57
57
|
function toConstName(provider: string): string {
|
|
58
|
-
return `${provider.toUpperCase().
|
|
58
|
+
return `${provider.toUpperCase().replaceAll(/[^A-Z0-9]/g, "_")}_MODELS`;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
@@ -63,14 +63,24 @@ function toConstName(provider: string): string {
|
|
|
63
63
|
* e.g. "OpenAI" → "openAI", "GoogleVertex" → "googleVertex", "XAI" → "xAI"
|
|
64
64
|
*/
|
|
65
65
|
function lowerFirst(s: string): string {
|
|
66
|
-
|
|
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);
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
/**
|
|
70
77
|
* Return the correct indefinite article ("a" or "an") for a word.
|
|
71
78
|
*/
|
|
72
79
|
function article(word: string): string {
|
|
73
|
-
|
|
80
|
+
if (/^[aeiou]/i.test(word)) {
|
|
81
|
+
return "an";
|
|
82
|
+
}
|
|
83
|
+
return "a";
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
/**
|
|
@@ -86,8 +96,12 @@ function toPerToken(perMillion: number): number {
|
|
|
86
96
|
* very small values.
|
|
87
97
|
*/
|
|
88
98
|
function fmtNum(n: number): string {
|
|
89
|
-
if (n === 0)
|
|
90
|
-
|
|
99
|
+
if (n === 0) {
|
|
100
|
+
return "0";
|
|
101
|
+
}
|
|
102
|
+
if (n < 0.000_000_1) {
|
|
103
|
+
return n.toExponential();
|
|
104
|
+
}
|
|
91
105
|
return String(n);
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -95,18 +109,32 @@ function fmtNum(n: number): string {
|
|
|
95
109
|
* Build the pricing object literal string for a model.
|
|
96
110
|
*/
|
|
97
111
|
function buildPricing(cost: ApiModel["cost"]): string {
|
|
98
|
-
const
|
|
99
|
-
|
|
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);
|
|
100
126
|
const parts: string[] = [`input: ${fmtNum(input)}`, `output: ${fmtNum(output)}`];
|
|
101
127
|
|
|
102
|
-
if (cost
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
+
}
|
|
110
138
|
}
|
|
111
139
|
|
|
112
140
|
return `{ ${parts.join(", ")} }`;
|
|
@@ -116,8 +144,30 @@ function buildPricing(cost: ApiModel["cost"]): string {
|
|
|
116
144
|
* Build the modalities object literal string.
|
|
117
145
|
*/
|
|
118
146
|
function buildModalities(modalities: ApiModel["modalities"]): string {
|
|
119
|
-
const
|
|
120
|
-
|
|
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);
|
|
121
171
|
return `{ input: ${input}, output: ${output} }`;
|
|
122
172
|
}
|
|
123
173
|
|
|
@@ -133,11 +183,37 @@ function buildCapabilities(m: ApiModel): string {
|
|
|
133
183
|
].join(", ");
|
|
134
184
|
}
|
|
135
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
|
+
|
|
136
208
|
/**
|
|
137
209
|
* Escape a string for use in a TypeScript single-quoted string literal.
|
|
138
210
|
*/
|
|
139
211
|
function escapeStr(s: string): string {
|
|
140
|
-
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`);
|
|
141
217
|
}
|
|
142
218
|
|
|
143
219
|
function isFresh(reqPath: string): boolean {
|
|
@@ -145,7 +221,7 @@ function isFresh(reqPath: string): boolean {
|
|
|
145
221
|
return false;
|
|
146
222
|
}
|
|
147
223
|
try {
|
|
148
|
-
const timestamp = readFileSync(reqPath, "
|
|
224
|
+
const timestamp = readFileSync(reqPath, "utf8").trim();
|
|
149
225
|
const lastRun = new Date(timestamp).getTime();
|
|
150
226
|
return Date.now() - lastRun < STALE_MS;
|
|
151
227
|
} catch {
|
|
@@ -175,7 +251,7 @@ export default lauf({
|
|
|
175
251
|
|
|
176
252
|
// Read provider config
|
|
177
253
|
const providers: Record<string, ProviderEntry> = JSON.parse(
|
|
178
|
-
readFileSync(PROVIDERS_PATH, "
|
|
254
|
+
readFileSync(PROVIDERS_PATH, "utf8"),
|
|
179
255
|
);
|
|
180
256
|
const providerKeys = Object.keys(providers);
|
|
181
257
|
|
|
@@ -213,30 +289,34 @@ export default lauf({
|
|
|
213
289
|
const providerFiles: { provider: string; constName: string; count: number }[] = [];
|
|
214
290
|
|
|
215
291
|
for (const providerKey of providerKeys) {
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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}
|
|
240
320
|
|
|
241
321
|
import type { ModelDefinition } from '../types.js'
|
|
242
322
|
|
|
@@ -245,16 +325,24 @@ ${lines.join("\n")}
|
|
|
245
325
|
] as const satisfies readonly ModelDefinition[]
|
|
246
326
|
`;
|
|
247
327
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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}
|
|
258
346
|
|
|
259
347
|
import type { LiteralUnion } from 'type-fest'
|
|
260
348
|
import type { ModelDefinition } from '../catalog/types.js'
|
|
@@ -309,11 +397,12 @@ export function ${camel}Model(id: LiteralUnion<${prefix}ModelId, string>): Model
|
|
|
309
397
|
}
|
|
310
398
|
`;
|
|
311
399
|
|
|
312
|
-
|
|
313
|
-
|
|
400
|
+
const entryPath = join(ENTRY_DIR, `${providerKey}.ts`);
|
|
401
|
+
writeFileSync(entryPath, entryContent, "utf8");
|
|
314
402
|
|
|
315
|
-
|
|
316
|
-
|
|
403
|
+
ctx.logger.success(`${providerKey} (${lines.length} models)`);
|
|
404
|
+
providerFiles.push({ provider: providerKey, constName, count: lines.length });
|
|
405
|
+
}
|
|
317
406
|
}
|
|
318
407
|
|
|
319
408
|
// Catalog barrel
|
|
@@ -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 }> = {
|
|
@@ -360,11 +449,11 @@ ${spreads}
|
|
|
360
449
|
}
|
|
361
450
|
|
|
362
451
|
pkg.exports = exportsMap;
|
|
363
|
-
writeFileSync(PACKAGE_JSON_PATH, JSON.stringify(pkg, null, 2)
|
|
452
|
+
writeFileSync(PACKAGE_JSON_PATH, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
364
453
|
ctx.logger.success("package.json exports map updated");
|
|
365
454
|
|
|
366
455
|
// Staleness timestamp
|
|
367
|
-
writeFileSync(REQ_PATH, new Date().toISOString(), "
|
|
456
|
+
writeFileSync(REQ_PATH, new Date().toISOString(), "utf8");
|
|
368
457
|
|
|
369
458
|
const totalModels = providerFiles.reduce((sum, p) => sum + p.count, 0);
|
|
370
459
|
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
|
@@ -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
|
}
|