@agentproto/catalog-sync 0.4.0 → 0.5.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/README.md CHANGED
@@ -66,3 +66,33 @@ Source: `https://openrouter.ai/api/v1/models` (pinned snapshot:
66
66
  (P3 spec: "plus cache fields if the source has them") — additive and
67
67
  `LLMPricing`-compatible.
68
68
  - Unpriced routes (`openrouter/auto`) are skipped.
69
+
70
+ ### `llm:requesty`
71
+
72
+ Source: `https://router.requesty.ai/v1/models` (pinned snapshot:
73
+ `snapshots/llm-requesty.json`). Emits
74
+ `packages/model-catalog/src/llm/requesty-routes.generated.ts` — a drop-in
75
+ `REQUESTY_ROUTES: Record<string, LLMPricing>` plus the derived
76
+ `REQUESTY_PROVIDERS` list.
77
+
78
+ - Per-token USD prices (JSON numbers) → `inputPer1M` / `outputPer1M`.
79
+ - `cached_price` is emitted as `cacheReadMultiplier` only when the source also
80
+ marks `supports_caching: true`; Requesty has no cache-write price, so
81
+ `cacheWriteMultiplier` is never emitted.
82
+ - Live refresh requires `REQUESTY_API_KEY` to be set; without it the generator
83
+ reuses the committed snapshot.
84
+
85
+ ### `llm:context-windows`
86
+
87
+ Sources: Anthropic, Groq, xAI, and Moonshot models-list endpoints (pinned
88
+ snapshots: `snapshots/llm-anthropic.json`, `snapshots/llm-groq.json`,
89
+ `snapshots/llm-xai.json`, `snapshots/llm-moonshot.json`). Emits
90
+ `packages/model-catalog/src/llm/context-windows.generated.ts` — a drop-in
91
+ `CONTEXT_WINDOWS: Record<string, ContextWindowEntry>`.
92
+
93
+ - `contextWindow` maps to each provider's max-input/context-length field.
94
+ - `maxOutput` is captured when the source publishes one.
95
+ - This is **not pricing**; billing data still comes from `llm:openrouter` and
96
+ hand-maintained pricing entries in `model-catalog`.
97
+ - OpenAI and ZAI/Zhipu are excluded because they don't publish a stable,
98
+ machine-readable context-window list.
package/dist/bin.mjs CHANGED
@@ -133,7 +133,247 @@ var llmOpenRouterGenerator = defineGenerator({
133
133
  sources,
134
134
  generate
135
135
  });
136
- var OUTPUT_PATH2 = "packages/model-catalog/src/llm/context-windows.generated.ts";
136
+ var REQUESTY_MODELS_URL = "https://router.requesty.ai/v1/models";
137
+ var OUTPUT_PATH2 = "packages/model-catalog/src/llm/requesty-routes.generated.ts";
138
+ var ModelSchema2 = z.object({
139
+ id: z.string(),
140
+ input_price: z.number().optional(),
141
+ output_price: z.number().optional(),
142
+ cached_price: z.number().optional(),
143
+ supports_caching: z.boolean().optional()
144
+ }).passthrough();
145
+ var SnapshotSchema2 = z.object({
146
+ data: z.array(ModelSchema2)
147
+ });
148
+ function round62(n) {
149
+ return Math.round(n * 1e6) / 1e6;
150
+ }
151
+ function per1m2(tokenPrice) {
152
+ if (tokenPrice === void 0) return void 0;
153
+ if (!Number.isFinite(tokenPrice)) return void 0;
154
+ return round62(tokenPrice * 1e6);
155
+ }
156
+ function fmt2(n) {
157
+ if (!Number.isFinite(n)) return "0";
158
+ return `${round62(n)}`;
159
+ }
160
+ function vendorOf2(id) {
161
+ if (!id.includes("/")) return id;
162
+ return id.split("/")[0] ?? id;
163
+ }
164
+ function serializeEntry2(e) {
165
+ const fields = [
166
+ `inputPer1M: ${fmt2(e.inputPer1M)}`,
167
+ `outputPer1M: ${fmt2(e.outputPer1M)}`
168
+ ];
169
+ if (e.cacheReadMultiplier !== void 0) {
170
+ fields.push(`cacheReadMultiplier: ${fmt2(e.cacheReadMultiplier)}`);
171
+ }
172
+ fields.push(`vendor: ${JSON.stringify(e.vendor)}`);
173
+ fields.push(`provider: ${JSON.stringify(e.provider)}`);
174
+ return `{
175
+ ${fields.join(",\n ")},
176
+ }`;
177
+ }
178
+ function serializeFile2(entries) {
179
+ const ids = Object.keys(entries).sort();
180
+ const providers = Array.from(new Set(ids.map(vendorOf2))).sort();
181
+ const lines = [
182
+ "// AUTO-GENERATED by @agentproto/catalog-sync (llm:requesty).",
183
+ "// Do not edit by hand \u2014 re-run `pnpm --filter @agentproto/catalog-sync generate`.",
184
+ `// Source: ${REQUESTY_MODELS_URL}`,
185
+ "// Pricing carries provider USD (inputPer1M / outputPer1M) plus a cache",
186
+ "// multiplier (cacheReadMultiplier) derived from the source's cached_price",
187
+ "// per-token field when `supports_caching` is true. Requesty has no",
188
+ "// cache-write price, so cacheWriteMultiplier is never emitted.",
189
+ "",
190
+ 'import type { LLMPricing } from "./catalog.js"',
191
+ "",
192
+ "export const REQUESTY_ROUTES: Record<string, LLMPricing> = {"
193
+ ];
194
+ for (const id of ids) {
195
+ lines.push(` ${JSON.stringify(id)}: ${serializeEntry2(entries[id])},`);
196
+ }
197
+ lines.push("}");
198
+ lines.push("");
199
+ lines.push("/** Provider slugs treated as Requesty routes by the picker. */");
200
+ lines.push("export const REQUESTY_PROVIDERS: readonly string[] = [");
201
+ for (const p of providers) {
202
+ lines.push(` ${JSON.stringify(p)},`);
203
+ }
204
+ lines.push("]");
205
+ lines.push("");
206
+ return lines.join("\n");
207
+ }
208
+ async function generate2(ctx) {
209
+ const src = sources2[0];
210
+ if (!src) throw new Error("llm:requesty: no source configured");
211
+ const parsed = SnapshotSchema2.parse(await ctx.fetchSource(src));
212
+ const entries = {};
213
+ for (const model of parsed.data) {
214
+ const inputPer1M = per1m2(model.input_price);
215
+ const outputPer1M = per1m2(model.output_price);
216
+ if (inputPer1M === void 0 || outputPer1M === void 0) continue;
217
+ if (inputPer1M === 0 && outputPer1M === 0) continue;
218
+ const entry = {
219
+ inputPer1M,
220
+ outputPer1M,
221
+ vendor: vendorOf2(model.id),
222
+ provider: "requesty"
223
+ };
224
+ if (model.supports_caching === true && inputPer1M > 0) {
225
+ const cachePer1M = per1m2(model.cached_price);
226
+ if (cachePer1M !== void 0) {
227
+ entry.cacheReadMultiplier = round62(cachePer1M / inputPer1M);
228
+ }
229
+ }
230
+ entries[model.id] = entry;
231
+ }
232
+ return { [OUTPUT_PATH2]: serializeFile2(entries) };
233
+ }
234
+ var sources2 = [
235
+ {
236
+ id: "llm-requesty",
237
+ url: REQUESTY_MODELS_URL,
238
+ // Live refresh needs the account key (Bearer). Without REQUESTY_API_KEY
239
+ // set, the framework reuses the committed snapshot.
240
+ headers: { Authorization: "Bearer env:REQUESTY_API_KEY" }
241
+ }
242
+ ];
243
+ var llmRequestyGenerator = defineGenerator({
244
+ name: "llm:requesty",
245
+ modality: "llm",
246
+ sources: sources2,
247
+ generate: generate2
248
+ });
249
+ var HUGGINGFACE_MODELS_URL = "https://router.huggingface.co/v1/models";
250
+ var OUTPUT_PATH3 = "packages/model-catalog/src/llm/huggingface-routes.generated.ts";
251
+ var HFProviderSchema = z.object({
252
+ provider: z.string(),
253
+ status: z.string(),
254
+ context_length: z.number().optional(),
255
+ pricing: z.object({
256
+ input: z.number().optional(),
257
+ output: z.number().optional()
258
+ }).optional(),
259
+ supports_tools: z.boolean().optional(),
260
+ supports_structured_output: z.boolean().optional()
261
+ }).passthrough();
262
+ var HFModelSchema = z.object({
263
+ id: z.string(),
264
+ providers: z.array(HFProviderSchema).optional()
265
+ }).passthrough();
266
+ var SnapshotSchema3 = z.object({
267
+ data: z.array(HFModelSchema)
268
+ });
269
+ function round4(n) {
270
+ return Math.round(n * 1e4) / 1e4;
271
+ }
272
+ function serializeProvider(p) {
273
+ const fields = [
274
+ `provider: ${JSON.stringify(p.provider)}`,
275
+ `status: ${JSON.stringify(p.status)}`
276
+ ];
277
+ if (p.contextLength !== void 0) fields.push(`contextLength: ${p.contextLength}`);
278
+ if (p.inputPer1M !== void 0) fields.push(`inputPer1M: ${p.inputPer1M}`);
279
+ if (p.outputPer1M !== void 0) fields.push(`outputPer1M: ${p.outputPer1M}`);
280
+ if (p.supportsTools !== void 0) fields.push(`supportsTools: ${p.supportsTools}`);
281
+ if (p.supportsStructuredOutput !== void 0) {
282
+ fields.push(`supportsStructuredOutput: ${p.supportsStructuredOutput}`);
283
+ }
284
+ return ` {
285
+ ${fields.join(",\n ")},
286
+ }`;
287
+ }
288
+ function serializeEntry3(id, e) {
289
+ const providerLines = e.providers.map(serializeProvider).join(",\n");
290
+ return [
291
+ ` ${JSON.stringify(id)}: {`,
292
+ ` wireId: ${JSON.stringify(e.wireId)},`,
293
+ ` providers: [`,
294
+ providerLines,
295
+ ` ],`,
296
+ ` },`
297
+ ].join("\n");
298
+ }
299
+ function serializeFile3(entries) {
300
+ const ids = Object.keys(entries).sort();
301
+ const lines = [
302
+ "// AUTO-GENERATED by @agentproto/catalog-sync (llm:huggingface).",
303
+ "// Do not edit by hand \u2014 re-run `pnpm --filter @agentproto/catalog-sync generate`.",
304
+ `// Source: ${HUGGINGFACE_MODELS_URL}`,
305
+ "// Per-model LIVE inference providers from the HuggingFace router. HF wire",
306
+ "// ids are case-sensitive and preserved verbatim. Provider entries are",
307
+ "// SPARSE on some models \u2014 every field below except provider/status is",
308
+ "// optional and emitted only when the upstream response carries it. Only",
309
+ '// providers with status === "live" are kept; models with zero live',
310
+ "// providers are skipped. inputPer1M/outputPer1M are USD per 1M tokens",
311
+ "// (HuggingFace router convention \u2014 already at that scale, unlike",
312
+ "// OpenRouter's per-token strings).",
313
+ "",
314
+ "export interface HuggingFaceRouteProvider {",
315
+ " provider: string",
316
+ " status: string",
317
+ " contextLength?: number",
318
+ " inputPer1M?: number",
319
+ " outputPer1M?: number",
320
+ " supportsTools?: boolean",
321
+ " supportsStructuredOutput?: boolean",
322
+ "}",
323
+ "",
324
+ "export interface HuggingFaceRoute {",
325
+ " wireId: string",
326
+ " providers: HuggingFaceRouteProvider[]",
327
+ "}",
328
+ "",
329
+ "export const HUGGINGFACE_ROUTES: Record<string, HuggingFaceRoute> = {"
330
+ ];
331
+ for (const id of ids) {
332
+ lines.push(serializeEntry3(id, entries[id]));
333
+ }
334
+ lines.push("}");
335
+ lines.push("");
336
+ return lines.join("\n");
337
+ }
338
+ async function generate3(ctx) {
339
+ const src = sources3[0];
340
+ if (!src) throw new Error("llm:huggingface: no source configured");
341
+ const parsed = SnapshotSchema3.parse(await ctx.fetchSource(src));
342
+ const entries = {};
343
+ for (const model of parsed.data) {
344
+ const liveProviders = (model.providers ?? []).filter((p) => p.status === "live");
345
+ if (liveProviders.length === 0) continue;
346
+ const providers = liveProviders.map((p) => {
347
+ const entry = {
348
+ provider: p.provider,
349
+ status: p.status
350
+ };
351
+ if (p.context_length !== void 0) entry.contextLength = p.context_length;
352
+ if (p.pricing?.input !== void 0) entry.inputPer1M = round4(p.pricing.input);
353
+ if (p.pricing?.output !== void 0) entry.outputPer1M = round4(p.pricing.output);
354
+ if (p.supports_tools !== void 0) entry.supportsTools = p.supports_tools;
355
+ if (p.supports_structured_output !== void 0) {
356
+ entry.supportsStructuredOutput = p.supports_structured_output;
357
+ }
358
+ return entry;
359
+ }).sort((a, b) => a.provider.localeCompare(b.provider));
360
+ entries[model.id] = { wireId: model.id, providers };
361
+ }
362
+ return { [OUTPUT_PATH3]: serializeFile3(entries) };
363
+ }
364
+ var sources3 = [
365
+ {
366
+ id: "llm-huggingface",
367
+ url: HUGGINGFACE_MODELS_URL
368
+ }
369
+ ];
370
+ var llmHuggingfaceGenerator = defineGenerator({
371
+ name: "llm:huggingface",
372
+ modality: "llm",
373
+ sources: sources3,
374
+ generate: generate3
375
+ });
376
+ var OUTPUT_PATH4 = "packages/model-catalog/src/llm/context-windows.generated.ts";
137
377
  var AnthropicModelSchema = z.object({
138
378
  id: z.string(),
139
379
  display_name: z.string().optional(),
@@ -157,14 +397,14 @@ var MoonshotModelSchema = z.object({
157
397
  context_length: z.number().optional()
158
398
  }).passthrough();
159
399
  var MoonshotSnapshotSchema = z.object({ data: z.array(MoonshotModelSchema) });
160
- function serializeEntry2(e) {
400
+ function serializeEntry4(e) {
161
401
  const fields = [`contextWindow: ${e.contextWindow}`];
162
402
  if (e.maxOutput !== void 0) fields.push(`maxOutput: ${e.maxOutput}`);
163
403
  if (e.displayName !== void 0) fields.push(`displayName: ${JSON.stringify(e.displayName)}`);
164
404
  fields.push(`provider: ${JSON.stringify(e.provider)}`);
165
405
  return `{ ${fields.join(", ")} }`;
166
406
  }
167
- function serializeFile2(entries) {
407
+ function serializeFile4(entries) {
168
408
  const ids = Object.keys(entries).sort();
169
409
  const lines = [
170
410
  "// AUTO-GENERATED by @agentproto/catalog-sync (llm:context-windows).",
@@ -184,15 +424,15 @@ function serializeFile2(entries) {
184
424
  "export const CONTEXT_WINDOWS: Record<string, ContextWindowEntry> = {"
185
425
  ];
186
426
  for (const id of ids) {
187
- lines.push(` ${JSON.stringify(id)}: ${serializeEntry2(entries[id])},`);
427
+ lines.push(` ${JSON.stringify(id)}: ${serializeEntry4(entries[id])},`);
188
428
  }
189
429
  lines.push("}");
190
430
  lines.push("");
191
431
  return lines.join("\n");
192
432
  }
193
- async function generate2(ctx) {
433
+ async function generate4(ctx) {
194
434
  const entries = {};
195
- const anthropicSrc = sources2.find((s) => s.id === "llm-anthropic");
435
+ const anthropicSrc = sources4.find((s) => s.id === "llm-anthropic");
196
436
  if (anthropicSrc) {
197
437
  const parsed = AnthropicSnapshotSchema.parse(await ctx.fetchSource(anthropicSrc));
198
438
  for (const m of parsed.data) {
@@ -205,7 +445,7 @@ async function generate2(ctx) {
205
445
  };
206
446
  }
207
447
  }
208
- const groqSrc = sources2.find((s) => s.id === "llm-groq");
448
+ const groqSrc = sources4.find((s) => s.id === "llm-groq");
209
449
  if (groqSrc) {
210
450
  const parsed = GroqSnapshotSchema.parse(await ctx.fetchSource(groqSrc));
211
451
  for (const m of parsed.data) {
@@ -217,7 +457,7 @@ async function generate2(ctx) {
217
457
  };
218
458
  }
219
459
  }
220
- const xaiSrc = sources2.find((s) => s.id === "llm-xai");
460
+ const xaiSrc = sources4.find((s) => s.id === "llm-xai");
221
461
  if (xaiSrc) {
222
462
  const parsed = XaiSnapshotSchema.parse(await ctx.fetchSource(xaiSrc));
223
463
  for (const m of parsed.data) {
@@ -225,7 +465,7 @@ async function generate2(ctx) {
225
465
  entries[m.id] = { contextWindow: m.context_length, provider: "xai" };
226
466
  }
227
467
  }
228
- const moonshotSrc = sources2.find((s) => s.id === "llm-moonshot");
468
+ const moonshotSrc = sources4.find((s) => s.id === "llm-moonshot");
229
469
  if (moonshotSrc) {
230
470
  const parsed = MoonshotSnapshotSchema.parse(await ctx.fetchSource(moonshotSrc));
231
471
  for (const m of parsed.data) {
@@ -233,9 +473,9 @@ async function generate2(ctx) {
233
473
  entries[m.id] = { contextWindow: m.context_length, provider: "moonshot" };
234
474
  }
235
475
  }
236
- return { [OUTPUT_PATH2]: serializeFile2(entries) };
476
+ return { [OUTPUT_PATH4]: serializeFile4(entries) };
237
477
  }
238
- var sources2 = [
478
+ var sources4 = [
239
479
  {
240
480
  id: "llm-anthropic",
241
481
  url: "https://api.anthropic.com/v1/models?limit=1000",
@@ -263,8 +503,8 @@ var sources2 = [
263
503
  var llmContextWindowsGenerator = defineGenerator({
264
504
  name: "llm:context-windows",
265
505
  modality: "llm",
266
- sources: sources2,
267
- generate: generate2
506
+ sources: sources4,
507
+ generate: generate4
268
508
  });
269
509
 
270
510
  // src/generators/serialize-voices.ts
@@ -309,7 +549,7 @@ var ELEVENLABS_SOURCE = {
309
549
  // Live refresh needs the account key; offline reads ignore this.
310
550
  headers: { "xi-api-key": "env:ELEVENLABS_API_KEY" }
311
551
  };
312
- var OUTPUT_PATH3 = "packages/catalog-sync/generated/voice-elevenlabs.generated.ts";
552
+ var OUTPUT_PATH5 = "packages/catalog-sync/generated/voice-elevenlabs.generated.ts";
313
553
  var voiceElevenlabs = defineGenerator({
314
554
  name: "voice:elevenlabs",
315
555
  modality: "voice",
@@ -320,7 +560,7 @@ var voiceElevenlabs = defineGenerator({
320
560
  );
321
561
  const voices = mapElevenLabsVoices(snapshot);
322
562
  return {
323
- [OUTPUT_PATH3]: serializeVoiceModule(
563
+ [OUTPUT_PATH5]: serializeVoiceModule(
324
564
  "voice:elevenlabs",
325
565
  "ELEVENLABS_VOICES",
326
566
  voices
@@ -340,7 +580,7 @@ var MINIMAX_SOURCE = {
340
580
  },
341
581
  body: { voice_type: "system" }
342
582
  };
343
- var OUTPUT_PATH4 = "packages/catalog-sync/generated/voice-minimax.generated.ts";
583
+ var OUTPUT_PATH6 = "packages/catalog-sync/generated/voice-minimax.generated.ts";
344
584
  var voiceMinimax = defineGenerator({
345
585
  name: "voice:minimax",
346
586
  modality: "voice",
@@ -351,7 +591,7 @@ var voiceMinimax = defineGenerator({
351
591
  );
352
592
  const voices = mapMinimaxVoices(snapshot);
353
593
  return {
354
- [OUTPUT_PATH4]: serializeVoiceModule(
594
+ [OUTPUT_PATH6]: serializeVoiceModule(
355
595
  "voice:minimax",
356
596
  "MINIMAX_VOICES",
357
597
  voices
@@ -764,6 +1004,8 @@ var refresh = flags.has("--refresh");
764
1004
  var check = flags.has("--check");
765
1005
  var generators = [
766
1006
  llmOpenRouterGenerator,
1007
+ llmRequestyGenerator,
1008
+ llmHuggingfaceGenerator,
767
1009
  llmContextWindowsGenerator,
768
1010
  voiceElevenlabs,
769
1011
  voiceMinimax,