@sriinnu/kosha-discovery 0.6.1 → 1.1.0
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 +95 -263
- package/dist/cli-cmd-model.d.ts +48 -0
- package/dist/cli-cmd-model.d.ts.map +1 -0
- package/dist/cli-cmd-model.js +231 -0
- package/dist/cli-cmd-model.js.map +1 -0
- package/dist/cli-cmd-query.d.ts +40 -0
- package/dist/cli-cmd-query.d.ts.map +1 -0
- package/dist/cli-cmd-query.js +303 -0
- package/dist/cli-cmd-query.js.map +1 -0
- package/dist/cli-commands.d.ts +65 -75
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +93 -543
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli-format.d.ts +32 -0
- package/dist/cli-format.d.ts.map +1 -1
- package/dist/cli-format.js +51 -0
- package/dist/cli-format.js.map +1 -1
- package/dist/cli-help.d.ts +19 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +150 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/discovery/anthropic.d.ts +8 -1
- package/dist/discovery/anthropic.d.ts.map +1 -1
- package/dist/discovery/anthropic.js +26 -3
- package/dist/discovery/anthropic.js.map +1 -1
- package/dist/discovery/base.d.ts +26 -0
- package/dist/discovery/base.d.ts.map +1 -1
- package/dist/discovery/base.js +55 -2
- package/dist/discovery/base.js.map +1 -1
- package/dist/discovery/free-tier.d.ts +31 -0
- package/dist/discovery/free-tier.d.ts.map +1 -0
- package/dist/discovery/free-tier.js +47 -0
- package/dist/discovery/free-tier.js.map +1 -0
- package/dist/discovery/google.d.ts +8 -1
- package/dist/discovery/google.d.ts.map +1 -1
- package/dist/discovery/google.js +25 -3
- package/dist/discovery/google.js.map +1 -1
- package/dist/discovery/index.d.ts +3 -6
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +36 -55
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/litellm-seed.d.ts +32 -0
- package/dist/discovery/litellm-seed.d.ts.map +1 -0
- package/dist/discovery/litellm-seed.js +252 -0
- package/dist/discovery/litellm-seed.js.map +1 -0
- package/dist/discovery/modelsdev-catalog.d.ts +95 -0
- package/dist/discovery/modelsdev-catalog.d.ts.map +1 -0
- package/dist/discovery/modelsdev-catalog.js +131 -0
- package/dist/discovery/modelsdev-catalog.js.map +1 -0
- package/dist/discovery/modelsdev-seed.d.ts +19 -0
- package/dist/discovery/modelsdev-seed.d.ts.map +1 -0
- package/dist/discovery/modelsdev-seed.js +203 -0
- package/dist/discovery/modelsdev-seed.js.map +1 -0
- package/dist/discovery/openai-compatible.d.ts.map +1 -1
- package/dist/discovery/openai-compatible.js +12 -1
- package/dist/discovery/openai-compatible.js.map +1 -1
- package/dist/discovery/openai.d.ts +9 -1
- package/dist/discovery/openai.d.ts.map +1 -1
- package/dist/discovery/openai.js +31 -8
- package/dist/discovery/openai.js.map +1 -1
- package/dist/discovery/promo-overrides.d.ts +54 -0
- package/dist/discovery/promo-overrides.d.ts.map +1 -0
- package/dist/discovery/promo-overrides.js +74 -0
- package/dist/discovery/promo-overrides.js.map +1 -0
- package/dist/discovery/public-seed.d.ts +30 -0
- package/dist/discovery/public-seed.d.ts.map +1 -0
- package/dist/discovery/public-seed.js +51 -0
- package/dist/discovery/public-seed.js.map +1 -0
- package/dist/discovery-contract.d.ts +14 -2
- package/dist/discovery-contract.d.ts.map +1 -1
- package/dist/discovery-contract.js +28 -0
- package/dist/discovery-contract.js.map +1 -1
- package/dist/enrichment/litellm-catalog.d.ts +81 -0
- package/dist/enrichment/litellm-catalog.d.ts.map +1 -0
- package/dist/enrichment/litellm-catalog.js +129 -0
- package/dist/enrichment/litellm-catalog.js.map +1 -0
- package/dist/enrichment/litellm.d.ts +9 -2
- package/dist/enrichment/litellm.d.ts.map +1 -1
- package/dist/enrichment/litellm.js +168 -20
- package/dist/enrichment/litellm.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +18 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +293 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/model-features.d.ts +77 -0
- package/dist/model-features.d.ts.map +1 -0
- package/dist/model-features.js +245 -0
- package/dist/model-features.js.map +1 -0
- package/dist/provider-catalog.d.ts +9 -0
- package/dist/provider-catalog.d.ts.map +1 -1
- package/dist/provider-catalog.js +22 -0
- package/dist/provider-catalog.js.map +1 -1
- package/dist/proxy.d.ts +31 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +193 -0
- package/dist/proxy.js.map +1 -0
- package/dist/registry-discovery.d.ts.map +1 -1
- package/dist/registry-discovery.js +12 -2
- package/dist/registry-discovery.js.map +1 -1
- package/dist/registry-runtime.d.ts +40 -1
- package/dist/registry-runtime.d.ts.map +1 -1
- package/dist/registry-runtime.js +502 -30
- package/dist/registry-runtime.js.map +1 -1
- package/dist/registry-selection.js +2 -0
- package/dist/registry-selection.js.map +1 -1
- package/dist/registry.d.ts +9 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +11 -1
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -0
- package/dist/server.js.map +1 -1
- package/dist/tokenizer-family.d.ts +39 -0
- package/dist/tokenizer-family.d.ts.map +1 -0
- package/dist/tokenizer-family.js +92 -0
- package/dist/tokenizer-family.js.map +1 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.d.ts.map +1 -1
- package/logo.png +0 -0
- package/package.json +15 -3
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kosha-discovery — Public-catalog seed for origin providers.
|
|
3
|
+
*
|
|
4
|
+
* Treats the LiteLLM community catalog as the canonical *public* source of
|
|
5
|
+
* "what models exist with what pricing/context/capabilities" and converts
|
|
6
|
+
* it into seed {@link ModelCard} arrays per origin provider.
|
|
7
|
+
*
|
|
8
|
+
* This decouples kosha discovery from API keys: anyone running `kosha
|
|
9
|
+
* latest`, with or without credentials, gets the current state of the
|
|
10
|
+
* world for every supported origin provider.
|
|
11
|
+
*
|
|
12
|
+
* Hardening notes:
|
|
13
|
+
* - All fields are read defensively (`typeof` checks, length caps).
|
|
14
|
+
* - Synthetic IDs are slugified to strict `[A-Za-z0-9._/:-]` so a
|
|
15
|
+
* compromised catalog cannot smuggle injection-shaped strings into
|
|
16
|
+
* downstream consumers.
|
|
17
|
+
* - The maximum number of seeded models per provider is capped to bound
|
|
18
|
+
* cost of a hostile catalog.
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { applyFreeTierFlag } from "./free-tier.js";
|
|
22
|
+
import { loadLiteLLMCatalog, } from "../enrichment/litellm-catalog.js";
|
|
23
|
+
/** Per-token cost → per-million conversion factor. */
|
|
24
|
+
const PER_MILLION = 1_000_000;
|
|
25
|
+
/** Maximum models seeded for a single provider — defensive ceiling. */
|
|
26
|
+
const MAX_MODELS_PER_SEED = 1_000;
|
|
27
|
+
/** Strict charset for model IDs; everything else gets stripped or rejected. */
|
|
28
|
+
const SAFE_ID_PATTERN = /^[A-Za-z0-9._:/\-]+$/;
|
|
29
|
+
/** Maximum length we accept for a model ID. */
|
|
30
|
+
const MAX_ID_LENGTH = 256;
|
|
31
|
+
/**
|
|
32
|
+
* Map kosha provider IDs (the slugs used internally) to the set of
|
|
33
|
+
* `litellm_provider` values that should be treated as the same origin.
|
|
34
|
+
* LiteLLM's slugs differ from kosha's in a few places (`gemini`, `together_ai`,
|
|
35
|
+
* `fireworks_ai`, etc.), so we alias them here rather than scattering
|
|
36
|
+
* conditionals across discoverers.
|
|
37
|
+
*/
|
|
38
|
+
const PROVIDER_SLUG_ALIASES = {
|
|
39
|
+
anthropic: ["anthropic"],
|
|
40
|
+
openai: ["openai"],
|
|
41
|
+
google: ["gemini", "vertex_ai-language-models", "vertex_ai-image-models", "vertex_ai-embedding-models"],
|
|
42
|
+
deepseek: ["deepseek"],
|
|
43
|
+
zai: ["zai"],
|
|
44
|
+
minimax: ["minimax"],
|
|
45
|
+
moonshot: ["moonshot"],
|
|
46
|
+
mistral: ["mistral"],
|
|
47
|
+
cohere: ["cohere", "cohere_chat"],
|
|
48
|
+
groq: ["groq"],
|
|
49
|
+
together: ["together_ai"],
|
|
50
|
+
fireworks: ["fireworks_ai"],
|
|
51
|
+
perplexity: ["perplexity"],
|
|
52
|
+
cerebras: ["cerebras"],
|
|
53
|
+
deepinfra: ["deepinfra"],
|
|
54
|
+
xai: ["xai"],
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Build a fresh array of seed {@link ModelCard}s for the given kosha
|
|
58
|
+
* provider ID, using the shared LiteLLM catalog. Returns `[]` if the
|
|
59
|
+
* provider has no entries in the catalog.
|
|
60
|
+
*
|
|
61
|
+
* The returned cards have `source: "litellm"` so downstream consumers
|
|
62
|
+
* (and the existing pricing enricher) can distinguish public-catalog
|
|
63
|
+
* data from live-API data.
|
|
64
|
+
*/
|
|
65
|
+
export async function getLiteLLMSeed(providerId) {
|
|
66
|
+
const slugs = PROVIDER_SLUG_ALIASES[providerId];
|
|
67
|
+
if (!slugs || slugs.length === 0)
|
|
68
|
+
return [];
|
|
69
|
+
const catalog = await loadLiteLLMCatalog();
|
|
70
|
+
const seeds = [];
|
|
71
|
+
const seenIds = new Set();
|
|
72
|
+
for (const [key, entry] of Object.entries(catalog)) {
|
|
73
|
+
if (seeds.length >= MAX_MODELS_PER_SEED)
|
|
74
|
+
break;
|
|
75
|
+
if (!entry || typeof entry !== "object")
|
|
76
|
+
continue;
|
|
77
|
+
const litellmProvider = entry.litellm_provider;
|
|
78
|
+
if (typeof litellmProvider !== "string")
|
|
79
|
+
continue;
|
|
80
|
+
if (!slugs.includes(litellmProvider))
|
|
81
|
+
continue;
|
|
82
|
+
const id = canonicaliseModelId(key, providerId);
|
|
83
|
+
if (!id || seenIds.has(id))
|
|
84
|
+
continue;
|
|
85
|
+
seenIds.add(id);
|
|
86
|
+
const card = buildSeedCard(id, providerId, entry);
|
|
87
|
+
if (card)
|
|
88
|
+
seeds.push(card);
|
|
89
|
+
}
|
|
90
|
+
return seeds;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Strip a LiteLLM catalog key down to the provider-native model ID.
|
|
94
|
+
*
|
|
95
|
+
* LiteLLM keys come in three shapes:
|
|
96
|
+
* 1. Bare ID: `claude-opus-4-7`
|
|
97
|
+
* 2. Prefixed: `anthropic/claude-opus-4-7`
|
|
98
|
+
* 3. Path-prefixed: `vertex_ai/claude-opus-4-7@default`
|
|
99
|
+
*
|
|
100
|
+
* Returns null when the key is not safe (oversized, illegal chars,
|
|
101
|
+
* or empty after stripping).
|
|
102
|
+
*/
|
|
103
|
+
function canonicaliseModelId(rawKey, providerId) {
|
|
104
|
+
if (typeof rawKey !== "string" || rawKey.length === 0 || rawKey.length > MAX_ID_LENGTH) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// Drop everything before the last `/`. Most public catalogs prefix the
|
|
108
|
+
// vendor; kosha stores the bare provider-native ID.
|
|
109
|
+
let id = rawKey.includes("/") ? rawKey.slice(rawKey.lastIndexOf("/") + 1) : rawKey;
|
|
110
|
+
// Drop deployment suffixes after `@` (Vertex naming).
|
|
111
|
+
const atIdx = id.indexOf("@");
|
|
112
|
+
if (atIdx > 0)
|
|
113
|
+
id = id.slice(0, atIdx);
|
|
114
|
+
if (!SAFE_ID_PATTERN.test(id))
|
|
115
|
+
return null;
|
|
116
|
+
// Skip pseudo entries the catalog uses for documentation (e.g. "sample_spec").
|
|
117
|
+
if (id === "sample_spec")
|
|
118
|
+
return null;
|
|
119
|
+
// For Google: only keep the gemini-* family, not other vertex bundle entries
|
|
120
|
+
// that aren't real model IDs.
|
|
121
|
+
if (providerId === "google" && !id.startsWith("gemini") && !id.startsWith("imagen") && !id.startsWith("veo")) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return id;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Convert a single LiteLLM entry into a {@link ModelCard}. Returns null
|
|
128
|
+
* when the entry is too sparse to be useful (e.g. no mode and no pricing
|
|
129
|
+
* signal at all).
|
|
130
|
+
*/
|
|
131
|
+
function buildSeedCard(id, providerId, entry) {
|
|
132
|
+
const mode = inferMode(entry);
|
|
133
|
+
const capabilities = inferCapabilities(entry, mode);
|
|
134
|
+
const pricing = extractPricing(entry);
|
|
135
|
+
const contextWindow = pickPositive(entry.max_input_tokens, entry.max_tokens) ?? 0;
|
|
136
|
+
const maxOutputTokens = pickPositive(entry.max_output_tokens, entry.max_tokens) ?? 0;
|
|
137
|
+
const card = {
|
|
138
|
+
id,
|
|
139
|
+
name: id,
|
|
140
|
+
provider: providerId,
|
|
141
|
+
originProvider: providerId,
|
|
142
|
+
mode,
|
|
143
|
+
capabilities,
|
|
144
|
+
rawCapabilities: [...capabilities],
|
|
145
|
+
contextWindow,
|
|
146
|
+
maxOutputTokens,
|
|
147
|
+
maxInputTokens: pickPositive(entry.max_input_tokens),
|
|
148
|
+
dimensions: pickPositive(entry.output_vector_size),
|
|
149
|
+
pricing,
|
|
150
|
+
aliases: [],
|
|
151
|
+
discoveredAt: Date.now(),
|
|
152
|
+
source: "litellm",
|
|
153
|
+
};
|
|
154
|
+
return applyFreeTierFlag(card);
|
|
155
|
+
}
|
|
156
|
+
/** Pick the first finite, positive number from a list of optional values. */
|
|
157
|
+
function pickPositive(...values) {
|
|
158
|
+
for (const value of values) {
|
|
159
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0)
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
/** Map LiteLLM mode strings to the kosha ModelMode union. */
|
|
165
|
+
function inferMode(entry) {
|
|
166
|
+
switch (entry.mode) {
|
|
167
|
+
case "embedding":
|
|
168
|
+
return "embedding";
|
|
169
|
+
case "image_generation":
|
|
170
|
+
return "image";
|
|
171
|
+
case "audio_transcription":
|
|
172
|
+
case "audio_speech":
|
|
173
|
+
return "audio";
|
|
174
|
+
case "moderation":
|
|
175
|
+
return "moderation";
|
|
176
|
+
case "rerank":
|
|
177
|
+
return "rerank";
|
|
178
|
+
default:
|
|
179
|
+
return "chat";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Infer kosha capability flags from LiteLLM's structured boolean fields. */
|
|
183
|
+
function inferCapabilities(entry, mode) {
|
|
184
|
+
if (mode === "embedding")
|
|
185
|
+
return ["embedding"];
|
|
186
|
+
if (mode === "image")
|
|
187
|
+
return ["image_generation"];
|
|
188
|
+
if (mode === "audio")
|
|
189
|
+
return ["audio"];
|
|
190
|
+
if (mode === "moderation")
|
|
191
|
+
return ["moderation"];
|
|
192
|
+
if (mode === "rerank")
|
|
193
|
+
return ["rerank"];
|
|
194
|
+
const caps = new Set(["chat"]);
|
|
195
|
+
if (entry.supports_vision)
|
|
196
|
+
caps.add("vision");
|
|
197
|
+
if (entry.supports_function_calling || entry.supports_tool_choice)
|
|
198
|
+
caps.add("function_calling");
|
|
199
|
+
if (entry.supports_prompt_caching)
|
|
200
|
+
caps.add("prompt_caching");
|
|
201
|
+
if (entry.supports_response_schema)
|
|
202
|
+
caps.add("structured_output");
|
|
203
|
+
// Most modern chat models cover code / NLU; LiteLLM doesn't expose those
|
|
204
|
+
// flags, but kosha consumers expect them present for chat-class models.
|
|
205
|
+
caps.add("code");
|
|
206
|
+
caps.add("nlu");
|
|
207
|
+
return Array.from(caps);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Convert LiteLLM per-token costs to per-million pricing.
|
|
211
|
+
* Returns undefined unless both base input and output pricing are present.
|
|
212
|
+
*/
|
|
213
|
+
function extractPricing(entry) {
|
|
214
|
+
const baseInput = isFinite(entry.input_cost_per_token) ? entry.input_cost_per_token : undefined;
|
|
215
|
+
const baseOutput = isFinite(entry.output_cost_per_token) ? entry.output_cost_per_token : undefined;
|
|
216
|
+
const reasoningInput = firstFinite(entry.input_cost_per_reasoning_token, entry.reasoning_input_cost_per_token);
|
|
217
|
+
const reasoningOutput = firstFinite(entry.output_cost_per_reasoning_token, entry.reasoning_output_cost_per_token);
|
|
218
|
+
if (baseInput === undefined || baseOutput === undefined)
|
|
219
|
+
return undefined;
|
|
220
|
+
const pricing = {
|
|
221
|
+
inputPerMillion: baseInput * PER_MILLION,
|
|
222
|
+
outputPerMillion: baseOutput * PER_MILLION,
|
|
223
|
+
};
|
|
224
|
+
if (reasoningInput !== undefined)
|
|
225
|
+
pricing.reasoningInputPerMillion = reasoningInput * PER_MILLION;
|
|
226
|
+
if (reasoningOutput !== undefined)
|
|
227
|
+
pricing.reasoningOutputPerMillion = reasoningOutput * PER_MILLION;
|
|
228
|
+
if (isFinite(entry.cache_read_input_token_cost)) {
|
|
229
|
+
pricing.cacheReadPerMillion = entry.cache_read_input_token_cost * PER_MILLION;
|
|
230
|
+
}
|
|
231
|
+
if (isFinite(entry.cache_creation_input_token_cost)) {
|
|
232
|
+
pricing.cacheWritePerMillion = entry.cache_creation_input_token_cost * PER_MILLION;
|
|
233
|
+
}
|
|
234
|
+
if (isFinite(entry.input_cost_per_token_batches)) {
|
|
235
|
+
pricing.batchInputPerMillion = entry.input_cost_per_token_batches * PER_MILLION;
|
|
236
|
+
}
|
|
237
|
+
if (isFinite(entry.output_cost_per_token_batches)) {
|
|
238
|
+
pricing.batchOutputPerMillion = entry.output_cost_per_token_batches * PER_MILLION;
|
|
239
|
+
}
|
|
240
|
+
return pricing;
|
|
241
|
+
}
|
|
242
|
+
function firstFinite(...values) {
|
|
243
|
+
for (const value of values) {
|
|
244
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
function isFinite(value) {
|
|
250
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=litellm-seed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"litellm-seed.js","sourceRoot":"","sources":["../../src/discovery/litellm-seed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kosha-discovery — Shared models.dev catalog loader.
|
|
3
|
+
*
|
|
4
|
+
* Pulls the keyless community catalog at https://models.dev/api.json,
|
|
5
|
+
* which is maintained by the SST team and tends to surface new models
|
|
6
|
+
* faster than LiteLLM. Same hardening contract as the LiteLLM loader:
|
|
7
|
+
* HTTPS-pinned URL, assertCleanPayload scan, bounded body read, entry-count
|
|
8
|
+
* cap, promise-deduped singleton.
|
|
9
|
+
*
|
|
10
|
+
* Schema (top level):
|
|
11
|
+
* {
|
|
12
|
+
* [providerSlug]: {
|
|
13
|
+
* id, name, env: string[], doc, npm, api,
|
|
14
|
+
* models: {
|
|
15
|
+
* [modelId]: {
|
|
16
|
+
* id, name, family,
|
|
17
|
+
* attachment, reasoning, tool_call, structured_output, temperature,
|
|
18
|
+
* knowledge, release_date, last_updated,
|
|
19
|
+
* modalities: { input: string[], output: string[] },
|
|
20
|
+
* open_weights,
|
|
21
|
+
* cost: { input, output, cache_read?, cache_write?, ... },
|
|
22
|
+
* limit: { context, output, input? }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
/** Pinned upstream catalog URL — HTTPS only. */
|
|
30
|
+
export declare const MODELSDEV_CATALOG_URL = "https://models.dev/api.json";
|
|
31
|
+
/** Cost block for a single model entry. Fields are optional / forward-compatible. */
|
|
32
|
+
export interface ModelsDevCost {
|
|
33
|
+
input?: number;
|
|
34
|
+
output?: number;
|
|
35
|
+
cache_read?: number;
|
|
36
|
+
cache_write?: number;
|
|
37
|
+
/** Per-million tokens for >200K-context billing tier (OpenAI long-context tier). */
|
|
38
|
+
context_over_200k?: {
|
|
39
|
+
input?: number;
|
|
40
|
+
output?: number;
|
|
41
|
+
cache_read?: number;
|
|
42
|
+
};
|
|
43
|
+
/** Per-million tokens for batch API tier. */
|
|
44
|
+
batch?: {
|
|
45
|
+
input?: number;
|
|
46
|
+
output?: number;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Limit block — context window + output cap + optional input cap. */
|
|
50
|
+
export interface ModelsDevLimit {
|
|
51
|
+
context?: number;
|
|
52
|
+
output?: number;
|
|
53
|
+
input?: number;
|
|
54
|
+
}
|
|
55
|
+
/** Modalities block — structured input/output media kinds. */
|
|
56
|
+
export interface ModelsDevModalities {
|
|
57
|
+
input?: string[];
|
|
58
|
+
output?: string[];
|
|
59
|
+
}
|
|
60
|
+
/** A single model entry in models.dev. */
|
|
61
|
+
export interface ModelsDevModel {
|
|
62
|
+
id: string;
|
|
63
|
+
name?: string;
|
|
64
|
+
family?: string;
|
|
65
|
+
attachment?: boolean;
|
|
66
|
+
reasoning?: boolean;
|
|
67
|
+
tool_call?: boolean;
|
|
68
|
+
structured_output?: boolean;
|
|
69
|
+
temperature?: boolean;
|
|
70
|
+
knowledge?: string;
|
|
71
|
+
release_date?: string;
|
|
72
|
+
last_updated?: string;
|
|
73
|
+
modalities?: ModelsDevModalities;
|
|
74
|
+
open_weights?: boolean;
|
|
75
|
+
cost?: ModelsDevCost;
|
|
76
|
+
limit?: ModelsDevLimit;
|
|
77
|
+
}
|
|
78
|
+
/** A single provider entry in models.dev. */
|
|
79
|
+
export interface ModelsDevProvider {
|
|
80
|
+
id: string;
|
|
81
|
+
name?: string;
|
|
82
|
+
env?: string[];
|
|
83
|
+
api?: string;
|
|
84
|
+
doc?: string;
|
|
85
|
+
npm?: string;
|
|
86
|
+
models?: Record<string, ModelsDevModel>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Fetch the models.dev catalog with full hardening. Concurrent callers
|
|
90
|
+
* share the same in-flight promise.
|
|
91
|
+
*/
|
|
92
|
+
export declare function loadModelsDevCatalog(): Promise<Record<string, ModelsDevProvider>>;
|
|
93
|
+
/** Test-only helper to clear the cache between runs. */
|
|
94
|
+
export declare function resetModelsDevCatalogCache(): void;
|
|
95
|
+
//# sourceMappingURL=modelsdev-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelsdev-catalog.d.ts","sourceRoot":"","sources":["../../src/discovery/modelsdev-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,gCAAgC,CAAC;AAWnE,qFAAqF;AACrF,MAAM,WAAW,aAAa;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,6CAA6C;IAC7C,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAED,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAED,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAQjF;AAED,wDAAwD;AACxD,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kosha-discovery — Shared models.dev catalog loader.
|
|
3
|
+
*
|
|
4
|
+
* Pulls the keyless community catalog at https://models.dev/api.json,
|
|
5
|
+
* which is maintained by the SST team and tends to surface new models
|
|
6
|
+
* faster than LiteLLM. Same hardening contract as the LiteLLM loader:
|
|
7
|
+
* HTTPS-pinned URL, assertCleanPayload scan, bounded body read, entry-count
|
|
8
|
+
* cap, promise-deduped singleton.
|
|
9
|
+
*
|
|
10
|
+
* Schema (top level):
|
|
11
|
+
* {
|
|
12
|
+
* [providerSlug]: {
|
|
13
|
+
* id, name, env: string[], doc, npm, api,
|
|
14
|
+
* models: {
|
|
15
|
+
* [modelId]: {
|
|
16
|
+
* id, name, family,
|
|
17
|
+
* attachment, reasoning, tool_call, structured_output, temperature,
|
|
18
|
+
* knowledge, release_date, last_updated,
|
|
19
|
+
* modalities: { input: string[], output: string[] },
|
|
20
|
+
* open_weights,
|
|
21
|
+
* cost: { input, output, cache_read?, cache_write?, ... },
|
|
22
|
+
* limit: { context, output, input? }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import { assertCleanPayload } from "../security.js";
|
|
30
|
+
/** Pinned upstream catalog URL — HTTPS only. */
|
|
31
|
+
export const MODELSDEV_CATALOG_URL = "https://models.dev/api.json";
|
|
32
|
+
/** Maximum response payload size (8 MB) — current catalog is ~600 KB. */
|
|
33
|
+
const MAX_PAYLOAD_BYTES = 8 * 1024 * 1024;
|
|
34
|
+
/** Maximum top-level provider entry count we accept. */
|
|
35
|
+
const MAX_PROVIDER_ENTRIES = 1_000;
|
|
36
|
+
/** Network timeout for the catalog fetch. */
|
|
37
|
+
const FETCH_TIMEOUT_MS = 15_000;
|
|
38
|
+
/** Module-level promise-dedup cache. */
|
|
39
|
+
let inflight = null;
|
|
40
|
+
/**
|
|
41
|
+
* Fetch the models.dev catalog with full hardening. Concurrent callers
|
|
42
|
+
* share the same in-flight promise.
|
|
43
|
+
*/
|
|
44
|
+
export function loadModelsDevCatalog() {
|
|
45
|
+
if (inflight)
|
|
46
|
+
return inflight;
|
|
47
|
+
inflight = fetchAndValidate().catch((error) => {
|
|
48
|
+
// Reset on failure so future calls can retry.
|
|
49
|
+
inflight = null;
|
|
50
|
+
throw error;
|
|
51
|
+
});
|
|
52
|
+
return inflight;
|
|
53
|
+
}
|
|
54
|
+
/** Test-only helper to clear the cache between runs. */
|
|
55
|
+
export function resetModelsDevCatalogCache() {
|
|
56
|
+
inflight = null;
|
|
57
|
+
}
|
|
58
|
+
async function fetchAndValidate() {
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
61
|
+
let response;
|
|
62
|
+
try {
|
|
63
|
+
response = await fetch(MODELSDEV_CATALOG_URL, { signal: controller.signal });
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
}
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`Failed to fetch models.dev catalog: ${response.status} ${response.statusText}`);
|
|
70
|
+
}
|
|
71
|
+
const text = await readBoundedText(response, MAX_PAYLOAD_BYTES);
|
|
72
|
+
let parsed;
|
|
73
|
+
try {
|
|
74
|
+
parsed = JSON.parse(text);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
throw new Error("Failed to parse models.dev catalog: invalid JSON");
|
|
78
|
+
}
|
|
79
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
80
|
+
throw new Error("Failed to parse models.dev catalog: expected an object");
|
|
81
|
+
}
|
|
82
|
+
assertCleanPayload(parsed, "models.dev");
|
|
83
|
+
const entries = Object.entries(parsed);
|
|
84
|
+
if (entries.length > MAX_PROVIDER_ENTRIES) {
|
|
85
|
+
throw new Error(`models.dev catalog exceeds provider cap (${entries.length} > ${MAX_PROVIDER_ENTRIES}) — refusing to load`);
|
|
86
|
+
}
|
|
87
|
+
const out = Object.create(null);
|
|
88
|
+
for (const [key, value] of entries) {
|
|
89
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
90
|
+
out[key] = value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/** Stream-read a Response body but cap total bytes received. */
|
|
96
|
+
async function readBoundedText(response, maxBytes) {
|
|
97
|
+
const contentLength = Number(response.headers.get("content-length") ?? "0");
|
|
98
|
+
if (contentLength > maxBytes) {
|
|
99
|
+
throw new Error(`models.dev catalog too large (${contentLength} > ${maxBytes} bytes) — refusing to load`);
|
|
100
|
+
}
|
|
101
|
+
if (!response.body) {
|
|
102
|
+
const text = await response.text();
|
|
103
|
+
if (text.length > maxBytes) {
|
|
104
|
+
throw new Error(`models.dev catalog too large (${text.length} > ${maxBytes} chars) — refusing to load`);
|
|
105
|
+
}
|
|
106
|
+
return text;
|
|
107
|
+
}
|
|
108
|
+
const reader = response.body.getReader();
|
|
109
|
+
const decoder = new TextDecoder("utf-8");
|
|
110
|
+
let received = 0;
|
|
111
|
+
let text = "";
|
|
112
|
+
while (true) {
|
|
113
|
+
const { done, value } = await reader.read();
|
|
114
|
+
if (done)
|
|
115
|
+
break;
|
|
116
|
+
received += value.byteLength;
|
|
117
|
+
if (received > maxBytes) {
|
|
118
|
+
try {
|
|
119
|
+
await reader.cancel();
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
/* swallow */
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`models.dev catalog too large (>${maxBytes} bytes) — refusing to load`);
|
|
125
|
+
}
|
|
126
|
+
text += decoder.decode(value, { stream: true });
|
|
127
|
+
}
|
|
128
|
+
text += decoder.decode();
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=modelsdev-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelsdev-catalog.js","sourceRoot":"","sources":["../../src/discovery/modelsdev-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kosha-discovery — models.dev public-catalog seed.
|
|
3
|
+
*
|
|
4
|
+
* Translates the keyless models.dev catalog into normalized {@link ModelCard}
|
|
5
|
+
* arrays per kosha provider. Same defensive posture as the LiteLLM seed:
|
|
6
|
+
* strict ID charset, length cap, model-count cap, no spreading of arbitrary
|
|
7
|
+
* fields into ModelCard.
|
|
8
|
+
*
|
|
9
|
+
* models.dev publishes pricing already in per-million-tokens, so the
|
|
10
|
+
* conversion done by the LiteLLM seed is unnecessary here.
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { ModelCard } from "../types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Build seed {@link ModelCard}s for the given kosha provider, sourced from
|
|
16
|
+
* models.dev. Returns `[]` if the provider has no entries.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getModelsDevSeed(providerId: string): Promise<ModelCard[]>;
|
|
19
|
+
//# sourceMappingURL=modelsdev-seed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelsdev-seed.d.ts","sourceRoot":"","sources":["../../src/discovery/modelsdev-seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA2B,MAAM,aAAa,CAAC;AA8CtE;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CA4B/E"}
|