@agentproto/catalog-sync 0.5.6 → 0.5.9
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/README.md +74 -0
- package/dist/bin.mjs +248 -13
- package/dist/bin.mjs.map +1 -1
- package/dist/generators/llm-openrouter.mjs +66 -2
- package/dist/generators/llm-openrouter.mjs.map +1 -1
- package/dist/generators/llm-requesty.mjs +66 -2
- package/dist/generators/llm-requesty.mjs.map +1 -1
- package/dist/index.d.ts +98 -2
- package/dist/index.mjs +168 -15
- package/dist/index.mjs.map +1 -1
- package/dist/runner.d.ts +8 -1
- package/dist/runner.mjs +79 -3
- package/dist/runner.mjs.map +1 -1
- package/package.json +3 -3
- package/snapshots/llm-huggingface.json +2716 -2377
- package/snapshots/llm-mistral.json +1941 -0
- package/snapshots/llm-moonshot.json +8 -166
- package/snapshots/llm-openrouter.json +8629 -8658
- package/snapshots/llm-requesty.json +21768 -7459
- package/snapshots/llm-xai.json +48 -2
- package/snapshots/voice-elevenlabs.json +5607 -5415
package/README.md
CHANGED
|
@@ -49,6 +49,76 @@ pnpm --filter @agentproto/catalog-sync generate --check # diff vs committed; ex
|
|
|
49
49
|
pnpm --filter @agentproto/catalog-sync generate --refresh # re-fetch pinned sources
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## `addedAt` convention
|
|
53
|
+
|
|
54
|
+
Some generators (currently `llm:openrouter` and `llm:requesty` — the two
|
|
55
|
+
producing `LLMPricing` entries) stamp an `addedAt?: string` field (ISO
|
|
56
|
+
`YYYY-MM-DD`) on each catalog entry: the date that model id was first seen
|
|
57
|
+
by a sync run.
|
|
58
|
+
|
|
59
|
+
- **Backfill rule.** When the provider source itself carries a creation
|
|
60
|
+
timestamp for the id (OpenRouter's and Requesty's `/v1/models` both carry
|
|
61
|
+
a Unix-seconds `created` field), a brand-new id is backfilled with that
|
|
62
|
+
date, converted to `YYYY-MM-DD`. When the source has no such field, the
|
|
63
|
+
id is stamped with the sync run's own date instead.
|
|
64
|
+
- **Never-mutated invariant.** Once an id has an `addedAt` stamp, it is
|
|
65
|
+
never overwritten by a later run — not even if the source's own `created`
|
|
66
|
+
value for that id later resolves to something different. A model id
|
|
67
|
+
disappearing and later reappearing keeps its ORIGINAL stamp rather than
|
|
68
|
+
looking newly added.
|
|
69
|
+
- **Mechanism: a per-generator ledger file**, committed at
|
|
70
|
+
`packages/catalog-sync/ledger/<generator-id>.json` (e.g.
|
|
71
|
+
`ledger/llm-openrouter.json`) — a flat `{ "model/id": "YYYY-MM-DD", ... }`
|
|
72
|
+
map, sorted keys, deterministic serialization. Each generator reads its
|
|
73
|
+
own ledger directly off disk (a plain `node:fs` read — `GeneratorContext`
|
|
74
|
+
intentionally has no general repo-read capability; the ledger is a
|
|
75
|
+
generator-internal concern, not part of the frozen `types.ts` contract),
|
|
76
|
+
merges in the current run's ids via `computeAddedAtLedger` (see
|
|
77
|
+
`src/added-at.ts`), and returns the updated ledger as a second entry in
|
|
78
|
+
its `GeneratedFiles` — so `runner.ts`'s existing diff/write logic (and
|
|
79
|
+
`--check`'s drift detection) covers the ledger file for free, same as any
|
|
80
|
+
other generated file.
|
|
81
|
+
- Ledger entries are **never dropped**, even for ids no longer present in a
|
|
82
|
+
run — the ledger records "first ever seen", not "currently present".
|
|
83
|
+
|
|
84
|
+
See `src/added-at.ts` for the implementation and `src/generators/llm-openrouter.ts` /
|
|
85
|
+
`src/generators/llm-requesty.ts` for how a generator wires it in.
|
|
86
|
+
|
|
87
|
+
## `CATALOG-CHANGELOG.md`
|
|
88
|
+
|
|
89
|
+
`packages/model-catalog/CATALOG-CHANGELOG.md` is a human-readable "what
|
|
90
|
+
models changed" log — NOT the changesets-driven `CHANGELOG.md` next to it
|
|
91
|
+
(that one is package-version history, one entry per publish). It answers
|
|
92
|
+
"what did catalog-sync add or remove, and when" by reading top to bottom.
|
|
93
|
+
|
|
94
|
+
- **Written automatically** by `runGenerators` (`src/runner.ts`), not by
|
|
95
|
+
hand. After collecting every generator's output, the runner diffs each
|
|
96
|
+
changed `*.generated.ts` file's top-level id keys (before vs after —
|
|
97
|
+
see `src/changelog.ts`'s `extractRecordIds` / `diffModelIds`) and, when at
|
|
98
|
+
least one generator actually added or removed a model id, appends one
|
|
99
|
+
dated section:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
## 2026-08-31
|
|
103
|
+
|
|
104
|
+
### llm:requesty
|
|
105
|
+
- Added: alibaba/qwen3.8-flash, alibaba/qwen3.8-max, ...
|
|
106
|
+
- Removed: anthropic/claude-opus-4-1, ...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- **Sections are appended at the END of the file (newest last)** — a plain,
|
|
110
|
+
non-destructive `+=`. This is a deliberate choice over "newest first";
|
|
111
|
+
it means the writer never has to parse and re-emit prior history, only
|
|
112
|
+
concatenate.
|
|
113
|
+
- **A run with no id-level drift emits nothing** — no empty section, no
|
|
114
|
+
touched file, no diff. Pricing-only changes (a model's price moved but no
|
|
115
|
+
id was added/removed) are NOT logged here; they're visible in the
|
|
116
|
+
generated file's own git history.
|
|
117
|
+
- addedAt ledger files (`ledger/*.json`) are deliberately excluded from this
|
|
118
|
+
diff — they never lose an id (see above), so they carry no "removed"
|
|
119
|
+
signal, and every "added" id they'd report is already covered by the
|
|
120
|
+
owning generator's own `*.generated.ts` diff.
|
|
121
|
+
|
|
52
122
|
## Generators
|
|
53
123
|
|
|
54
124
|
### `llm:openrouter`
|
|
@@ -66,6 +136,8 @@ Source: `https://openrouter.ai/api/v1/models` (pinned snapshot:
|
|
|
66
136
|
(P3 spec: "plus cache fields if the source has them") — additive and
|
|
67
137
|
`LLMPricing`-compatible.
|
|
68
138
|
- Unpriced routes (`openrouter/auto`) are skipped.
|
|
139
|
+
- `addedAt` (ISO date, first-seen, never mutated) is stamped per id via the
|
|
140
|
+
`ledger/llm-openrouter.json` ledger — see "`addedAt` convention" above.
|
|
69
141
|
|
|
70
142
|
### `llm:requesty`
|
|
71
143
|
|
|
@@ -81,6 +153,8 @@ Source: `https://router.requesty.ai/v1/models` (pinned snapshot:
|
|
|
81
153
|
`cacheWriteMultiplier` is never emitted.
|
|
82
154
|
- Live refresh requires `REQUESTY_API_KEY` to be set; without it the generator
|
|
83
155
|
reuses the committed snapshot.
|
|
156
|
+
- `addedAt` (ISO date, first-seen, never mutated) is stamped per id via the
|
|
157
|
+
`ledger/llm-requesty.json` ledger — see "`addedAt` convention" above.
|
|
84
158
|
|
|
85
159
|
### `llm:context-windows`
|
|
86
160
|
|
package/dist/bin.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { ElevenLabsVoicesSnapshotSchema, mapElevenLabsVoices, MinimaxVoicesSnapshotSchema, mapMinimaxVoices } from '@agentproto/model-catalog/providers';
|
|
4
3
|
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
|
|
5
4
|
import { dirname, join, resolve } from 'path';
|
|
6
5
|
import { fileURLToPath } from 'url';
|
|
6
|
+
import { ElevenLabsVoicesSnapshotSchema, mapElevenLabsVoices, MinimaxVoicesSnapshotSchema, mapMinimaxVoices } from '@agentproto/model-catalog/providers';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @agentproto/catalog-sync v0.1.0
|
|
@@ -16,6 +16,47 @@ import { fileURLToPath } from 'url';
|
|
|
16
16
|
function defineGenerator(g) {
|
|
17
17
|
return g;
|
|
18
18
|
}
|
|
19
|
+
var __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
function catalogSyncDir() {
|
|
21
|
+
return resolve(__dirname$1, "..");
|
|
22
|
+
}
|
|
23
|
+
function ledgerAbsPath(ledgerId) {
|
|
24
|
+
return join(catalogSyncDir(), "ledger", `${ledgerId}.json`);
|
|
25
|
+
}
|
|
26
|
+
function ledgerRelPath(ledgerId) {
|
|
27
|
+
return `packages/catalog-sync/ledger/${ledgerId}.json`;
|
|
28
|
+
}
|
|
29
|
+
function readLedger(ledgerId) {
|
|
30
|
+
const path = ledgerAbsPath(ledgerId);
|
|
31
|
+
if (!existsSync(path)) return {};
|
|
32
|
+
try {
|
|
33
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
34
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
35
|
+
return parsed;
|
|
36
|
+
} catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function serializeLedger(ledger) {
|
|
41
|
+
const sorted = {};
|
|
42
|
+
for (const id of Object.keys(ledger).sort()) sorted[id] = ledger[id];
|
|
43
|
+
return `${JSON.stringify(sorted, null, 2)}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
function computeAddedAtLedger(currentIds, previous, createdAt, today) {
|
|
47
|
+
const next = { ...previous };
|
|
48
|
+
for (const id of currentIds) {
|
|
49
|
+
if (next[id] !== void 0) continue;
|
|
50
|
+
next[id] = createdAt[id] ?? today;
|
|
51
|
+
}
|
|
52
|
+
return next;
|
|
53
|
+
}
|
|
54
|
+
function todayIso() {
|
|
55
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
56
|
+
}
|
|
57
|
+
function isoDateFromUnixSeconds(seconds) {
|
|
58
|
+
return new Date(seconds * 1e3).toISOString().slice(0, 10);
|
|
59
|
+
}
|
|
19
60
|
|
|
20
61
|
// src/generators/llm-openrouter.ts
|
|
21
62
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
@@ -28,7 +69,8 @@ var PricingSchema = z.object({
|
|
|
28
69
|
}).passthrough();
|
|
29
70
|
var ModelSchema = z.object({
|
|
30
71
|
id: z.string(),
|
|
31
|
-
pricing: PricingSchema.optional()
|
|
72
|
+
pricing: PricingSchema.optional(),
|
|
73
|
+
created: z.number().optional()
|
|
32
74
|
}).passthrough();
|
|
33
75
|
var SnapshotSchema = z.object({
|
|
34
76
|
data: z.array(ModelSchema)
|
|
@@ -61,6 +103,9 @@ function serializeEntry(e) {
|
|
|
61
103
|
if (e.cacheWriteMultiplier !== void 0) {
|
|
62
104
|
fields.push(`cacheWriteMultiplier: ${fmt(e.cacheWriteMultiplier)}`);
|
|
63
105
|
}
|
|
106
|
+
if (e.addedAt !== void 0) {
|
|
107
|
+
fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`);
|
|
108
|
+
}
|
|
64
109
|
fields.push(`vendor: ${JSON.stringify(e.vendor)}`);
|
|
65
110
|
fields.push(`provider: ${JSON.stringify(e.provider)}`);
|
|
66
111
|
return `{
|
|
@@ -77,6 +122,9 @@ function serializeFile(entries) {
|
|
|
77
122
|
"// Pricing carries provider USD (inputPer1M / outputPer1M) plus cache",
|
|
78
123
|
"// multipliers (cacheReadMultiplier / cacheWriteMultiplier) derived from the",
|
|
79
124
|
"// source's input_cache_read / input_cache_write per-token fields when present.",
|
|
125
|
+
"// addedAt is the ISO date this id was first seen by a sync run \u2014 backfilled",
|
|
126
|
+
"// from the source's own `created` timestamp, then NEVER mutated; see",
|
|
127
|
+
"// packages/catalog-sync/src/added-at.ts and the package README.",
|
|
80
128
|
"",
|
|
81
129
|
'import type { LLMPricing } from "./catalog.js"',
|
|
82
130
|
"",
|
|
@@ -96,11 +144,13 @@ function serializeFile(entries) {
|
|
|
96
144
|
lines.push("");
|
|
97
145
|
return lines.join("\n");
|
|
98
146
|
}
|
|
147
|
+
var LEDGER_ID = "llm-openrouter";
|
|
99
148
|
async function generate(ctx) {
|
|
100
149
|
const src = sources[0];
|
|
101
150
|
if (!src) throw new Error("llm:openrouter: no source configured");
|
|
102
151
|
const parsed = SnapshotSchema.parse(await ctx.fetchSource(src));
|
|
103
152
|
const entries = {};
|
|
153
|
+
const createdAt = {};
|
|
104
154
|
for (const model of parsed.data) {
|
|
105
155
|
const pricing = model.pricing;
|
|
106
156
|
if (!pricing) continue;
|
|
@@ -123,8 +173,19 @@ async function generate(ctx) {
|
|
|
123
173
|
entry.cacheWriteMultiplier = round6(cacheWritePer1M / inputPer1M);
|
|
124
174
|
}
|
|
125
175
|
entries[model.id] = entry;
|
|
176
|
+
if (model.created !== void 0) createdAt[model.id] = isoDateFromUnixSeconds(model.created);
|
|
126
177
|
}
|
|
127
|
-
|
|
178
|
+
const ledger = computeAddedAtLedger(
|
|
179
|
+
Object.keys(entries),
|
|
180
|
+
readLedger(LEDGER_ID),
|
|
181
|
+
createdAt,
|
|
182
|
+
todayIso()
|
|
183
|
+
);
|
|
184
|
+
for (const [id, entry] of Object.entries(entries)) entry.addedAt = ledger[id];
|
|
185
|
+
return {
|
|
186
|
+
[OUTPUT_PATH]: serializeFile(entries),
|
|
187
|
+
[ledgerRelPath(LEDGER_ID)]: serializeLedger(ledger)
|
|
188
|
+
};
|
|
128
189
|
}
|
|
129
190
|
var sources = [{ id: "llm-openrouter", url: OPENROUTER_MODELS_URL }];
|
|
130
191
|
var llmOpenRouterGenerator = defineGenerator({
|
|
@@ -140,7 +201,8 @@ var ModelSchema2 = z.object({
|
|
|
140
201
|
input_price: z.number().optional(),
|
|
141
202
|
output_price: z.number().optional(),
|
|
142
203
|
cached_price: z.number().optional(),
|
|
143
|
-
supports_caching: z.boolean().optional()
|
|
204
|
+
supports_caching: z.boolean().optional(),
|
|
205
|
+
created: z.number().optional()
|
|
144
206
|
}).passthrough();
|
|
145
207
|
var SnapshotSchema2 = z.object({
|
|
146
208
|
data: z.array(ModelSchema2)
|
|
@@ -169,6 +231,9 @@ function serializeEntry2(e) {
|
|
|
169
231
|
if (e.cacheReadMultiplier !== void 0) {
|
|
170
232
|
fields.push(`cacheReadMultiplier: ${fmt2(e.cacheReadMultiplier)}`);
|
|
171
233
|
}
|
|
234
|
+
if (e.addedAt !== void 0) {
|
|
235
|
+
fields.push(`addedAt: ${JSON.stringify(e.addedAt)}`);
|
|
236
|
+
}
|
|
172
237
|
fields.push(`vendor: ${JSON.stringify(e.vendor)}`);
|
|
173
238
|
fields.push(`provider: ${JSON.stringify(e.provider)}`);
|
|
174
239
|
return `{
|
|
@@ -186,6 +251,9 @@ function serializeFile2(entries) {
|
|
|
186
251
|
"// multiplier (cacheReadMultiplier) derived from the source's cached_price",
|
|
187
252
|
"// per-token field when `supports_caching` is true. Requesty has no",
|
|
188
253
|
"// cache-write price, so cacheWriteMultiplier is never emitted.",
|
|
254
|
+
"// addedAt is the ISO date this id was first seen by a sync run \u2014 backfilled",
|
|
255
|
+
"// from the source's own `created` timestamp, then NEVER mutated; see",
|
|
256
|
+
"// packages/catalog-sync/src/added-at.ts and the package README.",
|
|
189
257
|
"",
|
|
190
258
|
'import type { LLMPricing } from "./catalog.js"',
|
|
191
259
|
"",
|
|
@@ -205,11 +273,13 @@ function serializeFile2(entries) {
|
|
|
205
273
|
lines.push("");
|
|
206
274
|
return lines.join("\n");
|
|
207
275
|
}
|
|
276
|
+
var LEDGER_ID2 = "llm-requesty";
|
|
208
277
|
async function generate2(ctx) {
|
|
209
278
|
const src = sources2[0];
|
|
210
279
|
if (!src) throw new Error("llm:requesty: no source configured");
|
|
211
280
|
const parsed = SnapshotSchema2.parse(await ctx.fetchSource(src));
|
|
212
281
|
const entries = {};
|
|
282
|
+
const createdAt = {};
|
|
213
283
|
for (const model of parsed.data) {
|
|
214
284
|
const inputPer1M = per1m2(model.input_price);
|
|
215
285
|
const outputPer1M = per1m2(model.output_price);
|
|
@@ -228,8 +298,19 @@ async function generate2(ctx) {
|
|
|
228
298
|
}
|
|
229
299
|
}
|
|
230
300
|
entries[model.id] = entry;
|
|
301
|
+
if (model.created !== void 0) createdAt[model.id] = isoDateFromUnixSeconds(model.created);
|
|
231
302
|
}
|
|
232
|
-
|
|
303
|
+
const ledger = computeAddedAtLedger(
|
|
304
|
+
Object.keys(entries),
|
|
305
|
+
readLedger(LEDGER_ID2),
|
|
306
|
+
createdAt,
|
|
307
|
+
todayIso()
|
|
308
|
+
);
|
|
309
|
+
for (const [id, entry] of Object.entries(entries)) entry.addedAt = ledger[id];
|
|
310
|
+
return {
|
|
311
|
+
[OUTPUT_PATH2]: serializeFile2(entries),
|
|
312
|
+
[ledgerRelPath(LEDGER_ID2)]: serializeLedger(ledger)
|
|
313
|
+
};
|
|
233
314
|
}
|
|
234
315
|
var sources2 = [
|
|
235
316
|
{
|
|
@@ -373,6 +454,19 @@ var llmHuggingfaceGenerator = defineGenerator({
|
|
|
373
454
|
sources: sources3,
|
|
374
455
|
generate: generate3
|
|
375
456
|
});
|
|
457
|
+
|
|
458
|
+
// src/generators/google-native-model-ids.mjs
|
|
459
|
+
var GOOGLE_NATIVE_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
460
|
+
"gemini-2.5-pro",
|
|
461
|
+
"gemini-2.5-flash",
|
|
462
|
+
"gemini-2.5-flash-lite",
|
|
463
|
+
"gemini-3.5-flash",
|
|
464
|
+
"gemini-3.1-pro-preview",
|
|
465
|
+
"gemini-3-flash-preview",
|
|
466
|
+
"gemini-3.1-flash-lite"
|
|
467
|
+
]);
|
|
468
|
+
|
|
469
|
+
// src/generators/llm-context-windows.ts
|
|
376
470
|
var OUTPUT_PATH4 = "packages/model-catalog/src/llm/context-windows.generated.ts";
|
|
377
471
|
var AnthropicModelSchema = z.object({
|
|
378
472
|
id: z.string(),
|
|
@@ -397,6 +491,17 @@ var MoonshotModelSchema = z.object({
|
|
|
397
491
|
context_length: z.number().optional()
|
|
398
492
|
}).passthrough();
|
|
399
493
|
var MoonshotSnapshotSchema = z.object({ data: z.array(MoonshotModelSchema) });
|
|
494
|
+
var MistralModelSchema = z.object({
|
|
495
|
+
id: z.string(),
|
|
496
|
+
max_context_length: z.number().optional()
|
|
497
|
+
}).passthrough();
|
|
498
|
+
var MistralSnapshotSchema = z.object({ data: z.array(MistralModelSchema) });
|
|
499
|
+
var OpenRouterModelSchema = z.object({
|
|
500
|
+
id: z.string(),
|
|
501
|
+
context_length: z.number().nullable().optional(),
|
|
502
|
+
top_provider: z.object({ max_completion_tokens: z.number().nullable().optional() }).passthrough().optional()
|
|
503
|
+
}).passthrough();
|
|
504
|
+
var OpenRouterSnapshotSchema = z.object({ data: z.array(OpenRouterModelSchema) });
|
|
400
505
|
function serializeEntry4(e) {
|
|
401
506
|
const fields = [`contextWindow: ${e.contextWindow}`];
|
|
402
507
|
if (e.maxOutput !== void 0) fields.push(`maxOutput: ${e.maxOutput}`);
|
|
@@ -410,7 +515,12 @@ function serializeFile4(entries) {
|
|
|
410
515
|
"// AUTO-GENERATED by @agentproto/catalog-sync (llm:context-windows).",
|
|
411
516
|
"// Do not edit by hand \u2014 re-run `pnpm --filter @agentproto/catalog-sync generate --refresh`.",
|
|
412
517
|
"// Sources: api.anthropic.com/v1/models, api.groq.com/openai/v1/models,",
|
|
413
|
-
"// api.x.ai/v1/models, api.moonshot.ai/v1/models",
|
|
518
|
+
"// api.x.ai/v1/models, api.moonshot.ai/v1/models,",
|
|
519
|
+
"// api.mistral.ai/v1/models",
|
|
520
|
+
"// google entries: SOURCED FROM OPENROUTER (openrouter.ai/api/v1/models),",
|
|
521
|
+
"// Google's native API is billing-blocked \u2014 ids remapped from",
|
|
522
|
+
"// google/<id> to the bare native id; see the generator's module",
|
|
523
|
+
"// doc comment for the exact remap + skip rules.",
|
|
414
524
|
"// Context window (max input tokens) + max output tokens per live model id.",
|
|
415
525
|
"// NOT pricing \u2014 see LLM_PRICING_CATALOG / OPENROUTER_ROUTES in catalog.ts.",
|
|
416
526
|
"",
|
|
@@ -418,7 +528,7 @@ function serializeFile4(entries) {
|
|
|
418
528
|
" contextWindow: number",
|
|
419
529
|
" maxOutput?: number",
|
|
420
530
|
" displayName?: string",
|
|
421
|
-
' provider: "anthropic" | "groq" | "xai" | "moonshot"',
|
|
531
|
+
' provider: "anthropic" | "groq" | "xai" | "moonshot" | "mistral" | "google"',
|
|
422
532
|
"}",
|
|
423
533
|
"",
|
|
424
534
|
"export const CONTEXT_WINDOWS: Record<string, ContextWindowEntry> = {"
|
|
@@ -444,6 +554,13 @@ async function generate4(ctx) {
|
|
|
444
554
|
provider: "anthropic"
|
|
445
555
|
};
|
|
446
556
|
}
|
|
557
|
+
for (const [id, entry] of Object.entries(entries)) {
|
|
558
|
+
if (entry.provider !== "anthropic") continue;
|
|
559
|
+
const bare = id.replace(/-\d{8}$/, "");
|
|
560
|
+
if (bare === id) continue;
|
|
561
|
+
if (bare in entries) continue;
|
|
562
|
+
entries[bare] = { ...entry };
|
|
563
|
+
}
|
|
447
564
|
}
|
|
448
565
|
const groqSrc = sources4.find((s) => s.id === "llm-groq");
|
|
449
566
|
if (groqSrc) {
|
|
@@ -473,6 +590,40 @@ async function generate4(ctx) {
|
|
|
473
590
|
entries[m.id] = { contextWindow: m.context_length, provider: "moonshot" };
|
|
474
591
|
}
|
|
475
592
|
}
|
|
593
|
+
const mistralSrc = sources4.find((s) => s.id === "llm-mistral");
|
|
594
|
+
if (mistralSrc) {
|
|
595
|
+
const parsed = MistralSnapshotSchema.parse(await ctx.fetchSource(mistralSrc));
|
|
596
|
+
for (const m of parsed.data) {
|
|
597
|
+
if (!m.max_context_length) continue;
|
|
598
|
+
entries[m.id] = { contextWindow: m.max_context_length, provider: "mistral" };
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const googleSrc = sources4.find((s) => s.id === "llm-openrouter");
|
|
602
|
+
if (googleSrc) {
|
|
603
|
+
const parsed = OpenRouterSnapshotSchema.parse(await ctx.fetchSource(googleSrc));
|
|
604
|
+
for (const m of parsed.data) {
|
|
605
|
+
if (!m.id.startsWith("google/")) continue;
|
|
606
|
+
const nativeId = m.id.slice("google/".length);
|
|
607
|
+
if (m.id.includes(":")) {
|
|
608
|
+
console.error(
|
|
609
|
+
`llm:context-windows: skipping "${m.id}" \u2014 ":"-suffixed batch route, not the same billing surface as the base model.`
|
|
610
|
+
);
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
if (!GOOGLE_NATIVE_MODEL_IDS.has(nativeId)) {
|
|
614
|
+
console.error(
|
|
615
|
+
`llm:context-windows: skipping "${m.id}" \u2014 no confirmed native Gemini id "${nativeId}" (see GOOGLE_NATIVE_MODEL_IDS); not guessing the remap.`
|
|
616
|
+
);
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
if (!m.context_length) continue;
|
|
620
|
+
entries[nativeId] = {
|
|
621
|
+
contextWindow: m.context_length,
|
|
622
|
+
...m.top_provider?.max_completion_tokens ? { maxOutput: m.top_provider.max_completion_tokens } : {},
|
|
623
|
+
provider: "google"
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
}
|
|
476
627
|
return { [OUTPUT_PATH4]: serializeFile4(entries) };
|
|
477
628
|
}
|
|
478
629
|
var sources4 = [
|
|
@@ -498,6 +649,18 @@ var sources4 = [
|
|
|
498
649
|
id: "llm-moonshot",
|
|
499
650
|
url: "https://api.moonshot.ai/v1/models",
|
|
500
651
|
headers: { Authorization: "Bearer env:MOONSHOT_API_KEY" }
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
id: "llm-mistral",
|
|
655
|
+
url: "https://api.mistral.ai/v1/models",
|
|
656
|
+
headers: { Authorization: "Bearer env:MISTRAL_API_KEY" }
|
|
657
|
+
},
|
|
658
|
+
// Google via OpenRouter (no Google key needed — see module doc comment).
|
|
659
|
+
// Same source id/url as `llm-openrouter.ts`'s own generator, so both share
|
|
660
|
+
// the one pinned `snapshots/llm-openrouter.json` instead of fetching twice.
|
|
661
|
+
{
|
|
662
|
+
id: "llm-openrouter",
|
|
663
|
+
url: "https://openrouter.ai/api/v1/models"
|
|
501
664
|
}
|
|
502
665
|
];
|
|
503
666
|
var llmContextWindowsGenerator = defineGenerator({
|
|
@@ -879,22 +1042,70 @@ var imageReplicate = defineGenerator({
|
|
|
879
1042
|
return { [outputPath]: lines.join("\n") };
|
|
880
1043
|
}
|
|
881
1044
|
});
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1045
|
+
|
|
1046
|
+
// src/changelog.ts
|
|
1047
|
+
var RECORD_KEY_RE = /^ {2}"([^"]+)":\s*\{/gm;
|
|
1048
|
+
function extractRecordIds(source) {
|
|
1049
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1050
|
+
for (const m of source.matchAll(RECORD_KEY_RE)) {
|
|
1051
|
+
if (m[1] !== void 0) ids.add(m[1]);
|
|
1052
|
+
}
|
|
1053
|
+
return ids;
|
|
1054
|
+
}
|
|
1055
|
+
function diffModelIds(before, after) {
|
|
1056
|
+
const beforeIds = before !== void 0 ? extractRecordIds(before) : /* @__PURE__ */ new Set();
|
|
1057
|
+
const afterIds = extractRecordIds(after);
|
|
1058
|
+
const added = [...afterIds].filter((id) => !beforeIds.has(id)).sort();
|
|
1059
|
+
const removed = [...beforeIds].filter((id) => !afterIds.has(id)).sort();
|
|
1060
|
+
return { added, removed };
|
|
1061
|
+
}
|
|
1062
|
+
function renderChangelogSection(date, entries) {
|
|
1063
|
+
const nonEmpty = entries.filter((e) => e.added.length > 0 || e.removed.length > 0);
|
|
1064
|
+
if (nonEmpty.length === 0) return "";
|
|
1065
|
+
const lines = [`## ${date}`, ""];
|
|
1066
|
+
for (const e of nonEmpty) {
|
|
1067
|
+
lines.push(`### ${e.generator}`);
|
|
1068
|
+
if (e.added.length > 0) lines.push(`- Added: ${e.added.join(", ")}`);
|
|
1069
|
+
if (e.removed.length > 0) lines.push(`- Removed: ${e.removed.join(", ")}`);
|
|
1070
|
+
lines.push("");
|
|
1071
|
+
}
|
|
1072
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
1073
|
+
}
|
|
1074
|
+
var CATALOG_CHANGELOG_HEADER = `# Catalog Changelog
|
|
1075
|
+
|
|
1076
|
+
Human-readable log of model additions/removals detected by
|
|
1077
|
+
\`@agentproto/catalog-sync\` runs \u2014 NOT \`CHANGELOG.md\` next to it (that one is
|
|
1078
|
+
changesets-driven package-version history). A dated section is appended
|
|
1079
|
+
automatically here whenever a sync run adds or removes model ids; sections
|
|
1080
|
+
are appended at the END of the file (newest last). Pricing-only drift (no id
|
|
1081
|
+
added/removed) is not logged here \u2014 see the generated files' own git history
|
|
1082
|
+
for that.
|
|
1083
|
+
`;
|
|
1084
|
+
function appendChangelog(existing, date, entries) {
|
|
1085
|
+
const section = renderChangelogSection(date, entries);
|
|
1086
|
+
if (section === "") return void 0;
|
|
1087
|
+
const base = existing !== void 0 && existing.length > 0 ? existing.replace(/\n+$/, "\n") : CATALOG_CHANGELOG_HEADER;
|
|
1088
|
+
return `${base}
|
|
1089
|
+
${section}`;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// src/runner.ts
|
|
1093
|
+
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
1094
|
+
function catalogSyncDir2() {
|
|
1095
|
+
return resolve(__dirname2, "..");
|
|
885
1096
|
}
|
|
886
1097
|
function snapshotPath(id) {
|
|
887
|
-
return join(
|
|
1098
|
+
return join(catalogSyncDir2(), "snapshots", `${id}.json`);
|
|
888
1099
|
}
|
|
889
1100
|
function repoRoot() {
|
|
890
|
-
let dir =
|
|
1101
|
+
let dir = catalogSyncDir2();
|
|
891
1102
|
for (let i = 0; i < 12; i++) {
|
|
892
1103
|
if (existsSync(join(dir, "pnpm-workspace.yaml"))) return dir;
|
|
893
1104
|
const parent = dirname(dir);
|
|
894
1105
|
if (parent === dir) break;
|
|
895
1106
|
dir = parent;
|
|
896
1107
|
}
|
|
897
|
-
return dirname(dirname(
|
|
1108
|
+
return dirname(dirname(catalogSyncDir2()));
|
|
898
1109
|
}
|
|
899
1110
|
function readIfExists(absPath) {
|
|
900
1111
|
if (!existsSync(absPath)) return void 0;
|
|
@@ -916,8 +1127,13 @@ function resolveHeaders(headers) {
|
|
|
916
1127
|
}
|
|
917
1128
|
return { headers: out, missing };
|
|
918
1129
|
}
|
|
1130
|
+
var CATALOG_CHANGELOG_PATH = "packages/model-catalog/CATALOG-CHANGELOG.md";
|
|
1131
|
+
function isChangelogEligible(relPath) {
|
|
1132
|
+
return relPath.endsWith(".generated.ts");
|
|
1133
|
+
}
|
|
919
1134
|
async function runGenerators(gens, opts) {
|
|
920
1135
|
const files2 = {};
|
|
1136
|
+
const pathToGenerator = {};
|
|
921
1137
|
for (const gen of gens) {
|
|
922
1138
|
const ctx = {
|
|
923
1139
|
refresh: opts.refresh,
|
|
@@ -964,10 +1180,12 @@ async function runGenerators(gens, opts) {
|
|
|
964
1180
|
const out = await gen.generate(ctx);
|
|
965
1181
|
for (const [path, content] of Object.entries(out)) {
|
|
966
1182
|
files2[path] = content;
|
|
1183
|
+
pathToGenerator[path] = gen.name;
|
|
967
1184
|
}
|
|
968
1185
|
}
|
|
969
1186
|
const root = repoRoot();
|
|
970
1187
|
const changed2 = [];
|
|
1188
|
+
const changelogEntries = [];
|
|
971
1189
|
for (const [relPath, content] of Object.entries(files2)) {
|
|
972
1190
|
const abs = join(root, relPath);
|
|
973
1191
|
const existing = readIfExists(abs);
|
|
@@ -978,6 +1196,23 @@ async function runGenerators(gens, opts) {
|
|
|
978
1196
|
writeFileSync(abs, content, "utf8");
|
|
979
1197
|
}
|
|
980
1198
|
}
|
|
1199
|
+
if (isChangelogEligible(relPath)) {
|
|
1200
|
+
const { added, removed } = diffModelIds(existing, content);
|
|
1201
|
+
if (added.length > 0 || removed.length > 0) {
|
|
1202
|
+
changelogEntries.push({ generator: pathToGenerator[relPath] ?? relPath, added, removed });
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
const changelogAbs = join(root, CATALOG_CHANGELOG_PATH);
|
|
1207
|
+
const changelogExisting = readIfExists(changelogAbs);
|
|
1208
|
+
const newChangelog = appendChangelog(changelogExisting, todayIso(), changelogEntries);
|
|
1209
|
+
if (newChangelog !== void 0) {
|
|
1210
|
+
files2[CATALOG_CHANGELOG_PATH] = newChangelog;
|
|
1211
|
+
changed2.push(CATALOG_CHANGELOG_PATH);
|
|
1212
|
+
if (opts.write) {
|
|
1213
|
+
mkdirSync(dirname(changelogAbs), { recursive: true });
|
|
1214
|
+
writeFileSync(changelogAbs, newChangelog, "utf8");
|
|
1215
|
+
}
|
|
981
1216
|
}
|
|
982
1217
|
return { files: files2, changed: changed2 };
|
|
983
1218
|
}
|