@nebutra/ai-providers 1.0.1 → 3.0.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/dist/catalog.d.ts CHANGED
@@ -93,7 +93,32 @@ declare function providerForModel(modelId: string): Promise<AIProviderId | null>
93
93
  /** Last-resort id-prefix heuristic (only used when the catalog misses). */
94
94
  declare function providerFromIdHeuristic(modelId: string): AIProviderId | null;
95
95
  type ModelTier = "reasoning" | "flagship" | "fast" | "openai-flagship" | "google-flagship" | "google-fast";
96
- /** The hardcoded frontier fallbacks, exported so consumers can mirror them. */
96
+ interface TierRule {
97
+ /** Match on the OpenRouter (gateway) id. */
98
+ include: RegExp;
99
+ /**
100
+ * Drop variants that are not the interactive frontier model of the tier.
101
+ *
102
+ * `:batch` belongs in every one of these. OpenRouter lists a batch-only
103
+ * endpoint alongside most frontier models; it satisfies the family pattern,
104
+ * and routing a live stream at one fails. Before 2026-08 nothing excluded
105
+ * it and only Set iteration order kept it from being selected.
106
+ */
107
+ exclude: RegExp;
108
+ }
109
+ /**
110
+ * Tier matching rules. Exported so `scripts/generate-frontier-models.mjs` can be
111
+ * checked against them — the generator has to select by the same rules the
112
+ * runtime resolves by, or the offline fallback stops matching the online pick.
113
+ */
114
+ declare const TIER_RULES: Record<ModelTier, TierRule>;
115
+ /**
116
+ * Offline frontier fallbacks — GENERATED from the live gateway catalogue by
117
+ * `pnpm gen:frontier-models`, not hand-typed. They used to be `fallback` fields
118
+ * on the rules above with an audit date in a comment, and by 2026-08 they named
119
+ * two-generations-old models that still routed fine, so nothing failed and the
120
+ * only symptom was weaker output.
121
+ */
97
122
  declare const FRONTIER_FALLBACK: Record<ModelTier, string>;
98
123
  /**
99
124
  * Resolve a semantic tier to the newest concrete model id that is BOTH
@@ -125,4 +150,4 @@ declare function resolveModelSpec(spec: ModelSpecHints, fallback: string): Promi
125
150
  */
126
151
  declare function providerBaseUrl(provider: AIProviderId): string | null;
127
152
 
128
- export { type AIProviderId, type CatalogModel, FRONTIER_FALLBACK, type ModelInfo, type ModelModality, type ModelOffering, type ModelSpecHints, type ModelTier, PROVIDER_MAP, getModelInfo, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
153
+ export { type AIProviderId, type CatalogModel, FRONTIER_FALLBACK, type ModelInfo, type ModelModality, type ModelOffering, type ModelSpecHints, type ModelTier, PROVIDER_MAP, TIER_RULES, type TierRule, getModelInfo, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
package/dist/catalog.js CHANGED
@@ -1,5 +1,17 @@
1
1
  import { fetchModels } from '@tokenlens/fetch';
2
2
 
3
+ // src/catalog.ts
4
+
5
+ // src/frontier-fallback.generated.ts
6
+ var FRONTIER_FALLBACK = {
7
+ reasoning: "anthropic/claude-opus-5",
8
+ flagship: "anthropic/claude-sonnet-5",
9
+ fast: "anthropic/claude-haiku-4.5",
10
+ "openai-flagship": "openai/gpt-5.6-sol",
11
+ "google-flagship": "google/gemini-3.1-pro-preview",
12
+ "google-fast": "google/gemini-3.7-flash"
13
+ };
14
+
3
15
  // src/catalog.ts
4
16
  var PROVIDER_MAP = {
5
17
  openai: "OPENAI",
@@ -173,41 +185,31 @@ function providerFromIdHeuristic(modelId) {
173
185
  var TIER_RULES = {
174
186
  reasoning: {
175
187
  include: /^anthropic\/claude-opus-/,
176
- exclude: /-(fast|mini|nano|image|codex)/,
177
- fallback: "anthropic/claude-opus-4.8"
188
+ exclude: /-(fast|mini|nano|image|codex)|:batch/
178
189
  },
179
190
  flagship: {
180
191
  include: /^anthropic\/claude-sonnet-/,
181
- exclude: /-(fast|mini|nano|image|codex)/,
182
- fallback: "anthropic/claude-sonnet-4.6"
192
+ exclude: /-(fast|mini|nano|image|codex)|:batch/
183
193
  },
184
194
  fast: {
185
195
  include: /^anthropic\/claude-haiku-/,
186
- exclude: /-(image|codex)/,
187
- fallback: "anthropic/claude-haiku-4.5"
196
+ exclude: /-(image|codex)|:batch/
188
197
  },
189
198
  "openai-flagship": {
190
199
  include: /^openai\/gpt-5/,
191
200
  // Prefer full flagship over mini/nano/chat/image/codex variants
192
- exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\.\d+-(mini|nano|pro)/,
193
- // models.dev current OpenAI flagship (audited 2026-07); live resolver may pick newer
194
- fallback: "openai/gpt-5.5"
201
+ exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\.\d+-(mini|nano|pro)|:batch/
195
202
  },
196
203
  "google-flagship": {
197
204
  include: /^google\/gemini-.*pro/,
198
- exclude: /-(image|tts|customtools)/,
199
- fallback: "google/gemini-3.1-pro-preview"
205
+ exclude: /-(image|tts|customtools)|:batch/
200
206
  },
201
207
  "google-fast": {
202
208
  include: /^google\/gemini-.*flash/,
203
- exclude: /-(image|tts|lite)/,
204
- // models.dev: gemini-3.5-flash / 3.6-flash class
205
- fallback: "google/gemini-3.5-flash"
209
+ exclude: /-(image|tts|lite)|:batch/
206
210
  }
207
211
  };
208
- var FRONTIER_FALLBACK = Object.fromEntries(
209
- Object.entries(TIER_RULES).map(([tier, rule]) => [tier, rule.fallback])
210
- );
212
+ var FRONTIER_FALLBACK2 = FRONTIER_FALLBACK;
211
213
  var OPENROUTER_MODELS_URL = process.env.OPENROUTER_MODELS_URL ?? "https://openrouter.ai/api/v1/models";
212
214
  var routableCache = null;
213
215
  var routableInflight = null;
@@ -245,18 +247,19 @@ var versionOf = (id) => {
245
247
  };
246
248
  async function resolveFrontierModel(tier) {
247
249
  const rule = TIER_RULES[tier];
250
+ const fallback = FRONTIER_FALLBACK2[tier];
248
251
  try {
249
252
  const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);
250
- if (routable.size === 0) return rule.fallback;
253
+ if (routable.size === 0) return fallback;
251
254
  const metaNorm = new Set([...metaIndex.keys()].map(collapseId));
252
255
  const candidates = [...routable].filter(
253
256
  (id) => rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id)))
254
257
  );
255
- if (candidates.length === 0) return rule.fallback;
258
+ if (candidates.length === 0) return fallback;
256
259
  candidates.sort((a, b) => versionOf(b) - versionOf(a));
257
- return candidates[0] ?? rule.fallback;
260
+ return candidates[0] ?? fallback;
258
261
  } catch {
259
- return rule.fallback;
262
+ return fallback;
260
263
  }
261
264
  }
262
265
  var EFFORT_TIER = {
@@ -307,6 +310,6 @@ function providerBaseUrl(provider) {
307
310
  }
308
311
  }
309
312
 
310
- export { FRONTIER_FALLBACK, PROVIDER_MAP, getModelInfo, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
313
+ export { FRONTIER_FALLBACK2 as FRONTIER_FALLBACK, PROVIDER_MAP, TIER_RULES, getModelInfo, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
311
314
  //# sourceMappingURL=catalog.js.map
312
315
  //# sourceMappingURL=catalog.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/catalog.ts"],"names":[],"mappings":"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","file":"catalog.js","sourcesContent":["/**\n * Model catalog — the single source of model/provider FACTS.\n *\n * Data comes from the upstream community catalog https://models.dev (via\n * `@tokenlens/fetch`), so model ids, providers, pricing, context windows and\n * capabilities are NOT hand-maintained here. Replaces the scattered hardcoded\n * lists (BYOK provider regex, the /models endpoint, seed fallbacks).\n *\n * Delivery: runtime fetch + in-memory TTL cache. The first call fetches; later\n * calls serve from cache until `MODEL_CATALOG_TTL_MS` (default 6h) elapses. On\n * fetch failure the last good catalog is reused, so a models.dev outage degrades\n * to \"slightly stale\" rather than \"down\". Accessors are async by design.\n */\n\nimport type { ProviderModel, ProvidersCatalog } from \"@tokenlens/core\";\nimport { fetchModels } from \"@tokenlens/fetch\";\n\n/** Our coarse provider buckets — one BYOK key + base URL per bucket. */\nexport type AIProviderId = \"OPENAI\" | \"ANTHROPIC\" | \"GOOGLE\" | \"SILICONFLOW\" | \"CUSTOM\";\n\n/**\n * Map models.dev provider IDs → our AIProvider buckets. Canonical home (the DB\n * seed imports this, instead of keeping a parallel copy). Unknown → CUSTOM.\n */\nexport const PROVIDER_MAP: Record<string, AIProviderId> = {\n openai: \"OPENAI\",\n anthropic: \"ANTHROPIC\",\n google: \"GOOGLE\",\n \"google-vertex\": \"GOOGLE\",\n \"google-vertex-anthropic\": \"ANTHROPIC\",\n \"amazon-bedrock\": \"ANTHROPIC\", // most bedrock usage is claude\n siliconflow: \"SILICONFLOW\",\n \"siliconflow-cn\": \"SILICONFLOW\", // SiliconFlow ecosystem (GLM / Qwen / DeepSeek / MiniMax / …)\n deepseek: \"SILICONFLOW\", // deepseek is siliconflow-compatible\n alibaba: \"SILICONFLOW\",\n \"alibaba-token-plan\": \"SILICONFLOW\",\n qwen: \"SILICONFLOW\",\n moonshot: \"SILICONFLOW\",\n zhipu: \"SILICONFLOW\",\n minimax: \"SILICONFLOW\",\n};\n\nexport function mapProvider(modelsDevProviderId: string): AIProviderId {\n return PROVIDER_MAP[modelsDevProviderId] ?? \"CUSTOM\";\n}\n\n/**\n * Output modality — the primary dimension for canvas orchestration (\"give me a\n * video model\", \"an image model\", …). Derived from models.dev `modalities.output`.\n */\nexport type ModelModality = \"text\" | \"image\" | \"video\" | \"audio\" | \"embedding\";\n\n/** Classify a model by what it PRODUCES (not what it consumes). */\nfunction classifyModality(model: ProviderModel): ModelModality {\n const out = model.modalities?.output ?? [];\n if (out.includes(\"video\")) return \"video\";\n if (out.includes(\"image\")) return \"image\";\n if (out.includes(\"audio\")) return \"audio\";\n if (/embedding/i.test(model.id) || out.includes(\"embedding\")) return \"embedding\";\n return \"text\";\n}\n\n/** Normalized, provider-agnostic model facts surfaced to the rest of the app. */\nexport interface ModelInfo {\n id: string;\n name: string;\n /** Our coarse bucket (for BYOK key selection / base URL). */\n provider: AIProviderId;\n /** The raw models.dev provider id (e.g. \"google-vertex\"). */\n rawProvider: string;\n /** What the model produces — the canvas-orchestration grouping key. */\n modality: ModelModality;\n contextWindow?: number;\n maxOutput?: number;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n}\n\nfunction toModelInfo(rawProvider: string, model: ProviderModel): ModelInfo {\n const visionIn = model.modalities?.input?.includes(\"image\") ?? false;\n return {\n id: model.id,\n name: model.name ?? model.id,\n provider: mapProvider(rawProvider),\n rawProvider,\n modality: classifyModality(model),\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n ...(model.limit?.output !== undefined ? { maxOutput: model.limit.output } : {}),\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: visionIn,\n },\n };\n}\n\n// ─── Deduped, modality-grouped view (for canvas orchestration) ─────────────────\n//\n// models.dev lists the SAME logical model under dozens of aggregator providers\n// (e.g. minimax-m2.5 appears under 47). The canvas needs ONE entry per logical\n// model with the list of providers that can serve it (so it can pick a routable\n// id + compare cost). `dedupKey` collapses the bare name across aggregators.\n\n/** A single provider's offering of a logical model. */\nexport interface ModelOffering {\n /** Routable model id as listed by models.dev (aggregator-prefixed). */\n id: string;\n rawProvider: string;\n bucket: AIProviderId;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n contextWindow?: number;\n}\n\n/** One deduped logical model, with all the providers that serve it. */\nexport interface CatalogModel {\n key: string;\n name: string;\n modality: ModelModality;\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n /** Distinct provider buckets that serve it (for BYOK key selection). */\n buckets: AIProviderId[];\n offerings: ModelOffering[];\n}\n\n/** Collapse a model id to a logical key (drop aggregator path + separators). */\nfunction dedupKey(modelId: string): string {\n return modelId\n .toLowerCase()\n .replace(/^.*\\//, \"\") // keep only the final path segment\n .replace(/[._:\\-\\s]/g, \"\");\n}\n\n// ─── Runtime cache ────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n /** Per-id view (exact lookups: providerForModel, getModelInfo, resolveFrontier). */\n index: Map<string, ModelInfo>;\n /** Deduped logical view (canvas orchestration: listModelsByModality). */\n logical: CatalogModel[];\n fetchedAt: number;\n}\n\nconst TTL_MS = (() => {\n const n = Number.parseInt(process.env.MODEL_CATALOG_TTL_MS ?? \"21600000\", 10); // 6h\n return Number.isFinite(n) && n > 0 ? n : 21_600_000;\n})();\n\nlet cache: CacheEntry | null = null;\nlet inflight: Promise<CacheEntry> | null = null;\n\nfunction buildIndex(catalog: ProvidersCatalog): Map<string, ModelInfo> {\n const index = new Map<string, ModelInfo>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n // First writer wins; model ids are unique within models.dev.\n if (!index.has(model.id)) index.set(model.id, toModelInfo(providerId, model));\n }\n }\n return index;\n}\n\nfunction buildLogical(catalog: ProvidersCatalog): CatalogModel[] {\n const byKey = new Map<string, CatalogModel>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n const key = dedupKey(model.id);\n if (!key) continue;\n const bucket = mapProvider(providerId);\n const offering: ModelOffering = {\n id: model.id,\n rawProvider: providerId,\n bucket,\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n };\n const existing = byKey.get(key);\n if (existing) {\n existing.offerings.push(offering);\n if (!existing.buckets.includes(bucket)) existing.buckets.push(bucket);\n // Capabilities are the union across offerings.\n existing.capabilities.reasoning ||= model.reasoning ?? false;\n existing.capabilities.toolCall ||= model.tool_call ?? false;\n existing.capabilities.vision ||= model.modalities?.input?.includes(\"image\") ?? false;\n } else {\n byKey.set(key, {\n key,\n name: model.name ?? model.id,\n modality: classifyModality(model),\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: model.modalities?.input?.includes(\"image\") ?? false,\n },\n buckets: [bucket],\n offerings: [offering],\n });\n }\n }\n }\n return [...byKey.values()];\n}\n\nasync function loadCache(): Promise<CacheEntry> {\n const now = Date.now();\n if (cache && now - cache.fetchedAt < TTL_MS) return cache;\n if (inflight) return inflight;\n\n inflight = (async () => {\n try {\n const catalog = await fetchModels();\n cache = { index: buildIndex(catalog), logical: buildLogical(catalog), fetchedAt: Date.now() };\n return cache;\n } catch {\n // models.dev unreachable — reuse the last good catalog if we have one,\n // otherwise empty (callers degrade safely, e.g. BYOK → platform).\n if (cache) return cache;\n cache = { index: new Map(), logical: [], fetchedAt: Date.now() };\n return cache;\n } finally {\n inflight = null;\n }\n })();\n\n return inflight;\n}\n\nasync function loadIndex(): Promise<Map<string, ModelInfo>> {\n return (await loadCache()).index;\n}\n\n// ─── Public accessors ─────────────────────────────────────────────────────────\n\n/** Full model list (newest fetch, cached). */\nexport async function listModels(): Promise<ModelInfo[]> {\n return [...(await loadIndex()).values()];\n}\n\n/**\n * Deduped logical models for a given output modality — the canvas-orchestration\n * entry point. One entry per logical model, with the providers that serve it.\n * Sorted by offering count (most widely available first).\n */\nexport async function listModelsByModality(modality: ModelModality): Promise<CatalogModel[]> {\n const { logical } = await loadCache();\n return logical\n .filter((m) => m.modality === modality)\n .sort((a, b) => b.offerings.length - a.offerings.length);\n}\n\n/** Count of deduped logical models per modality (for canvas tab badges, etc.). */\nexport async function listModalities(): Promise<Record<ModelModality, number>> {\n const { logical } = await loadCache();\n const counts: Record<ModelModality, number> = {\n text: 0,\n image: 0,\n video: 0,\n audio: 0,\n embedding: 0,\n };\n for (const m of logical) counts[m.modality]++;\n return counts;\n}\n\n/** Look up a single model by id. `null` if unknown to the catalog. */\nexport async function getModelInfo(modelId: string): Promise<ModelInfo | null> {\n return (await loadIndex()).get(modelId) ?? null;\n}\n\n/**\n * Resolve the coarse provider bucket for a model id. Catalog-first; falls back\n * to id-prefix heuristics for brand-new models models.dev hasn't indexed yet.\n * `null` means \"unknown\" → callers should use the platform default.\n */\nexport async function providerForModel(modelId: string): Promise<AIProviderId | null> {\n const info = await getModelInfo(modelId);\n if (info) return info.provider;\n return providerFromIdHeuristic(modelId);\n}\n\n/** Last-resort id-prefix heuristic (only used when the catalog misses). */\nexport function providerFromIdHeuristic(modelId: string): AIProviderId | null {\n const m = modelId.toLowerCase().replace(/^[^/]+\\//, \"\"); // strip \"anthropic/\" style prefix\n if (/^(gpt-|o1|o3|o4|chatgpt|text-|davinci|babbage)/.test(m)) return \"OPENAI\";\n if (m.startsWith(\"claude\")) return \"ANTHROPIC\";\n if (m.startsWith(\"gemini\")) return \"GOOGLE\";\n if (/deepseek|qwen|glm|yi-|internlm|siliconflow|moonshot|kimi/.test(m)) return \"SILICONFLOW\";\n return null;\n}\n\n// ─── Frontier resolver (hybrid: live intersection + hardcoded fallback) ─────────\n//\n// Resolves a SEMANTIC tier (e.g. \"the latest reasoning model\") to a concrete\n// model id, picking the newest from the INTERSECTION of:\n// - gateway-routable ids (OpenRouter /api/v1/models — the gateway namespace), and\n// - models.dev catalog metadata (so the pick has pricing/context/capabilities).\n// On any failure (offline / empty) it returns the hardcoded fallback, so callers\n// always get a usable id. This is the \"don't hand-maintain model strings\" engine.\n\nexport type ModelTier =\n | \"reasoning\"\n | \"flagship\"\n | \"fast\"\n | \"openai-flagship\"\n | \"google-flagship\"\n | \"google-fast\";\n\ninterface TierRule {\n /** Match on the OpenRouter (gateway) id. */\n include: RegExp;\n /** Drop non-frontier variants (fast/mini/codex/image/…). */\n exclude: RegExp;\n /** Current frontier (audited 2026-06-05) — used when the live lists are unavailable. */\n fallback: string;\n}\n\nconst TIER_RULES: Record<ModelTier, TierRule> = {\n reasoning: {\n include: /^anthropic\\/claude-opus-/,\n exclude: /-(fast|mini|nano|image|codex)/,\n fallback: \"anthropic/claude-opus-4.8\",\n },\n flagship: {\n include: /^anthropic\\/claude-sonnet-/,\n exclude: /-(fast|mini|nano|image|codex)/,\n fallback: \"anthropic/claude-sonnet-4.6\",\n },\n fast: {\n include: /^anthropic\\/claude-haiku-/,\n exclude: /-(image|codex)/,\n fallback: \"anthropic/claude-haiku-4.5\",\n },\n \"openai-flagship\": {\n include: /^openai\\/gpt-5/,\n // Prefer full flagship over mini/nano/chat/image/codex variants\n exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\\.\\d+-(mini|nano|pro)/,\n // models.dev current OpenAI flagship (audited 2026-07); live resolver may pick newer\n fallback: \"openai/gpt-5.5\",\n },\n \"google-flagship\": {\n include: /^google\\/gemini-.*pro/,\n exclude: /-(image|tts|customtools)/,\n fallback: \"google/gemini-3.1-pro-preview\",\n },\n \"google-fast\": {\n include: /^google\\/gemini-.*flash/,\n exclude: /-(image|tts|lite)/,\n // models.dev: gemini-3.5-flash / 3.6-flash class\n fallback: \"google/gemini-3.5-flash\",\n },\n};\n\n/** The hardcoded frontier fallbacks, exported so consumers can mirror them. */\nexport const FRONTIER_FALLBACK: Record<ModelTier, string> = Object.fromEntries(\n Object.entries(TIER_RULES).map(([tier, rule]) => [tier, rule.fallback]),\n) as Record<ModelTier, string>;\n\nconst OPENROUTER_MODELS_URL =\n process.env.OPENROUTER_MODELS_URL ?? \"https://openrouter.ai/api/v1/models\";\n\nlet routableCache: { ids: Set<string>; fetchedAt: number } | null = null;\nlet routableInflight: Promise<Set<string>> | null = null;\n\n/** Gateway-routable model ids (OpenRouter), cached with the same TTL as the catalog. */\nasync function fetchRoutableIds(): Promise<Set<string>> {\n const now = Date.now();\n if (routableCache && now - routableCache.fetchedAt < TTL_MS) return routableCache.ids;\n if (routableInflight) return routableInflight;\n\n routableInflight = (async () => {\n try {\n const res = await fetch(OPENROUTER_MODELS_URL);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const json = (await res.json()) as { data?: Array<{ id?: string }> };\n const ids = new Set(\n (json.data ?? []).map((m) => m.id).filter((x): x is string => typeof x === \"string\"),\n );\n routableCache = { ids, fetchedAt: Date.now() };\n return ids;\n } catch {\n return routableCache?.ids ?? new Set<string>();\n } finally {\n routableInflight = null;\n }\n })();\n\n return routableInflight;\n}\n\n/** Collapse separators so `claude-opus-4.8` (OpenRouter) matches `claude-opus-4-8` (models.dev). */\nconst collapseId = (s: string): string => s.toLowerCase().replace(/[._-]/g, \"\");\nconst bareId = (id: string): string => {\n const slash = id.indexOf(\"/\");\n return slash >= 0 ? id.slice(slash + 1) : id;\n};\nconst versionOf = (id: string): number => {\n const m = bareId(id).match(/(\\d+(?:\\.\\d+)?)/);\n const raw = m?.[1];\n return raw ? Number.parseFloat(raw) : -1;\n};\n\n/**\n * Resolve a semantic tier to the newest concrete model id that is BOTH\n * gateway-routable and present in the models.dev catalog. Falls back to the\n * audited hardcoded frontier when the live lists are unavailable.\n */\nexport async function resolveFrontierModel(tier: ModelTier): Promise<string> {\n const rule = TIER_RULES[tier];\n try {\n const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);\n if (routable.size === 0) return rule.fallback;\n\n const metaNorm = new Set([...metaIndex.keys()].map(collapseId));\n const candidates = [...routable].filter(\n (id) =>\n rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id))),\n );\n if (candidates.length === 0) return rule.fallback;\n\n candidates.sort((a, b) => versionOf(b) - versionOf(a));\n return candidates[0] ?? rule.fallback;\n } catch {\n return rule.fallback;\n }\n}\n\n// ─── Per-node model spec resolution ─────────────────────────────────────────────\n//\n// Resolves a node's model HINTS (id pin OR modality/effort/capability) to a\n// concrete model id. Mirrors agent-runtime NodeModelSpec structurally (plain\n// object — no cross-package dependency). Used by the workflow/agent executor.\n\nexport interface ModelSpecHints {\n /** Explicit pin — wins over every hint. Preset alias or `provider/model` id. */\n id?: string;\n reasoningEffort?: \"low\" | \"medium\" | \"high\" | \"xhigh\";\n modality?: ModelModality;\n capabilities?: { vision?: boolean; toolCall?: boolean; reasoning?: boolean };\n}\n\nconst EFFORT_TIER: Record<NonNullable<ModelSpecHints[\"reasoningEffort\"]>, ModelTier> = {\n low: \"fast\",\n medium: \"flagship\",\n high: \"flagship\",\n xhigh: \"reasoning\",\n};\n\nfunction pickByCapabilities(\n models: CatalogModel[],\n caps?: ModelSpecHints[\"capabilities\"],\n): CatalogModel | null {\n const filtered = caps\n ? models.filter(\n (m) =>\n (!caps.vision || m.capabilities.vision) &&\n (!caps.toolCall || m.capabilities.toolCall) &&\n (!caps.reasoning || m.capabilities.reasoning),\n )\n : models;\n return filtered[0] ?? null;\n}\n\n/**\n * Resolve a per-node model spec to a concrete model id. Precedence: explicit\n * `id` → non-text `modality` (most-available matching model) → `reasoningEffort`\n * tier (text frontier) → text `capabilities` match → `fallback` (the run\n * default). Never throws — degrades to `fallback`.\n */\nexport async function resolveModelSpec(spec: ModelSpecHints, fallback: string): Promise<string> {\n try {\n if (spec.id) return spec.id;\n\n if (spec.modality && spec.modality !== \"text\") {\n const match = pickByCapabilities(\n await listModelsByModality(spec.modality),\n spec.capabilities,\n );\n if (match) return match.offerings[0]?.id ?? match.key;\n return fallback;\n }\n\n if (spec.reasoningEffort) return resolveFrontierModel(EFFORT_TIER[spec.reasoningEffort]);\n\n if (spec.capabilities) {\n const match = pickByCapabilities(await listModelsByModality(\"text\"), spec.capabilities);\n if (match) return match.offerings[0]?.id ?? match.key;\n }\n\n return fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Default OpenAI-compatible base URL per provider bucket (env-overridable).\n * Single home for these — BYOK and any other caller import from here.\n */\nexport function providerBaseUrl(provider: AIProviderId): string | null {\n switch (provider) {\n case \"OPENAI\":\n return process.env.OPENAI_BASE_URL ?? \"https://api.openai.com/v1\";\n case \"ANTHROPIC\":\n return process.env.ANTHROPIC_BASE_URL ?? \"https://api.anthropic.com/v1\";\n case \"GOOGLE\":\n return (\n process.env.GOOGLE_BASE_URL ?? \"https://generativelanguage.googleapis.com/v1beta/openai\"\n );\n case \"SILICONFLOW\":\n return process.env.SILICONFLOW_BASE_URL ?? \"https://api.siliconflow.cn/v1\";\n default:\n // CUSTOM has no canonical endpoint — the caller must supply a baseUrl.\n return null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/frontier-fallback.generated.ts","../src/catalog.ts"],"names":["FRONTIER_FALLBACK"],"mappings":"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","file":"catalog.js","sourcesContent":["// GENERATED — do not edit by hand.\n// Run `pnpm gen:frontier-models` to refresh from the live gateway catalogue.\n//\n// The newest model OpenRouter routes for each tier, resolved 2026-08-21.\n// These are the offline fallbacks: `resolveFrontierModel()` re-resolves against\n// the live list at runtime and only lands here when that list is unreachable.\n\nexport const FRONTIER_FALLBACK = {\n reasoning: \"anthropic/claude-opus-5\",\n flagship: \"anthropic/claude-sonnet-5\",\n fast: \"anthropic/claude-haiku-4.5\",\n \"openai-flagship\": \"openai/gpt-5.6-sol\",\n \"google-flagship\": \"google/gemini-3.1-pro-preview\",\n \"google-fast\": \"google/gemini-3.7-flash\",\n} as const;\n\nexport type FrontierTier = keyof typeof FRONTIER_FALLBACK;\n\n/**\n * Tiers whose id 302.AI spells differently from the gateway. Only the tiers\n * that actually differ appear; everything else is served under the bare id.\n * A missing entry means \"no alias needed\", never \"unknown\".\n */\nexport const AI302_ALIASES: Partial<Record<FrontierTier, string>> = {\n fast: \"claude-haiku-4-5-20251001\",\n};\n\n/**\n * Newest member of each open-weight family 302.AI serves — the models the\n * Anthropic/OpenAI/Google tiers above do not cover. Ids are 302-native and bare,\n * so these presets only resolve against the `ai302` provider.\n */\nexport const AI302_OPEN_MODELS = {\n \"302-deepseek\": \"deepseek-v4-pro\",\n \"302-deepseek-fast\": \"deepseek-v4-flash\",\n \"302-qwen\": \"qwen3.8-max\",\n \"302-glm\": \"glm-5.3\",\n \"302-kimi\": \"kimi-k3\",\n \"302-minimax\": \"MiniMax-M3\",\n} as const;\n","/**\n * Model catalog — the single source of model/provider FACTS.\n *\n * Data comes from the upstream community catalog https://models.dev (via\n * `@tokenlens/fetch`), so model ids, providers, pricing, context windows and\n * capabilities are NOT hand-maintained here. Replaces the scattered hardcoded\n * lists (BYOK provider regex, the /models endpoint, seed fallbacks).\n *\n * Delivery: runtime fetch + in-memory TTL cache. The first call fetches; later\n * calls serve from cache until `MODEL_CATALOG_TTL_MS` (default 6h) elapses. On\n * fetch failure the last good catalog is reused, so a models.dev outage degrades\n * to \"slightly stale\" rather than \"down\". Accessors are async by design.\n */\n\nimport type { ProviderModel, ProvidersCatalog } from \"@tokenlens/core\";\nimport { fetchModels } from \"@tokenlens/fetch\";\nimport { FRONTIER_FALLBACK as GENERATED_FALLBACK } from \"./frontier-fallback.generated\";\n\n/** Our coarse provider buckets — one BYOK key + base URL per bucket. */\nexport type AIProviderId = \"OPENAI\" | \"ANTHROPIC\" | \"GOOGLE\" | \"SILICONFLOW\" | \"CUSTOM\";\n\n/**\n * Map models.dev provider IDs → our AIProvider buckets. Canonical home (the DB\n * seed imports this, instead of keeping a parallel copy). Unknown → CUSTOM.\n */\nexport const PROVIDER_MAP: Record<string, AIProviderId> = {\n openai: \"OPENAI\",\n anthropic: \"ANTHROPIC\",\n google: \"GOOGLE\",\n \"google-vertex\": \"GOOGLE\",\n \"google-vertex-anthropic\": \"ANTHROPIC\",\n \"amazon-bedrock\": \"ANTHROPIC\", // most bedrock usage is claude\n siliconflow: \"SILICONFLOW\",\n \"siliconflow-cn\": \"SILICONFLOW\", // SiliconFlow ecosystem (GLM / Qwen / DeepSeek / MiniMax / …)\n deepseek: \"SILICONFLOW\", // deepseek is siliconflow-compatible\n alibaba: \"SILICONFLOW\",\n \"alibaba-token-plan\": \"SILICONFLOW\",\n qwen: \"SILICONFLOW\",\n moonshot: \"SILICONFLOW\",\n zhipu: \"SILICONFLOW\",\n minimax: \"SILICONFLOW\",\n};\n\nexport function mapProvider(modelsDevProviderId: string): AIProviderId {\n return PROVIDER_MAP[modelsDevProviderId] ?? \"CUSTOM\";\n}\n\n/**\n * Output modality — the primary dimension for canvas orchestration (\"give me a\n * video model\", \"an image model\", …). Derived from models.dev `modalities.output`.\n */\nexport type ModelModality = \"text\" | \"image\" | \"video\" | \"audio\" | \"embedding\";\n\n/** Classify a model by what it PRODUCES (not what it consumes). */\nfunction classifyModality(model: ProviderModel): ModelModality {\n const out = model.modalities?.output ?? [];\n if (out.includes(\"video\")) return \"video\";\n if (out.includes(\"image\")) return \"image\";\n if (out.includes(\"audio\")) return \"audio\";\n if (/embedding/i.test(model.id) || out.includes(\"embedding\")) return \"embedding\";\n return \"text\";\n}\n\n/** Normalized, provider-agnostic model facts surfaced to the rest of the app. */\nexport interface ModelInfo {\n id: string;\n name: string;\n /** Our coarse bucket (for BYOK key selection / base URL). */\n provider: AIProviderId;\n /** The raw models.dev provider id (e.g. \"google-vertex\"). */\n rawProvider: string;\n /** What the model produces — the canvas-orchestration grouping key. */\n modality: ModelModality;\n contextWindow?: number;\n maxOutput?: number;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n}\n\nfunction toModelInfo(rawProvider: string, model: ProviderModel): ModelInfo {\n const visionIn = model.modalities?.input?.includes(\"image\") ?? false;\n return {\n id: model.id,\n name: model.name ?? model.id,\n provider: mapProvider(rawProvider),\n rawProvider,\n modality: classifyModality(model),\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n ...(model.limit?.output !== undefined ? { maxOutput: model.limit.output } : {}),\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: visionIn,\n },\n };\n}\n\n// ─── Deduped, modality-grouped view (for canvas orchestration) ─────────────────\n//\n// models.dev lists the SAME logical model under dozens of aggregator providers\n// (e.g. minimax-m2.5 appears under 47). The canvas needs ONE entry per logical\n// model with the list of providers that can serve it (so it can pick a routable\n// id + compare cost). `dedupKey` collapses the bare name across aggregators.\n\n/** A single provider's offering of a logical model. */\nexport interface ModelOffering {\n /** Routable model id as listed by models.dev (aggregator-prefixed). */\n id: string;\n rawProvider: string;\n bucket: AIProviderId;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n contextWindow?: number;\n}\n\n/** One deduped logical model, with all the providers that serve it. */\nexport interface CatalogModel {\n key: string;\n name: string;\n modality: ModelModality;\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n /** Distinct provider buckets that serve it (for BYOK key selection). */\n buckets: AIProviderId[];\n offerings: ModelOffering[];\n}\n\n/** Collapse a model id to a logical key (drop aggregator path + separators). */\nfunction dedupKey(modelId: string): string {\n return modelId\n .toLowerCase()\n .replace(/^.*\\//, \"\") // keep only the final path segment\n .replace(/[._:\\-\\s]/g, \"\");\n}\n\n// ─── Runtime cache ────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n /** Per-id view (exact lookups: providerForModel, getModelInfo, resolveFrontier). */\n index: Map<string, ModelInfo>;\n /** Deduped logical view (canvas orchestration: listModelsByModality). */\n logical: CatalogModel[];\n fetchedAt: number;\n}\n\nconst TTL_MS = (() => {\n const n = Number.parseInt(process.env.MODEL_CATALOG_TTL_MS ?? \"21600000\", 10); // 6h\n return Number.isFinite(n) && n > 0 ? n : 21_600_000;\n})();\n\nlet cache: CacheEntry | null = null;\nlet inflight: Promise<CacheEntry> | null = null;\n\nfunction buildIndex(catalog: ProvidersCatalog): Map<string, ModelInfo> {\n const index = new Map<string, ModelInfo>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n // First writer wins; model ids are unique within models.dev.\n if (!index.has(model.id)) index.set(model.id, toModelInfo(providerId, model));\n }\n }\n return index;\n}\n\nfunction buildLogical(catalog: ProvidersCatalog): CatalogModel[] {\n const byKey = new Map<string, CatalogModel>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n const key = dedupKey(model.id);\n if (!key) continue;\n const bucket = mapProvider(providerId);\n const offering: ModelOffering = {\n id: model.id,\n rawProvider: providerId,\n bucket,\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n };\n const existing = byKey.get(key);\n if (existing) {\n existing.offerings.push(offering);\n if (!existing.buckets.includes(bucket)) existing.buckets.push(bucket);\n // Capabilities are the union across offerings.\n existing.capabilities.reasoning ||= model.reasoning ?? false;\n existing.capabilities.toolCall ||= model.tool_call ?? false;\n existing.capabilities.vision ||= model.modalities?.input?.includes(\"image\") ?? false;\n } else {\n byKey.set(key, {\n key,\n name: model.name ?? model.id,\n modality: classifyModality(model),\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: model.modalities?.input?.includes(\"image\") ?? false,\n },\n buckets: [bucket],\n offerings: [offering],\n });\n }\n }\n }\n return [...byKey.values()];\n}\n\nasync function loadCache(): Promise<CacheEntry> {\n const now = Date.now();\n if (cache && now - cache.fetchedAt < TTL_MS) return cache;\n if (inflight) return inflight;\n\n inflight = (async () => {\n try {\n const catalog = await fetchModels();\n cache = { index: buildIndex(catalog), logical: buildLogical(catalog), fetchedAt: Date.now() };\n return cache;\n } catch {\n // models.dev unreachable — reuse the last good catalog if we have one,\n // otherwise empty (callers degrade safely, e.g. BYOK → platform).\n if (cache) return cache;\n cache = { index: new Map(), logical: [], fetchedAt: Date.now() };\n return cache;\n } finally {\n inflight = null;\n }\n })();\n\n return inflight;\n}\n\nasync function loadIndex(): Promise<Map<string, ModelInfo>> {\n return (await loadCache()).index;\n}\n\n// ─── Public accessors ─────────────────────────────────────────────────────────\n\n/** Full model list (newest fetch, cached). */\nexport async function listModels(): Promise<ModelInfo[]> {\n return [...(await loadIndex()).values()];\n}\n\n/**\n * Deduped logical models for a given output modality — the canvas-orchestration\n * entry point. One entry per logical model, with the providers that serve it.\n * Sorted by offering count (most widely available first).\n */\nexport async function listModelsByModality(modality: ModelModality): Promise<CatalogModel[]> {\n const { logical } = await loadCache();\n return logical\n .filter((m) => m.modality === modality)\n .sort((a, b) => b.offerings.length - a.offerings.length);\n}\n\n/** Count of deduped logical models per modality (for canvas tab badges, etc.). */\nexport async function listModalities(): Promise<Record<ModelModality, number>> {\n const { logical } = await loadCache();\n const counts: Record<ModelModality, number> = {\n text: 0,\n image: 0,\n video: 0,\n audio: 0,\n embedding: 0,\n };\n for (const m of logical) counts[m.modality]++;\n return counts;\n}\n\n/** Look up a single model by id. `null` if unknown to the catalog. */\nexport async function getModelInfo(modelId: string): Promise<ModelInfo | null> {\n return (await loadIndex()).get(modelId) ?? null;\n}\n\n/**\n * Resolve the coarse provider bucket for a model id. Catalog-first; falls back\n * to id-prefix heuristics for brand-new models models.dev hasn't indexed yet.\n * `null` means \"unknown\" → callers should use the platform default.\n */\nexport async function providerForModel(modelId: string): Promise<AIProviderId | null> {\n const info = await getModelInfo(modelId);\n if (info) return info.provider;\n return providerFromIdHeuristic(modelId);\n}\n\n/** Last-resort id-prefix heuristic (only used when the catalog misses). */\nexport function providerFromIdHeuristic(modelId: string): AIProviderId | null {\n const m = modelId.toLowerCase().replace(/^[^/]+\\//, \"\"); // strip \"anthropic/\" style prefix\n if (/^(gpt-|o1|o3|o4|chatgpt|text-|davinci|babbage)/.test(m)) return \"OPENAI\";\n if (m.startsWith(\"claude\")) return \"ANTHROPIC\";\n if (m.startsWith(\"gemini\")) return \"GOOGLE\";\n if (/deepseek|qwen|glm|yi-|internlm|siliconflow|moonshot|kimi/.test(m)) return \"SILICONFLOW\";\n return null;\n}\n\n// ─── Frontier resolver (hybrid: live intersection + hardcoded fallback) ─────────\n//\n// Resolves a SEMANTIC tier (e.g. \"the latest reasoning model\") to a concrete\n// model id, picking the newest from the INTERSECTION of:\n// - gateway-routable ids (OpenRouter /api/v1/models — the gateway namespace), and\n// - models.dev catalog metadata (so the pick has pricing/context/capabilities).\n// On any failure (offline / empty) it returns the hardcoded fallback, so callers\n// always get a usable id. This is the \"don't hand-maintain model strings\" engine.\n\nexport type ModelTier =\n | \"reasoning\"\n | \"flagship\"\n | \"fast\"\n | \"openai-flagship\"\n | \"google-flagship\"\n | \"google-fast\";\n\nexport interface TierRule {\n /** Match on the OpenRouter (gateway) id. */\n include: RegExp;\n /**\n * Drop variants that are not the interactive frontier model of the tier.\n *\n * `:batch` belongs in every one of these. OpenRouter lists a batch-only\n * endpoint alongside most frontier models; it satisfies the family pattern,\n * and routing a live stream at one fails. Before 2026-08 nothing excluded\n * it and only Set iteration order kept it from being selected.\n */\n exclude: RegExp;\n}\n\n/**\n * Tier matching rules. Exported so `scripts/generate-frontier-models.mjs` can be\n * checked against them — the generator has to select by the same rules the\n * runtime resolves by, or the offline fallback stops matching the online pick.\n */\nexport const TIER_RULES: Record<ModelTier, TierRule> = {\n reasoning: {\n include: /^anthropic\\/claude-opus-/,\n exclude: /-(fast|mini|nano|image|codex)|:batch/,\n },\n flagship: {\n include: /^anthropic\\/claude-sonnet-/,\n exclude: /-(fast|mini|nano|image|codex)|:batch/,\n },\n fast: {\n include: /^anthropic\\/claude-haiku-/,\n exclude: /-(image|codex)|:batch/,\n },\n \"openai-flagship\": {\n include: /^openai\\/gpt-5/,\n // Prefer full flagship over mini/nano/chat/image/codex variants\n exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\\.\\d+-(mini|nano|pro)|:batch/,\n },\n \"google-flagship\": {\n include: /^google\\/gemini-.*pro/,\n exclude: /-(image|tts|customtools)|:batch/,\n },\n \"google-fast\": {\n include: /^google\\/gemini-.*flash/,\n exclude: /-(image|tts|lite)|:batch/,\n },\n};\n\n/**\n * Offline frontier fallbacks — GENERATED from the live gateway catalogue by\n * `pnpm gen:frontier-models`, not hand-typed. They used to be `fallback` fields\n * on the rules above with an audit date in a comment, and by 2026-08 they named\n * two-generations-old models that still routed fine, so nothing failed and the\n * only symptom was weaker output.\n */\nexport const FRONTIER_FALLBACK: Record<ModelTier, string> = GENERATED_FALLBACK;\n\nconst OPENROUTER_MODELS_URL =\n process.env.OPENROUTER_MODELS_URL ?? \"https://openrouter.ai/api/v1/models\";\n\nlet routableCache: { ids: Set<string>; fetchedAt: number } | null = null;\nlet routableInflight: Promise<Set<string>> | null = null;\n\n/** Gateway-routable model ids (OpenRouter), cached with the same TTL as the catalog. */\nasync function fetchRoutableIds(): Promise<Set<string>> {\n const now = Date.now();\n if (routableCache && now - routableCache.fetchedAt < TTL_MS) return routableCache.ids;\n if (routableInflight) return routableInflight;\n\n routableInflight = (async () => {\n try {\n const res = await fetch(OPENROUTER_MODELS_URL);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const json = (await res.json()) as { data?: Array<{ id?: string }> };\n const ids = new Set(\n (json.data ?? []).map((m) => m.id).filter((x): x is string => typeof x === \"string\"),\n );\n routableCache = { ids, fetchedAt: Date.now() };\n return ids;\n } catch {\n return routableCache?.ids ?? new Set<string>();\n } finally {\n routableInflight = null;\n }\n })();\n\n return routableInflight;\n}\n\n/** Collapse separators so `claude-opus-4.8` (OpenRouter) matches `claude-opus-4-8` (models.dev). */\nconst collapseId = (s: string): string => s.toLowerCase().replace(/[._-]/g, \"\");\nconst bareId = (id: string): string => {\n const slash = id.indexOf(\"/\");\n return slash >= 0 ? id.slice(slash + 1) : id;\n};\nconst versionOf = (id: string): number => {\n const m = bareId(id).match(/(\\d+(?:\\.\\d+)?)/);\n const raw = m?.[1];\n return raw ? Number.parseFloat(raw) : -1;\n};\n\n/**\n * Resolve a semantic tier to the newest concrete model id that is BOTH\n * gateway-routable and present in the models.dev catalog. Falls back to the\n * audited hardcoded frontier when the live lists are unavailable.\n */\nexport async function resolveFrontierModel(tier: ModelTier): Promise<string> {\n const rule = TIER_RULES[tier];\n const fallback = FRONTIER_FALLBACK[tier];\n try {\n const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);\n if (routable.size === 0) return fallback;\n\n const metaNorm = new Set([...metaIndex.keys()].map(collapseId));\n const candidates = [...routable].filter(\n (id) =>\n rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id))),\n );\n if (candidates.length === 0) return fallback;\n\n candidates.sort((a, b) => versionOf(b) - versionOf(a));\n return candidates[0] ?? fallback;\n } catch {\n return fallback;\n }\n}\n\n// ─── Per-node model spec resolution ─────────────────────────────────────────────\n//\n// Resolves a node's model HINTS (id pin OR modality/effort/capability) to a\n// concrete model id. Mirrors agent-runtime NodeModelSpec structurally (plain\n// object — no cross-package dependency). Used by the workflow/agent executor.\n\nexport interface ModelSpecHints {\n /** Explicit pin — wins over every hint. Preset alias or `provider/model` id. */\n id?: string;\n reasoningEffort?: \"low\" | \"medium\" | \"high\" | \"xhigh\";\n modality?: ModelModality;\n capabilities?: { vision?: boolean; toolCall?: boolean; reasoning?: boolean };\n}\n\nconst EFFORT_TIER: Record<NonNullable<ModelSpecHints[\"reasoningEffort\"]>, ModelTier> = {\n low: \"fast\",\n medium: \"flagship\",\n high: \"flagship\",\n xhigh: \"reasoning\",\n};\n\nfunction pickByCapabilities(\n models: CatalogModel[],\n caps?: ModelSpecHints[\"capabilities\"],\n): CatalogModel | null {\n const filtered = caps\n ? models.filter(\n (m) =>\n (!caps.vision || m.capabilities.vision) &&\n (!caps.toolCall || m.capabilities.toolCall) &&\n (!caps.reasoning || m.capabilities.reasoning),\n )\n : models;\n return filtered[0] ?? null;\n}\n\n/**\n * Resolve a per-node model spec to a concrete model id. Precedence: explicit\n * `id` → non-text `modality` (most-available matching model) → `reasoningEffort`\n * tier (text frontier) → text `capabilities` match → `fallback` (the run\n * default). Never throws — degrades to `fallback`.\n */\nexport async function resolveModelSpec(spec: ModelSpecHints, fallback: string): Promise<string> {\n try {\n if (spec.id) return spec.id;\n\n if (spec.modality && spec.modality !== \"text\") {\n const match = pickByCapabilities(\n await listModelsByModality(spec.modality),\n spec.capabilities,\n );\n if (match) return match.offerings[0]?.id ?? match.key;\n return fallback;\n }\n\n if (spec.reasoningEffort) return resolveFrontierModel(EFFORT_TIER[spec.reasoningEffort]);\n\n if (spec.capabilities) {\n const match = pickByCapabilities(await listModelsByModality(\"text\"), spec.capabilities);\n if (match) return match.offerings[0]?.id ?? match.key;\n }\n\n return fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Default OpenAI-compatible base URL per provider bucket (env-overridable).\n * Single home for these — BYOK and any other caller import from here.\n */\nexport function providerBaseUrl(provider: AIProviderId): string | null {\n switch (provider) {\n case \"OPENAI\":\n return process.env.OPENAI_BASE_URL ?? \"https://api.openai.com/v1\";\n case \"ANTHROPIC\":\n return process.env.ANTHROPIC_BASE_URL ?? \"https://api.anthropic.com/v1\";\n case \"GOOGLE\":\n return (\n process.env.GOOGLE_BASE_URL ?? \"https://generativelanguage.googleapis.com/v1beta/openai\"\n );\n case \"SILICONFLOW\":\n return process.env.SILICONFLOW_BASE_URL ?? \"https://api.siliconflow.cn/v1\";\n default:\n // CUSTOM has no canonical endpoint — the caller must supply a baseUrl.\n return null;\n }\n}\n"]}
package/dist/index.js CHANGED
@@ -1,5 +1,17 @@
1
1
  import { fetchModels } from '@tokenlens/fetch';
2
2
 
3
+ // src/catalog.ts
4
+
5
+ // src/frontier-fallback.generated.ts
6
+ var FRONTIER_FALLBACK = {
7
+ reasoning: "anthropic/claude-opus-5",
8
+ flagship: "anthropic/claude-sonnet-5",
9
+ fast: "anthropic/claude-haiku-4.5",
10
+ "openai-flagship": "openai/gpt-5.6-sol",
11
+ "google-flagship": "google/gemini-3.1-pro-preview",
12
+ "google-fast": "google/gemini-3.7-flash"
13
+ };
14
+
3
15
  // src/catalog.ts
4
16
  var PROVIDER_MAP = {
5
17
  openai: "OPENAI",
@@ -173,41 +185,31 @@ function providerFromIdHeuristic(modelId) {
173
185
  var TIER_RULES = {
174
186
  reasoning: {
175
187
  include: /^anthropic\/claude-opus-/,
176
- exclude: /-(fast|mini|nano|image|codex)/,
177
- fallback: "anthropic/claude-opus-4.8"
188
+ exclude: /-(fast|mini|nano|image|codex)|:batch/
178
189
  },
179
190
  flagship: {
180
191
  include: /^anthropic\/claude-sonnet-/,
181
- exclude: /-(fast|mini|nano|image|codex)/,
182
- fallback: "anthropic/claude-sonnet-4.6"
192
+ exclude: /-(fast|mini|nano|image|codex)|:batch/
183
193
  },
184
194
  fast: {
185
195
  include: /^anthropic\/claude-haiku-/,
186
- exclude: /-(image|codex)/,
187
- fallback: "anthropic/claude-haiku-4.5"
196
+ exclude: /-(image|codex)|:batch/
188
197
  },
189
198
  "openai-flagship": {
190
199
  include: /^openai\/gpt-5/,
191
200
  // Prefer full flagship over mini/nano/chat/image/codex variants
192
- exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\.\d+-(mini|nano|pro)/,
193
- // models.dev current OpenAI flagship (audited 2026-07); live resolver may pick newer
194
- fallback: "openai/gpt-5.5"
201
+ exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\.\d+-(mini|nano|pro)|:batch/
195
202
  },
196
203
  "google-flagship": {
197
204
  include: /^google\/gemini-.*pro/,
198
- exclude: /-(image|tts|customtools)/,
199
- fallback: "google/gemini-3.1-pro-preview"
205
+ exclude: /-(image|tts|customtools)|:batch/
200
206
  },
201
207
  "google-fast": {
202
208
  include: /^google\/gemini-.*flash/,
203
- exclude: /-(image|tts|lite)/,
204
- // models.dev: gemini-3.5-flash / 3.6-flash class
205
- fallback: "google/gemini-3.5-flash"
209
+ exclude: /-(image|tts|lite)|:batch/
206
210
  }
207
211
  };
208
- var FRONTIER_FALLBACK = Object.fromEntries(
209
- Object.entries(TIER_RULES).map(([tier, rule]) => [tier, rule.fallback])
210
- );
212
+ var FRONTIER_FALLBACK2 = FRONTIER_FALLBACK;
211
213
  var OPENROUTER_MODELS_URL = process.env.OPENROUTER_MODELS_URL ?? "https://openrouter.ai/api/v1/models";
212
214
  var routableCache = null;
213
215
  var routableInflight = null;
@@ -245,18 +247,19 @@ var versionOf = (id) => {
245
247
  };
246
248
  async function resolveFrontierModel(tier) {
247
249
  const rule = TIER_RULES[tier];
250
+ const fallback = FRONTIER_FALLBACK2[tier];
248
251
  try {
249
252
  const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);
250
- if (routable.size === 0) return rule.fallback;
253
+ if (routable.size === 0) return fallback;
251
254
  const metaNorm = new Set([...metaIndex.keys()].map(collapseId));
252
255
  const candidates = [...routable].filter(
253
256
  (id) => rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id)))
254
257
  );
255
- if (candidates.length === 0) return rule.fallback;
258
+ if (candidates.length === 0) return fallback;
256
259
  candidates.sort((a, b) => versionOf(b) - versionOf(a));
257
- return candidates[0] ?? rule.fallback;
260
+ return candidates[0] ?? fallback;
258
261
  } catch {
259
- return rule.fallback;
262
+ return fallback;
260
263
  }
261
264
  }
262
265
  var EFFORT_TIER = {
@@ -778,6 +781,6 @@ function getProvider(id) {
778
781
  }
779
782
  var isCNCompatible = (id) => getProvider(id)?.status === "cn-compatible";
780
783
 
781
- export { FRONTIER_FALLBACK, PROVIDERS, PROVIDERS_BY_CATEGORY, PROVIDER_MAP, getModelInfo, getProvider, isCNCompatible, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
784
+ export { FRONTIER_FALLBACK2 as FRONTIER_FALLBACK, PROVIDERS, PROVIDERS_BY_CATEGORY, PROVIDER_MAP, getModelInfo, getProvider, isCNCompatible, listModalities, listModels, listModelsByModality, mapProvider, providerBaseUrl, providerForModel, providerFromIdHeuristic, resolveFrontierModel, resolveModelSpec };
782
785
  //# sourceMappingURL=index.js.map
783
786
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/catalog.ts","../src/meta.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Model catalog — the single source of model/provider FACTS.\n *\n * Data comes from the upstream community catalog https://models.dev (via\n * `@tokenlens/fetch`), so model ids, providers, pricing, context windows and\n * capabilities are NOT hand-maintained here. Replaces the scattered hardcoded\n * lists (BYOK provider regex, the /models endpoint, seed fallbacks).\n *\n * Delivery: runtime fetch + in-memory TTL cache. The first call fetches; later\n * calls serve from cache until `MODEL_CATALOG_TTL_MS` (default 6h) elapses. On\n * fetch failure the last good catalog is reused, so a models.dev outage degrades\n * to \"slightly stale\" rather than \"down\". Accessors are async by design.\n */\n\nimport type { ProviderModel, ProvidersCatalog } from \"@tokenlens/core\";\nimport { fetchModels } from \"@tokenlens/fetch\";\n\n/** Our coarse provider buckets — one BYOK key + base URL per bucket. */\nexport type AIProviderId = \"OPENAI\" | \"ANTHROPIC\" | \"GOOGLE\" | \"SILICONFLOW\" | \"CUSTOM\";\n\n/**\n * Map models.dev provider IDs → our AIProvider buckets. Canonical home (the DB\n * seed imports this, instead of keeping a parallel copy). Unknown → CUSTOM.\n */\nexport const PROVIDER_MAP: Record<string, AIProviderId> = {\n openai: \"OPENAI\",\n anthropic: \"ANTHROPIC\",\n google: \"GOOGLE\",\n \"google-vertex\": \"GOOGLE\",\n \"google-vertex-anthropic\": \"ANTHROPIC\",\n \"amazon-bedrock\": \"ANTHROPIC\", // most bedrock usage is claude\n siliconflow: \"SILICONFLOW\",\n \"siliconflow-cn\": \"SILICONFLOW\", // SiliconFlow ecosystem (GLM / Qwen / DeepSeek / MiniMax / …)\n deepseek: \"SILICONFLOW\", // deepseek is siliconflow-compatible\n alibaba: \"SILICONFLOW\",\n \"alibaba-token-plan\": \"SILICONFLOW\",\n qwen: \"SILICONFLOW\",\n moonshot: \"SILICONFLOW\",\n zhipu: \"SILICONFLOW\",\n minimax: \"SILICONFLOW\",\n};\n\nexport function mapProvider(modelsDevProviderId: string): AIProviderId {\n return PROVIDER_MAP[modelsDevProviderId] ?? \"CUSTOM\";\n}\n\n/**\n * Output modality — the primary dimension for canvas orchestration (\"give me a\n * video model\", \"an image model\", …). Derived from models.dev `modalities.output`.\n */\nexport type ModelModality = \"text\" | \"image\" | \"video\" | \"audio\" | \"embedding\";\n\n/** Classify a model by what it PRODUCES (not what it consumes). */\nfunction classifyModality(model: ProviderModel): ModelModality {\n const out = model.modalities?.output ?? [];\n if (out.includes(\"video\")) return \"video\";\n if (out.includes(\"image\")) return \"image\";\n if (out.includes(\"audio\")) return \"audio\";\n if (/embedding/i.test(model.id) || out.includes(\"embedding\")) return \"embedding\";\n return \"text\";\n}\n\n/** Normalized, provider-agnostic model facts surfaced to the rest of the app. */\nexport interface ModelInfo {\n id: string;\n name: string;\n /** Our coarse bucket (for BYOK key selection / base URL). */\n provider: AIProviderId;\n /** The raw models.dev provider id (e.g. \"google-vertex\"). */\n rawProvider: string;\n /** What the model produces — the canvas-orchestration grouping key. */\n modality: ModelModality;\n contextWindow?: number;\n maxOutput?: number;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n}\n\nfunction toModelInfo(rawProvider: string, model: ProviderModel): ModelInfo {\n const visionIn = model.modalities?.input?.includes(\"image\") ?? false;\n return {\n id: model.id,\n name: model.name ?? model.id,\n provider: mapProvider(rawProvider),\n rawProvider,\n modality: classifyModality(model),\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n ...(model.limit?.output !== undefined ? { maxOutput: model.limit.output } : {}),\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: visionIn,\n },\n };\n}\n\n// ─── Deduped, modality-grouped view (for canvas orchestration) ─────────────────\n//\n// models.dev lists the SAME logical model under dozens of aggregator providers\n// (e.g. minimax-m2.5 appears under 47). The canvas needs ONE entry per logical\n// model with the list of providers that can serve it (so it can pick a routable\n// id + compare cost). `dedupKey` collapses the bare name across aggregators.\n\n/** A single provider's offering of a logical model. */\nexport interface ModelOffering {\n /** Routable model id as listed by models.dev (aggregator-prefixed). */\n id: string;\n rawProvider: string;\n bucket: AIProviderId;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n contextWindow?: number;\n}\n\n/** One deduped logical model, with all the providers that serve it. */\nexport interface CatalogModel {\n key: string;\n name: string;\n modality: ModelModality;\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n /** Distinct provider buckets that serve it (for BYOK key selection). */\n buckets: AIProviderId[];\n offerings: ModelOffering[];\n}\n\n/** Collapse a model id to a logical key (drop aggregator path + separators). */\nfunction dedupKey(modelId: string): string {\n return modelId\n .toLowerCase()\n .replace(/^.*\\//, \"\") // keep only the final path segment\n .replace(/[._:\\-\\s]/g, \"\");\n}\n\n// ─── Runtime cache ────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n /** Per-id view (exact lookups: providerForModel, getModelInfo, resolveFrontier). */\n index: Map<string, ModelInfo>;\n /** Deduped logical view (canvas orchestration: listModelsByModality). */\n logical: CatalogModel[];\n fetchedAt: number;\n}\n\nconst TTL_MS = (() => {\n const n = Number.parseInt(process.env.MODEL_CATALOG_TTL_MS ?? \"21600000\", 10); // 6h\n return Number.isFinite(n) && n > 0 ? n : 21_600_000;\n})();\n\nlet cache: CacheEntry | null = null;\nlet inflight: Promise<CacheEntry> | null = null;\n\nfunction buildIndex(catalog: ProvidersCatalog): Map<string, ModelInfo> {\n const index = new Map<string, ModelInfo>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n // First writer wins; model ids are unique within models.dev.\n if (!index.has(model.id)) index.set(model.id, toModelInfo(providerId, model));\n }\n }\n return index;\n}\n\nfunction buildLogical(catalog: ProvidersCatalog): CatalogModel[] {\n const byKey = new Map<string, CatalogModel>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n const key = dedupKey(model.id);\n if (!key) continue;\n const bucket = mapProvider(providerId);\n const offering: ModelOffering = {\n id: model.id,\n rawProvider: providerId,\n bucket,\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n };\n const existing = byKey.get(key);\n if (existing) {\n existing.offerings.push(offering);\n if (!existing.buckets.includes(bucket)) existing.buckets.push(bucket);\n // Capabilities are the union across offerings.\n existing.capabilities.reasoning ||= model.reasoning ?? false;\n existing.capabilities.toolCall ||= model.tool_call ?? false;\n existing.capabilities.vision ||= model.modalities?.input?.includes(\"image\") ?? false;\n } else {\n byKey.set(key, {\n key,\n name: model.name ?? model.id,\n modality: classifyModality(model),\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: model.modalities?.input?.includes(\"image\") ?? false,\n },\n buckets: [bucket],\n offerings: [offering],\n });\n }\n }\n }\n return [...byKey.values()];\n}\n\nasync function loadCache(): Promise<CacheEntry> {\n const now = Date.now();\n if (cache && now - cache.fetchedAt < TTL_MS) return cache;\n if (inflight) return inflight;\n\n inflight = (async () => {\n try {\n const catalog = await fetchModels();\n cache = { index: buildIndex(catalog), logical: buildLogical(catalog), fetchedAt: Date.now() };\n return cache;\n } catch {\n // models.dev unreachable — reuse the last good catalog if we have one,\n // otherwise empty (callers degrade safely, e.g. BYOK → platform).\n if (cache) return cache;\n cache = { index: new Map(), logical: [], fetchedAt: Date.now() };\n return cache;\n } finally {\n inflight = null;\n }\n })();\n\n return inflight;\n}\n\nasync function loadIndex(): Promise<Map<string, ModelInfo>> {\n return (await loadCache()).index;\n}\n\n// ─── Public accessors ─────────────────────────────────────────────────────────\n\n/** Full model list (newest fetch, cached). */\nexport async function listModels(): Promise<ModelInfo[]> {\n return [...(await loadIndex()).values()];\n}\n\n/**\n * Deduped logical models for a given output modality — the canvas-orchestration\n * entry point. One entry per logical model, with the providers that serve it.\n * Sorted by offering count (most widely available first).\n */\nexport async function listModelsByModality(modality: ModelModality): Promise<CatalogModel[]> {\n const { logical } = await loadCache();\n return logical\n .filter((m) => m.modality === modality)\n .sort((a, b) => b.offerings.length - a.offerings.length);\n}\n\n/** Count of deduped logical models per modality (for canvas tab badges, etc.). */\nexport async function listModalities(): Promise<Record<ModelModality, number>> {\n const { logical } = await loadCache();\n const counts: Record<ModelModality, number> = {\n text: 0,\n image: 0,\n video: 0,\n audio: 0,\n embedding: 0,\n };\n for (const m of logical) counts[m.modality]++;\n return counts;\n}\n\n/** Look up a single model by id. `null` if unknown to the catalog. */\nexport async function getModelInfo(modelId: string): Promise<ModelInfo | null> {\n return (await loadIndex()).get(modelId) ?? null;\n}\n\n/**\n * Resolve the coarse provider bucket for a model id. Catalog-first; falls back\n * to id-prefix heuristics for brand-new models models.dev hasn't indexed yet.\n * `null` means \"unknown\" → callers should use the platform default.\n */\nexport async function providerForModel(modelId: string): Promise<AIProviderId | null> {\n const info = await getModelInfo(modelId);\n if (info) return info.provider;\n return providerFromIdHeuristic(modelId);\n}\n\n/** Last-resort id-prefix heuristic (only used when the catalog misses). */\nexport function providerFromIdHeuristic(modelId: string): AIProviderId | null {\n const m = modelId.toLowerCase().replace(/^[^/]+\\//, \"\"); // strip \"anthropic/\" style prefix\n if (/^(gpt-|o1|o3|o4|chatgpt|text-|davinci|babbage)/.test(m)) return \"OPENAI\";\n if (m.startsWith(\"claude\")) return \"ANTHROPIC\";\n if (m.startsWith(\"gemini\")) return \"GOOGLE\";\n if (/deepseek|qwen|glm|yi-|internlm|siliconflow|moonshot|kimi/.test(m)) return \"SILICONFLOW\";\n return null;\n}\n\n// ─── Frontier resolver (hybrid: live intersection + hardcoded fallback) ─────────\n//\n// Resolves a SEMANTIC tier (e.g. \"the latest reasoning model\") to a concrete\n// model id, picking the newest from the INTERSECTION of:\n// - gateway-routable ids (OpenRouter /api/v1/models — the gateway namespace), and\n// - models.dev catalog metadata (so the pick has pricing/context/capabilities).\n// On any failure (offline / empty) it returns the hardcoded fallback, so callers\n// always get a usable id. This is the \"don't hand-maintain model strings\" engine.\n\nexport type ModelTier =\n | \"reasoning\"\n | \"flagship\"\n | \"fast\"\n | \"openai-flagship\"\n | \"google-flagship\"\n | \"google-fast\";\n\ninterface TierRule {\n /** Match on the OpenRouter (gateway) id. */\n include: RegExp;\n /** Drop non-frontier variants (fast/mini/codex/image/…). */\n exclude: RegExp;\n /** Current frontier (audited 2026-06-05) — used when the live lists are unavailable. */\n fallback: string;\n}\n\nconst TIER_RULES: Record<ModelTier, TierRule> = {\n reasoning: {\n include: /^anthropic\\/claude-opus-/,\n exclude: /-(fast|mini|nano|image|codex)/,\n fallback: \"anthropic/claude-opus-4.8\",\n },\n flagship: {\n include: /^anthropic\\/claude-sonnet-/,\n exclude: /-(fast|mini|nano|image|codex)/,\n fallback: \"anthropic/claude-sonnet-4.6\",\n },\n fast: {\n include: /^anthropic\\/claude-haiku-/,\n exclude: /-(image|codex)/,\n fallback: \"anthropic/claude-haiku-4.5\",\n },\n \"openai-flagship\": {\n include: /^openai\\/gpt-5/,\n // Prefer full flagship over mini/nano/chat/image/codex variants\n exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\\.\\d+-(mini|nano|pro)/,\n // models.dev current OpenAI flagship (audited 2026-07); live resolver may pick newer\n fallback: \"openai/gpt-5.5\",\n },\n \"google-flagship\": {\n include: /^google\\/gemini-.*pro/,\n exclude: /-(image|tts|customtools)/,\n fallback: \"google/gemini-3.1-pro-preview\",\n },\n \"google-fast\": {\n include: /^google\\/gemini-.*flash/,\n exclude: /-(image|tts|lite)/,\n // models.dev: gemini-3.5-flash / 3.6-flash class\n fallback: \"google/gemini-3.5-flash\",\n },\n};\n\n/** The hardcoded frontier fallbacks, exported so consumers can mirror them. */\nexport const FRONTIER_FALLBACK: Record<ModelTier, string> = Object.fromEntries(\n Object.entries(TIER_RULES).map(([tier, rule]) => [tier, rule.fallback]),\n) as Record<ModelTier, string>;\n\nconst OPENROUTER_MODELS_URL =\n process.env.OPENROUTER_MODELS_URL ?? \"https://openrouter.ai/api/v1/models\";\n\nlet routableCache: { ids: Set<string>; fetchedAt: number } | null = null;\nlet routableInflight: Promise<Set<string>> | null = null;\n\n/** Gateway-routable model ids (OpenRouter), cached with the same TTL as the catalog. */\nasync function fetchRoutableIds(): Promise<Set<string>> {\n const now = Date.now();\n if (routableCache && now - routableCache.fetchedAt < TTL_MS) return routableCache.ids;\n if (routableInflight) return routableInflight;\n\n routableInflight = (async () => {\n try {\n const res = await fetch(OPENROUTER_MODELS_URL);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const json = (await res.json()) as { data?: Array<{ id?: string }> };\n const ids = new Set(\n (json.data ?? []).map((m) => m.id).filter((x): x is string => typeof x === \"string\"),\n );\n routableCache = { ids, fetchedAt: Date.now() };\n return ids;\n } catch {\n return routableCache?.ids ?? new Set<string>();\n } finally {\n routableInflight = null;\n }\n })();\n\n return routableInflight;\n}\n\n/** Collapse separators so `claude-opus-4.8` (OpenRouter) matches `claude-opus-4-8` (models.dev). */\nconst collapseId = (s: string): string => s.toLowerCase().replace(/[._-]/g, \"\");\nconst bareId = (id: string): string => {\n const slash = id.indexOf(\"/\");\n return slash >= 0 ? id.slice(slash + 1) : id;\n};\nconst versionOf = (id: string): number => {\n const m = bareId(id).match(/(\\d+(?:\\.\\d+)?)/);\n const raw = m?.[1];\n return raw ? Number.parseFloat(raw) : -1;\n};\n\n/**\n * Resolve a semantic tier to the newest concrete model id that is BOTH\n * gateway-routable and present in the models.dev catalog. Falls back to the\n * audited hardcoded frontier when the live lists are unavailable.\n */\nexport async function resolveFrontierModel(tier: ModelTier): Promise<string> {\n const rule = TIER_RULES[tier];\n try {\n const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);\n if (routable.size === 0) return rule.fallback;\n\n const metaNorm = new Set([...metaIndex.keys()].map(collapseId));\n const candidates = [...routable].filter(\n (id) =>\n rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id))),\n );\n if (candidates.length === 0) return rule.fallback;\n\n candidates.sort((a, b) => versionOf(b) - versionOf(a));\n return candidates[0] ?? rule.fallback;\n } catch {\n return rule.fallback;\n }\n}\n\n// ─── Per-node model spec resolution ─────────────────────────────────────────────\n//\n// Resolves a node's model HINTS (id pin OR modality/effort/capability) to a\n// concrete model id. Mirrors agent-runtime NodeModelSpec structurally (plain\n// object — no cross-package dependency). Used by the workflow/agent executor.\n\nexport interface ModelSpecHints {\n /** Explicit pin — wins over every hint. Preset alias or `provider/model` id. */\n id?: string;\n reasoningEffort?: \"low\" | \"medium\" | \"high\" | \"xhigh\";\n modality?: ModelModality;\n capabilities?: { vision?: boolean; toolCall?: boolean; reasoning?: boolean };\n}\n\nconst EFFORT_TIER: Record<NonNullable<ModelSpecHints[\"reasoningEffort\"]>, ModelTier> = {\n low: \"fast\",\n medium: \"flagship\",\n high: \"flagship\",\n xhigh: \"reasoning\",\n};\n\nfunction pickByCapabilities(\n models: CatalogModel[],\n caps?: ModelSpecHints[\"capabilities\"],\n): CatalogModel | null {\n const filtered = caps\n ? models.filter(\n (m) =>\n (!caps.vision || m.capabilities.vision) &&\n (!caps.toolCall || m.capabilities.toolCall) &&\n (!caps.reasoning || m.capabilities.reasoning),\n )\n : models;\n return filtered[0] ?? null;\n}\n\n/**\n * Resolve a per-node model spec to a concrete model id. Precedence: explicit\n * `id` → non-text `modality` (most-available matching model) → `reasoningEffort`\n * tier (text frontier) → text `capabilities` match → `fallback` (the run\n * default). Never throws — degrades to `fallback`.\n */\nexport async function resolveModelSpec(spec: ModelSpecHints, fallback: string): Promise<string> {\n try {\n if (spec.id) return spec.id;\n\n if (spec.modality && spec.modality !== \"text\") {\n const match = pickByCapabilities(\n await listModelsByModality(spec.modality),\n spec.capabilities,\n );\n if (match) return match.offerings[0]?.id ?? match.key;\n return fallback;\n }\n\n if (spec.reasoningEffort) return resolveFrontierModel(EFFORT_TIER[spec.reasoningEffort]);\n\n if (spec.capabilities) {\n const match = pickByCapabilities(await listModelsByModality(\"text\"), spec.capabilities);\n if (match) return match.offerings[0]?.id ?? match.key;\n }\n\n return fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Default OpenAI-compatible base URL per provider bucket (env-overridable).\n * Single home for these — BYOK and any other caller import from here.\n */\nexport function providerBaseUrl(provider: AIProviderId): string | null {\n switch (provider) {\n case \"OPENAI\":\n return process.env.OPENAI_BASE_URL ?? \"https://api.openai.com/v1\";\n case \"ANTHROPIC\":\n return process.env.ANTHROPIC_BASE_URL ?? \"https://api.anthropic.com/v1\";\n case \"GOOGLE\":\n return (\n process.env.GOOGLE_BASE_URL ?? \"https://generativelanguage.googleapis.com/v1beta/openai\"\n );\n case \"SILICONFLOW\":\n return process.env.SILICONFLOW_BASE_URL ?? \"https://api.siliconflow.cn/v1\";\n default:\n // CUSTOM has no canonical endpoint — the caller must supply a baseUrl.\n return null;\n }\n}\n","/**\n * Provider registry metadata — single source of truth for the\n * create-sailor CLI and documentation generators.\n *\n * Coverage: 20 providers across direct labs, CN platforms, gateways,\n * inference accelerators, multimodal, local, and developer ecosystem.\n */\n\nexport type ProviderStatus = \"opencode\" | \"ai-sdk\" | \"cn-compatible\" | \"pending\";\n\nexport type ProviderCategory =\n | \"直接实验室\"\n | \"国内平台\"\n | \"云平台\"\n | \"推理加速\"\n | \"统一网关\"\n | \"多模态\"\n | \"本地部署\"\n | \"开发者生态\";\n\nexport interface ProviderMeta {\n id: string;\n name: string;\n category: ProviderCategory;\n status: ProviderStatus;\n docs: string;\n baseURL?: string;\n envVarPrefix: string;\n requiredEnvVars: string[];\n}\n\nexport const PROVIDERS: ProviderMeta[] = [\n // ─── 直接实验室 ───────────────────────────────────────────────\n {\n id: \"anthropic\",\n name: \"Anthropic\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://docs.anthropic.com\",\n envVarPrefix: \"ANTHROPIC\",\n requiredEnvVars: [\"ANTHROPIC_API_KEY\"],\n },\n {\n id: \"openai\",\n name: \"OpenAI\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://platform.openai.com/docs\",\n envVarPrefix: \"OPENAI\",\n requiredEnvVars: [\"OPENAI_API_KEY\"],\n },\n {\n id: \"deepseek\",\n name: \"DeepSeek\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://platform.deepseek.com/api-docs\",\n envVarPrefix: \"DEEPSEEK\",\n requiredEnvVars: [\"DEEPSEEK_API_KEY\"],\n },\n {\n id: \"xai\",\n name: \"xAI (Grok)\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://docs.x.ai/api\",\n envVarPrefix: \"XAI\",\n requiredEnvVars: [\"XAI_API_KEY\"],\n },\n {\n id: \"moonshot\",\n name: \"Moonshot (Kimi)\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://platform.moonshot.cn/docs\",\n envVarPrefix: \"MOONSHOT\",\n requiredEnvVars: [\"MOONSHOT_API_KEY\", \"MOONSHOT_BASE_URL\"],\n },\n {\n id: \"google\",\n name: \"Google Gemini\",\n category: \"直接实验室\",\n status: \"ai-sdk\",\n docs: \"https://ai.google.dev/gemini-api/docs\",\n envVarPrefix: \"GOOGLE_GENERATIVE_AI\",\n requiredEnvVars: [\"GOOGLE_GENERATIVE_AI_API_KEY\"],\n },\n {\n id: \"mistral\",\n name: \"Mistral AI\",\n category: \"直接实验室\",\n status: \"ai-sdk\",\n docs: \"https://docs.mistral.ai\",\n envVarPrefix: \"MISTRAL\",\n requiredEnvVars: [\"MISTRAL_API_KEY\"],\n },\n {\n id: \"cohere\",\n name: \"Cohere\",\n category: \"直接实验室\",\n status: \"ai-sdk\",\n docs: \"https://docs.cohere.com\",\n envVarPrefix: \"COHERE\",\n requiredEnvVars: [\"COHERE_API_KEY\"],\n },\n {\n id: \"perplexity\",\n name: \"Perplexity\",\n category: \"直接实验室\",\n status: \"cn-compatible\",\n docs: \"https://docs.perplexity.ai\",\n baseURL: \"https://api.perplexity.ai\",\n envVarPrefix: \"PERPLEXITY\",\n requiredEnvVars: [\"PERPLEXITY_API_KEY\"],\n },\n {\n id: \"ai21\",\n name: \"AI21 Labs\",\n category: \"直接实验室\",\n status: \"cn-compatible\",\n docs: \"https://docs.ai21.com\",\n baseURL: \"https://api.ai21.com/studio/v1\",\n envVarPrefix: \"AI21\",\n requiredEnvVars: [\"AI21_API_KEY\"],\n },\n {\n id: \"upstage\",\n name: \"Upstage (Solar)\",\n category: \"直接实验室\",\n status: \"cn-compatible\",\n docs: \"https://developers.upstage.ai/docs\",\n baseURL: \"https://api.upstage.ai/v1/solar\",\n envVarPrefix: \"UPSTAGE\",\n requiredEnvVars: [\"UPSTAGE_API_KEY\"],\n },\n\n // ─── 国内平台 ───────────────────────────────────────────────\n {\n id: \"siliconflow\",\n name: \"硅基流动 SiliconFlow\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://docs.siliconflow.cn\",\n baseURL: \"https://api.siliconflow.cn/v1\",\n envVarPrefix: \"SILICONFLOW\",\n requiredEnvVars: [\"SILICONFLOW_API_KEY\"],\n },\n {\n id: \"volcengine-ark\",\n name: \"火山引擎 ARK (豆包)\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://www.volcengine.com/docs/82379\",\n baseURL: \"https://ark.cn-beijing.volces.com/api/v3\",\n envVarPrefix: \"ARK\",\n requiredEnvVars: [\"ARK_API_KEY\"],\n },\n {\n id: \"bailian\",\n name: \"百炼 Bailian (通义)\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://help.aliyun.com/zh/model-studio\",\n baseURL: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n envVarPrefix: \"DASHSCOPE\",\n requiredEnvVars: [\"DASHSCOPE_API_KEY\"],\n },\n {\n id: \"zhipu\",\n name: \"智谱 AI Zhipu\",\n category: \"国内平台\",\n status: \"ai-sdk\",\n docs: \"https://docs.bigmodel.cn\",\n baseURL: \"https://open.bigmodel.cn/api/paas/v4\",\n envVarPrefix: \"ZHIPU\",\n requiredEnvVars: [\"ZHIPU_API_KEY\"],\n },\n {\n id: \"baichuan\",\n name: \"百川智能 Baichuan\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.baichuan-ai.com/docs\",\n baseURL: \"https://api.baichuan-ai.com/v1\",\n envVarPrefix: \"BAICHUAN\",\n requiredEnvVars: [\"BAICHUAN_API_KEY\"],\n },\n {\n id: \"minimax\",\n name: \"MiniMax (ABAB)\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.minimaxi.com/document/guides\",\n baseURL: \"https://api.minimax.chat/v1\",\n envVarPrefix: \"MINIMAX\",\n requiredEnvVars: [\"MINIMAX_API_KEY\", \"MINIMAX_GROUP_ID\"],\n },\n {\n id: \"stepfun\",\n name: \"阶跃星辰 StepFun\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.stepfun.com/docs\",\n baseURL: \"https://api.stepfun.com/v1\",\n envVarPrefix: \"STEPFUN\",\n requiredEnvVars: [\"STEPFUN_API_KEY\"],\n },\n {\n id: \"sensetime\",\n name: \"商汤 SenseNova\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.sensenova.cn/docs\",\n // Token Plan OpenAI-compatible surface (NOT enterprise api.sensenova.cn)\n // Ref: https://github.com/OpenSenseNova/SenseNova6.7/blob/main/API_CN.md\n baseURL: \"https://token.sensenova.cn/v1\",\n envVarPrefix: \"SENSENOVA\",\n requiredEnvVars: [\"SENSENOVA_API_KEY\"],\n },\n {\n id: \"tencent\",\n name: \"腾讯混元 Hunyuan\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://cloud.tencent.com/document/product/1729\",\n baseURL: \"https://api.hunyuan.cloud.tencent.com/v1\",\n envVarPrefix: \"HUNYUAN\",\n requiredEnvVars: [\"HUNYUAN_API_KEY\"],\n },\n {\n id: \"lingyi\",\n name: \"零一万物 Yi\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.lingyiwanwu.com/docs\",\n baseURL: \"https://api.lingyiwanwu.com/v1\",\n envVarPrefix: \"YI\",\n requiredEnvVars: [\"YI_API_KEY\"],\n },\n\n // ─── 统一网关 ───────────────────────────────────────────────\n {\n id: \"openrouter\",\n name: \"OpenRouter\",\n category: \"统一网关\",\n status: \"opencode\",\n docs: \"https://openrouter.ai/docs\",\n envVarPrefix: \"OPENROUTER\",\n requiredEnvVars: [\"OPENROUTER_API_KEY\"],\n },\n {\n id: \"vercel-gateway\",\n name: \"Vercel AI Gateway\",\n category: \"统一网关\",\n status: \"opencode\",\n docs: \"https://vercel.com/docs/ai-gateway\",\n envVarPrefix: \"AI_GATEWAY\",\n requiredEnvVars: [\"AI_GATEWAY_API_KEY\"],\n },\n {\n id: \"litellm\",\n name: \"LiteLLM Proxy\",\n category: \"统一网关\",\n status: \"cn-compatible\",\n docs: \"https://docs.litellm.ai\",\n baseURL: \"http://localhost:4000/v1\",\n envVarPrefix: \"LITELLM\",\n requiredEnvVars: [\"LITELLM_API_KEY\"],\n },\n {\n id: \"portkey\",\n name: \"Portkey\",\n category: \"统一网关\",\n status: \"cn-compatible\",\n docs: \"https://portkey.ai/docs\",\n baseURL: \"https://api.portkey.ai/v1\",\n envVarPrefix: \"PORTKEY\",\n requiredEnvVars: [\"PORTKEY_API_KEY\"],\n },\n {\n id: \"aws-bedrock\",\n name: \"AWS Bedrock\",\n category: \"云平台\",\n status: \"ai-sdk\",\n docs: \"https://aws.amazon.com/bedrock/\",\n envVarPrefix: \"AWS_BEDROCK\",\n requiredEnvVars: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\", \"AWS_REGION\"],\n },\n {\n id: \"azure-openai\",\n name: \"Azure OpenAI\",\n category: \"云平台\",\n status: \"ai-sdk\",\n docs: \"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/\",\n envVarPrefix: \"AZURE_OPENAI\",\n requiredEnvVars: [\"AZURE_OPENAI_API_KEY\", \"AZURE_OPENAI_ENDPOINT\"],\n },\n {\n id: \"gcp-vertex\",\n name: \"GCP Vertex AI\",\n category: \"云平台\",\n status: \"ai-sdk\",\n docs: \"https://cloud.google.com/vertex-ai\",\n envVarPrefix: \"GOOGLE_VERTEX\",\n requiredEnvVars: [\"GOOGLE_APPLICATION_CREDENTIALS\"],\n },\n\n // ─── 推理加速 ───────────────────────────────────────────────\n {\n id: \"groq\",\n name: \"Groq\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://console.groq.com/docs\",\n envVarPrefix: \"GROQ\",\n requiredEnvVars: [\"GROQ_API_KEY\"],\n },\n {\n id: \"fireworks\",\n name: \"Fireworks AI\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://docs.fireworks.ai\",\n envVarPrefix: \"FIREWORKS\",\n requiredEnvVars: [\"FIREWORKS_API_KEY\"],\n },\n {\n id: \"together\",\n name: \"Together AI\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://docs.together.ai\",\n envVarPrefix: \"TOGETHER\",\n requiredEnvVars: [\"TOGETHER_API_KEY\"],\n },\n {\n id: \"huggingface\",\n name: \"Hugging Face\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://huggingface.co/docs/inference-providers\",\n envVarPrefix: \"HF\",\n requiredEnvVars: [\"HF_API_KEY\"],\n },\n {\n id: \"replicate\",\n name: \"Replicate\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://replicate.com/docs\",\n envVarPrefix: \"REPLICATE\",\n requiredEnvVars: [\"REPLICATE_API_TOKEN\"],\n },\n {\n id: \"lepton\",\n name: \"Lepton AI\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://www.lepton.ai/docs\",\n baseURL: \"https://api.lepton.ai/v1\",\n envVarPrefix: \"LEPTON\",\n requiredEnvVars: [\"LEPTON_API_KEY\"],\n },\n {\n id: \"anyscale\",\n name: \"Anyscale Endpoints\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://docs.endpoints.anyscale.com/\",\n baseURL: \"https://api.endpoints.anyscale.com/v1\",\n envVarPrefix: \"ANYSCALE\",\n requiredEnvVars: [\"ANYSCALE_API_KEY\"],\n },\n {\n id: \"octoai\",\n name: \"OctoAI\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://octoai.cloud/docs\",\n baseURL: \"https://text.octoai.run/v1\",\n envVarPrefix: \"OCTOAI\",\n requiredEnvVars: [\"OCTOAI_TOKEN\"],\n },\n {\n id: \"deepinfra\",\n name: \"DeepInfra\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://deepinfra.com/docs\",\n baseURL: \"https://api.deepinfra.com/v1/openai\",\n envVarPrefix: \"DEEPINFRA\",\n requiredEnvVars: [\"DEEPINFRA_API_KEY\"],\n },\n {\n id: \"novita\",\n name: \"Novita AI\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://novita.ai/docs\",\n baseURL: \"https://api.novita.ai/v3/openai\",\n envVarPrefix: \"NOVITA\",\n requiredEnvVars: [\"NOVITA_API_KEY\"],\n },\n\n // ─── 多模态 ───────────────────────────────────────────────\n {\n id: \"black-forest-labs\",\n name: \"Black Forest Labs (FLUX)\",\n category: \"多模态\",\n status: \"ai-sdk\",\n docs: \"https://docs.bfl.ml\",\n envVarPrefix: \"BFL\",\n requiredEnvVars: [\"BFL_API_KEY\"],\n },\n {\n id: \"elevenlabs\",\n name: \"ElevenLabs (TTS)\",\n category: \"多模态\",\n status: \"ai-sdk\",\n docs: \"https://elevenlabs.io/docs\",\n envVarPrefix: \"ELEVENLABS\",\n requiredEnvVars: [\"ELEVENLABS_API_KEY\"],\n },\n {\n id: \"runway\",\n name: \"Runway (Video)\",\n category: \"多模态\",\n status: \"pending\",\n docs: \"https://runwayml.com\",\n envVarPrefix: \"RUNWAY\",\n requiredEnvVars: [\"RUNWAY_API_KEY\"],\n },\n {\n id: \"luma\",\n name: \"Luma (Video)\",\n category: \"多模态\",\n status: \"pending\",\n docs: \"https://lumalabs.ai\",\n envVarPrefix: \"LUMA\",\n requiredEnvVars: [\"LUMA_API_KEY\"],\n },\n {\n id: \"midjourney\",\n name: \"Midjourney\",\n category: \"多模态\",\n status: \"pending\",\n docs: \"https://docs.midjourney.com\",\n envVarPrefix: \"MIDJOURNEY\",\n requiredEnvVars: [],\n },\n\n // ─── 本地部署 ───────────────────────────────────────────────\n {\n id: \"ollama\",\n name: \"Ollama (本地)\",\n category: \"本地部署\",\n status: \"opencode\",\n docs: \"https://github.com/ollama/ollama\",\n baseURL: \"http://localhost:11434/v1\",\n envVarPrefix: \"OLLAMA\",\n requiredEnvVars: [\"OLLAMA_BASE_URL\"],\n },\n {\n id: \"lmstudio\",\n name: \"LM Studio\",\n category: \"本地部署\",\n status: \"cn-compatible\",\n docs: \"https://lmstudio.ai/docs\",\n baseURL: \"http://localhost:1234/v1\",\n envVarPrefix: \"LMSTUDIO\",\n requiredEnvVars: [],\n },\n {\n id: \"vllm\",\n name: \"vLLM\",\n category: \"本地部署\",\n status: \"cn-compatible\",\n docs: \"https://docs.vllm.ai\",\n baseURL: \"http://localhost:8000/v1\",\n envVarPrefix: \"VLLM\",\n requiredEnvVars: [],\n },\n {\n id: \"tgi\",\n name: \"HF TGI\",\n category: \"本地部署\",\n status: \"cn-compatible\",\n docs: \"https://huggingface.co/docs/text-generation-inference\",\n baseURL: \"http://localhost:8080/v1\",\n envVarPrefix: \"TGI\",\n requiredEnvVars: [],\n },\n];\n\nexport const PROVIDERS_BY_CATEGORY = PROVIDERS.reduce<Record<ProviderCategory, ProviderMeta[]>>(\n (acc, p) => {\n if (!acc[p.category]) acc[p.category] = [];\n acc[p.category].push(p);\n return acc;\n },\n {} as Record<ProviderCategory, ProviderMeta[]>,\n);\n\nexport function getProvider(id: string): ProviderMeta | undefined {\n return PROVIDERS.find((p) => p.id === id);\n}\n\nexport const isCNCompatible = (id: string): boolean => getProvider(id)?.status === \"cn-compatible\";\n"]}
1
+ {"version":3,"sources":["../src/frontier-fallback.generated.ts","../src/catalog.ts","../src/meta.ts"],"names":["FRONTIER_FALLBACK"],"mappings":"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","file":"index.js","sourcesContent":["// GENERATED — do not edit by hand.\n// Run `pnpm gen:frontier-models` to refresh from the live gateway catalogue.\n//\n// The newest model OpenRouter routes for each tier, resolved 2026-08-21.\n// These are the offline fallbacks: `resolveFrontierModel()` re-resolves against\n// the live list at runtime and only lands here when that list is unreachable.\n\nexport const FRONTIER_FALLBACK = {\n reasoning: \"anthropic/claude-opus-5\",\n flagship: \"anthropic/claude-sonnet-5\",\n fast: \"anthropic/claude-haiku-4.5\",\n \"openai-flagship\": \"openai/gpt-5.6-sol\",\n \"google-flagship\": \"google/gemini-3.1-pro-preview\",\n \"google-fast\": \"google/gemini-3.7-flash\",\n} as const;\n\nexport type FrontierTier = keyof typeof FRONTIER_FALLBACK;\n\n/**\n * Tiers whose id 302.AI spells differently from the gateway. Only the tiers\n * that actually differ appear; everything else is served under the bare id.\n * A missing entry means \"no alias needed\", never \"unknown\".\n */\nexport const AI302_ALIASES: Partial<Record<FrontierTier, string>> = {\n fast: \"claude-haiku-4-5-20251001\",\n};\n\n/**\n * Newest member of each open-weight family 302.AI serves — the models the\n * Anthropic/OpenAI/Google tiers above do not cover. Ids are 302-native and bare,\n * so these presets only resolve against the `ai302` provider.\n */\nexport const AI302_OPEN_MODELS = {\n \"302-deepseek\": \"deepseek-v4-pro\",\n \"302-deepseek-fast\": \"deepseek-v4-flash\",\n \"302-qwen\": \"qwen3.8-max\",\n \"302-glm\": \"glm-5.3\",\n \"302-kimi\": \"kimi-k3\",\n \"302-minimax\": \"MiniMax-M3\",\n} as const;\n","/**\n * Model catalog — the single source of model/provider FACTS.\n *\n * Data comes from the upstream community catalog https://models.dev (via\n * `@tokenlens/fetch`), so model ids, providers, pricing, context windows and\n * capabilities are NOT hand-maintained here. Replaces the scattered hardcoded\n * lists (BYOK provider regex, the /models endpoint, seed fallbacks).\n *\n * Delivery: runtime fetch + in-memory TTL cache. The first call fetches; later\n * calls serve from cache until `MODEL_CATALOG_TTL_MS` (default 6h) elapses. On\n * fetch failure the last good catalog is reused, so a models.dev outage degrades\n * to \"slightly stale\" rather than \"down\". Accessors are async by design.\n */\n\nimport type { ProviderModel, ProvidersCatalog } from \"@tokenlens/core\";\nimport { fetchModels } from \"@tokenlens/fetch\";\nimport { FRONTIER_FALLBACK as GENERATED_FALLBACK } from \"./frontier-fallback.generated\";\n\n/** Our coarse provider buckets — one BYOK key + base URL per bucket. */\nexport type AIProviderId = \"OPENAI\" | \"ANTHROPIC\" | \"GOOGLE\" | \"SILICONFLOW\" | \"CUSTOM\";\n\n/**\n * Map models.dev provider IDs → our AIProvider buckets. Canonical home (the DB\n * seed imports this, instead of keeping a parallel copy). Unknown → CUSTOM.\n */\nexport const PROVIDER_MAP: Record<string, AIProviderId> = {\n openai: \"OPENAI\",\n anthropic: \"ANTHROPIC\",\n google: \"GOOGLE\",\n \"google-vertex\": \"GOOGLE\",\n \"google-vertex-anthropic\": \"ANTHROPIC\",\n \"amazon-bedrock\": \"ANTHROPIC\", // most bedrock usage is claude\n siliconflow: \"SILICONFLOW\",\n \"siliconflow-cn\": \"SILICONFLOW\", // SiliconFlow ecosystem (GLM / Qwen / DeepSeek / MiniMax / …)\n deepseek: \"SILICONFLOW\", // deepseek is siliconflow-compatible\n alibaba: \"SILICONFLOW\",\n \"alibaba-token-plan\": \"SILICONFLOW\",\n qwen: \"SILICONFLOW\",\n moonshot: \"SILICONFLOW\",\n zhipu: \"SILICONFLOW\",\n minimax: \"SILICONFLOW\",\n};\n\nexport function mapProvider(modelsDevProviderId: string): AIProviderId {\n return PROVIDER_MAP[modelsDevProviderId] ?? \"CUSTOM\";\n}\n\n/**\n * Output modality — the primary dimension for canvas orchestration (\"give me a\n * video model\", \"an image model\", …). Derived from models.dev `modalities.output`.\n */\nexport type ModelModality = \"text\" | \"image\" | \"video\" | \"audio\" | \"embedding\";\n\n/** Classify a model by what it PRODUCES (not what it consumes). */\nfunction classifyModality(model: ProviderModel): ModelModality {\n const out = model.modalities?.output ?? [];\n if (out.includes(\"video\")) return \"video\";\n if (out.includes(\"image\")) return \"image\";\n if (out.includes(\"audio\")) return \"audio\";\n if (/embedding/i.test(model.id) || out.includes(\"embedding\")) return \"embedding\";\n return \"text\";\n}\n\n/** Normalized, provider-agnostic model facts surfaced to the rest of the app. */\nexport interface ModelInfo {\n id: string;\n name: string;\n /** Our coarse bucket (for BYOK key selection / base URL). */\n provider: AIProviderId;\n /** The raw models.dev provider id (e.g. \"google-vertex\"). */\n rawProvider: string;\n /** What the model produces — the canvas-orchestration grouping key. */\n modality: ModelModality;\n contextWindow?: number;\n maxOutput?: number;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n}\n\nfunction toModelInfo(rawProvider: string, model: ProviderModel): ModelInfo {\n const visionIn = model.modalities?.input?.includes(\"image\") ?? false;\n return {\n id: model.id,\n name: model.name ?? model.id,\n provider: mapProvider(rawProvider),\n rawProvider,\n modality: classifyModality(model),\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n ...(model.limit?.output !== undefined ? { maxOutput: model.limit.output } : {}),\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: visionIn,\n },\n };\n}\n\n// ─── Deduped, modality-grouped view (for canvas orchestration) ─────────────────\n//\n// models.dev lists the SAME logical model under dozens of aggregator providers\n// (e.g. minimax-m2.5 appears under 47). The canvas needs ONE entry per logical\n// model with the list of providers that can serve it (so it can pick a routable\n// id + compare cost). `dedupKey` collapses the bare name across aggregators.\n\n/** A single provider's offering of a logical model. */\nexport interface ModelOffering {\n /** Routable model id as listed by models.dev (aggregator-prefixed). */\n id: string;\n rawProvider: string;\n bucket: AIProviderId;\n pricing?: { inputPerMTok?: number; outputPerMTok?: number };\n contextWindow?: number;\n}\n\n/** One deduped logical model, with all the providers that serve it. */\nexport interface CatalogModel {\n key: string;\n name: string;\n modality: ModelModality;\n capabilities: { reasoning: boolean; toolCall: boolean; vision: boolean };\n /** Distinct provider buckets that serve it (for BYOK key selection). */\n buckets: AIProviderId[];\n offerings: ModelOffering[];\n}\n\n/** Collapse a model id to a logical key (drop aggregator path + separators). */\nfunction dedupKey(modelId: string): string {\n return modelId\n .toLowerCase()\n .replace(/^.*\\//, \"\") // keep only the final path segment\n .replace(/[._:\\-\\s]/g, \"\");\n}\n\n// ─── Runtime cache ────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n /** Per-id view (exact lookups: providerForModel, getModelInfo, resolveFrontier). */\n index: Map<string, ModelInfo>;\n /** Deduped logical view (canvas orchestration: listModelsByModality). */\n logical: CatalogModel[];\n fetchedAt: number;\n}\n\nconst TTL_MS = (() => {\n const n = Number.parseInt(process.env.MODEL_CATALOG_TTL_MS ?? \"21600000\", 10); // 6h\n return Number.isFinite(n) && n > 0 ? n : 21_600_000;\n})();\n\nlet cache: CacheEntry | null = null;\nlet inflight: Promise<CacheEntry> | null = null;\n\nfunction buildIndex(catalog: ProvidersCatalog): Map<string, ModelInfo> {\n const index = new Map<string, ModelInfo>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n // First writer wins; model ids are unique within models.dev.\n if (!index.has(model.id)) index.set(model.id, toModelInfo(providerId, model));\n }\n }\n return index;\n}\n\nfunction buildLogical(catalog: ProvidersCatalog): CatalogModel[] {\n const byKey = new Map<string, CatalogModel>();\n for (const [providerId, providerInfo] of Object.entries(catalog)) {\n for (const model of Object.values(providerInfo.models)) {\n const key = dedupKey(model.id);\n if (!key) continue;\n const bucket = mapProvider(providerId);\n const offering: ModelOffering = {\n id: model.id,\n rawProvider: providerId,\n bucket,\n pricing: {\n ...(model.cost?.input !== undefined ? { inputPerMTok: model.cost.input } : {}),\n ...(model.cost?.output !== undefined ? { outputPerMTok: model.cost.output } : {}),\n },\n ...(model.limit?.context !== undefined ? { contextWindow: model.limit.context } : {}),\n };\n const existing = byKey.get(key);\n if (existing) {\n existing.offerings.push(offering);\n if (!existing.buckets.includes(bucket)) existing.buckets.push(bucket);\n // Capabilities are the union across offerings.\n existing.capabilities.reasoning ||= model.reasoning ?? false;\n existing.capabilities.toolCall ||= model.tool_call ?? false;\n existing.capabilities.vision ||= model.modalities?.input?.includes(\"image\") ?? false;\n } else {\n byKey.set(key, {\n key,\n name: model.name ?? model.id,\n modality: classifyModality(model),\n capabilities: {\n reasoning: model.reasoning ?? false,\n toolCall: model.tool_call ?? false,\n vision: model.modalities?.input?.includes(\"image\") ?? false,\n },\n buckets: [bucket],\n offerings: [offering],\n });\n }\n }\n }\n return [...byKey.values()];\n}\n\nasync function loadCache(): Promise<CacheEntry> {\n const now = Date.now();\n if (cache && now - cache.fetchedAt < TTL_MS) return cache;\n if (inflight) return inflight;\n\n inflight = (async () => {\n try {\n const catalog = await fetchModels();\n cache = { index: buildIndex(catalog), logical: buildLogical(catalog), fetchedAt: Date.now() };\n return cache;\n } catch {\n // models.dev unreachable — reuse the last good catalog if we have one,\n // otherwise empty (callers degrade safely, e.g. BYOK → platform).\n if (cache) return cache;\n cache = { index: new Map(), logical: [], fetchedAt: Date.now() };\n return cache;\n } finally {\n inflight = null;\n }\n })();\n\n return inflight;\n}\n\nasync function loadIndex(): Promise<Map<string, ModelInfo>> {\n return (await loadCache()).index;\n}\n\n// ─── Public accessors ─────────────────────────────────────────────────────────\n\n/** Full model list (newest fetch, cached). */\nexport async function listModels(): Promise<ModelInfo[]> {\n return [...(await loadIndex()).values()];\n}\n\n/**\n * Deduped logical models for a given output modality — the canvas-orchestration\n * entry point. One entry per logical model, with the providers that serve it.\n * Sorted by offering count (most widely available first).\n */\nexport async function listModelsByModality(modality: ModelModality): Promise<CatalogModel[]> {\n const { logical } = await loadCache();\n return logical\n .filter((m) => m.modality === modality)\n .sort((a, b) => b.offerings.length - a.offerings.length);\n}\n\n/** Count of deduped logical models per modality (for canvas tab badges, etc.). */\nexport async function listModalities(): Promise<Record<ModelModality, number>> {\n const { logical } = await loadCache();\n const counts: Record<ModelModality, number> = {\n text: 0,\n image: 0,\n video: 0,\n audio: 0,\n embedding: 0,\n };\n for (const m of logical) counts[m.modality]++;\n return counts;\n}\n\n/** Look up a single model by id. `null` if unknown to the catalog. */\nexport async function getModelInfo(modelId: string): Promise<ModelInfo | null> {\n return (await loadIndex()).get(modelId) ?? null;\n}\n\n/**\n * Resolve the coarse provider bucket for a model id. Catalog-first; falls back\n * to id-prefix heuristics for brand-new models models.dev hasn't indexed yet.\n * `null` means \"unknown\" → callers should use the platform default.\n */\nexport async function providerForModel(modelId: string): Promise<AIProviderId | null> {\n const info = await getModelInfo(modelId);\n if (info) return info.provider;\n return providerFromIdHeuristic(modelId);\n}\n\n/** Last-resort id-prefix heuristic (only used when the catalog misses). */\nexport function providerFromIdHeuristic(modelId: string): AIProviderId | null {\n const m = modelId.toLowerCase().replace(/^[^/]+\\//, \"\"); // strip \"anthropic/\" style prefix\n if (/^(gpt-|o1|o3|o4|chatgpt|text-|davinci|babbage)/.test(m)) return \"OPENAI\";\n if (m.startsWith(\"claude\")) return \"ANTHROPIC\";\n if (m.startsWith(\"gemini\")) return \"GOOGLE\";\n if (/deepseek|qwen|glm|yi-|internlm|siliconflow|moonshot|kimi/.test(m)) return \"SILICONFLOW\";\n return null;\n}\n\n// ─── Frontier resolver (hybrid: live intersection + hardcoded fallback) ─────────\n//\n// Resolves a SEMANTIC tier (e.g. \"the latest reasoning model\") to a concrete\n// model id, picking the newest from the INTERSECTION of:\n// - gateway-routable ids (OpenRouter /api/v1/models — the gateway namespace), and\n// - models.dev catalog metadata (so the pick has pricing/context/capabilities).\n// On any failure (offline / empty) it returns the hardcoded fallback, so callers\n// always get a usable id. This is the \"don't hand-maintain model strings\" engine.\n\nexport type ModelTier =\n | \"reasoning\"\n | \"flagship\"\n | \"fast\"\n | \"openai-flagship\"\n | \"google-flagship\"\n | \"google-fast\";\n\nexport interface TierRule {\n /** Match on the OpenRouter (gateway) id. */\n include: RegExp;\n /**\n * Drop variants that are not the interactive frontier model of the tier.\n *\n * `:batch` belongs in every one of these. OpenRouter lists a batch-only\n * endpoint alongside most frontier models; it satisfies the family pattern,\n * and routing a live stream at one fails. Before 2026-08 nothing excluded\n * it and only Set iteration order kept it from being selected.\n */\n exclude: RegExp;\n}\n\n/**\n * Tier matching rules. Exported so `scripts/generate-frontier-models.mjs` can be\n * checked against them — the generator has to select by the same rules the\n * runtime resolves by, or the offline fallback stops matching the online pick.\n */\nexport const TIER_RULES: Record<ModelTier, TierRule> = {\n reasoning: {\n include: /^anthropic\\/claude-opus-/,\n exclude: /-(fast|mini|nano|image|codex)|:batch/,\n },\n flagship: {\n include: /^anthropic\\/claude-sonnet-/,\n exclude: /-(fast|mini|nano|image|codex)|:batch/,\n },\n fast: {\n include: /^anthropic\\/claude-haiku-/,\n exclude: /-(image|codex)|:batch/,\n },\n \"openai-flagship\": {\n include: /^openai\\/gpt-5/,\n // Prefer full flagship over mini/nano/chat/image/codex variants\n exclude: /-(pro|mini|nano|codex|image|chat)|gpt-5\\.\\d+-(mini|nano|pro)|:batch/,\n },\n \"google-flagship\": {\n include: /^google\\/gemini-.*pro/,\n exclude: /-(image|tts|customtools)|:batch/,\n },\n \"google-fast\": {\n include: /^google\\/gemini-.*flash/,\n exclude: /-(image|tts|lite)|:batch/,\n },\n};\n\n/**\n * Offline frontier fallbacks — GENERATED from the live gateway catalogue by\n * `pnpm gen:frontier-models`, not hand-typed. They used to be `fallback` fields\n * on the rules above with an audit date in a comment, and by 2026-08 they named\n * two-generations-old models that still routed fine, so nothing failed and the\n * only symptom was weaker output.\n */\nexport const FRONTIER_FALLBACK: Record<ModelTier, string> = GENERATED_FALLBACK;\n\nconst OPENROUTER_MODELS_URL =\n process.env.OPENROUTER_MODELS_URL ?? \"https://openrouter.ai/api/v1/models\";\n\nlet routableCache: { ids: Set<string>; fetchedAt: number } | null = null;\nlet routableInflight: Promise<Set<string>> | null = null;\n\n/** Gateway-routable model ids (OpenRouter), cached with the same TTL as the catalog. */\nasync function fetchRoutableIds(): Promise<Set<string>> {\n const now = Date.now();\n if (routableCache && now - routableCache.fetchedAt < TTL_MS) return routableCache.ids;\n if (routableInflight) return routableInflight;\n\n routableInflight = (async () => {\n try {\n const res = await fetch(OPENROUTER_MODELS_URL);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const json = (await res.json()) as { data?: Array<{ id?: string }> };\n const ids = new Set(\n (json.data ?? []).map((m) => m.id).filter((x): x is string => typeof x === \"string\"),\n );\n routableCache = { ids, fetchedAt: Date.now() };\n return ids;\n } catch {\n return routableCache?.ids ?? new Set<string>();\n } finally {\n routableInflight = null;\n }\n })();\n\n return routableInflight;\n}\n\n/** Collapse separators so `claude-opus-4.8` (OpenRouter) matches `claude-opus-4-8` (models.dev). */\nconst collapseId = (s: string): string => s.toLowerCase().replace(/[._-]/g, \"\");\nconst bareId = (id: string): string => {\n const slash = id.indexOf(\"/\");\n return slash >= 0 ? id.slice(slash + 1) : id;\n};\nconst versionOf = (id: string): number => {\n const m = bareId(id).match(/(\\d+(?:\\.\\d+)?)/);\n const raw = m?.[1];\n return raw ? Number.parseFloat(raw) : -1;\n};\n\n/**\n * Resolve a semantic tier to the newest concrete model id that is BOTH\n * gateway-routable and present in the models.dev catalog. Falls back to the\n * audited hardcoded frontier when the live lists are unavailable.\n */\nexport async function resolveFrontierModel(tier: ModelTier): Promise<string> {\n const rule = TIER_RULES[tier];\n const fallback = FRONTIER_FALLBACK[tier];\n try {\n const [routable, metaIndex] = await Promise.all([fetchRoutableIds(), loadIndex()]);\n if (routable.size === 0) return fallback;\n\n const metaNorm = new Set([...metaIndex.keys()].map(collapseId));\n const candidates = [...routable].filter(\n (id) =>\n rule.include.test(id) && !rule.exclude.test(id) && metaNorm.has(collapseId(bareId(id))),\n );\n if (candidates.length === 0) return fallback;\n\n candidates.sort((a, b) => versionOf(b) - versionOf(a));\n return candidates[0] ?? fallback;\n } catch {\n return fallback;\n }\n}\n\n// ─── Per-node model spec resolution ─────────────────────────────────────────────\n//\n// Resolves a node's model HINTS (id pin OR modality/effort/capability) to a\n// concrete model id. Mirrors agent-runtime NodeModelSpec structurally (plain\n// object — no cross-package dependency). Used by the workflow/agent executor.\n\nexport interface ModelSpecHints {\n /** Explicit pin — wins over every hint. Preset alias or `provider/model` id. */\n id?: string;\n reasoningEffort?: \"low\" | \"medium\" | \"high\" | \"xhigh\";\n modality?: ModelModality;\n capabilities?: { vision?: boolean; toolCall?: boolean; reasoning?: boolean };\n}\n\nconst EFFORT_TIER: Record<NonNullable<ModelSpecHints[\"reasoningEffort\"]>, ModelTier> = {\n low: \"fast\",\n medium: \"flagship\",\n high: \"flagship\",\n xhigh: \"reasoning\",\n};\n\nfunction pickByCapabilities(\n models: CatalogModel[],\n caps?: ModelSpecHints[\"capabilities\"],\n): CatalogModel | null {\n const filtered = caps\n ? models.filter(\n (m) =>\n (!caps.vision || m.capabilities.vision) &&\n (!caps.toolCall || m.capabilities.toolCall) &&\n (!caps.reasoning || m.capabilities.reasoning),\n )\n : models;\n return filtered[0] ?? null;\n}\n\n/**\n * Resolve a per-node model spec to a concrete model id. Precedence: explicit\n * `id` → non-text `modality` (most-available matching model) → `reasoningEffort`\n * tier (text frontier) → text `capabilities` match → `fallback` (the run\n * default). Never throws — degrades to `fallback`.\n */\nexport async function resolveModelSpec(spec: ModelSpecHints, fallback: string): Promise<string> {\n try {\n if (spec.id) return spec.id;\n\n if (spec.modality && spec.modality !== \"text\") {\n const match = pickByCapabilities(\n await listModelsByModality(spec.modality),\n spec.capabilities,\n );\n if (match) return match.offerings[0]?.id ?? match.key;\n return fallback;\n }\n\n if (spec.reasoningEffort) return resolveFrontierModel(EFFORT_TIER[spec.reasoningEffort]);\n\n if (spec.capabilities) {\n const match = pickByCapabilities(await listModelsByModality(\"text\"), spec.capabilities);\n if (match) return match.offerings[0]?.id ?? match.key;\n }\n\n return fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Default OpenAI-compatible base URL per provider bucket (env-overridable).\n * Single home for these — BYOK and any other caller import from here.\n */\nexport function providerBaseUrl(provider: AIProviderId): string | null {\n switch (provider) {\n case \"OPENAI\":\n return process.env.OPENAI_BASE_URL ?? \"https://api.openai.com/v1\";\n case \"ANTHROPIC\":\n return process.env.ANTHROPIC_BASE_URL ?? \"https://api.anthropic.com/v1\";\n case \"GOOGLE\":\n return (\n process.env.GOOGLE_BASE_URL ?? \"https://generativelanguage.googleapis.com/v1beta/openai\"\n );\n case \"SILICONFLOW\":\n return process.env.SILICONFLOW_BASE_URL ?? \"https://api.siliconflow.cn/v1\";\n default:\n // CUSTOM has no canonical endpoint — the caller must supply a baseUrl.\n return null;\n }\n}\n","/**\n * Provider registry metadata — single source of truth for the\n * create-sailor CLI and documentation generators.\n *\n * Coverage: 20 providers across direct labs, CN platforms, gateways,\n * inference accelerators, multimodal, local, and developer ecosystem.\n */\n\nexport type ProviderStatus = \"opencode\" | \"ai-sdk\" | \"cn-compatible\" | \"pending\";\n\nexport type ProviderCategory =\n | \"直接实验室\"\n | \"国内平台\"\n | \"云平台\"\n | \"推理加速\"\n | \"统一网关\"\n | \"多模态\"\n | \"本地部署\"\n | \"开发者生态\";\n\nexport interface ProviderMeta {\n id: string;\n name: string;\n category: ProviderCategory;\n status: ProviderStatus;\n docs: string;\n baseURL?: string;\n envVarPrefix: string;\n requiredEnvVars: string[];\n}\n\nexport const PROVIDERS: ProviderMeta[] = [\n // ─── 直接实验室 ───────────────────────────────────────────────\n {\n id: \"anthropic\",\n name: \"Anthropic\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://docs.anthropic.com\",\n envVarPrefix: \"ANTHROPIC\",\n requiredEnvVars: [\"ANTHROPIC_API_KEY\"],\n },\n {\n id: \"openai\",\n name: \"OpenAI\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://platform.openai.com/docs\",\n envVarPrefix: \"OPENAI\",\n requiredEnvVars: [\"OPENAI_API_KEY\"],\n },\n {\n id: \"deepseek\",\n name: \"DeepSeek\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://platform.deepseek.com/api-docs\",\n envVarPrefix: \"DEEPSEEK\",\n requiredEnvVars: [\"DEEPSEEK_API_KEY\"],\n },\n {\n id: \"xai\",\n name: \"xAI (Grok)\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://docs.x.ai/api\",\n envVarPrefix: \"XAI\",\n requiredEnvVars: [\"XAI_API_KEY\"],\n },\n {\n id: \"moonshot\",\n name: \"Moonshot (Kimi)\",\n category: \"直接实验室\",\n status: \"opencode\",\n docs: \"https://platform.moonshot.cn/docs\",\n envVarPrefix: \"MOONSHOT\",\n requiredEnvVars: [\"MOONSHOT_API_KEY\", \"MOONSHOT_BASE_URL\"],\n },\n {\n id: \"google\",\n name: \"Google Gemini\",\n category: \"直接实验室\",\n status: \"ai-sdk\",\n docs: \"https://ai.google.dev/gemini-api/docs\",\n envVarPrefix: \"GOOGLE_GENERATIVE_AI\",\n requiredEnvVars: [\"GOOGLE_GENERATIVE_AI_API_KEY\"],\n },\n {\n id: \"mistral\",\n name: \"Mistral AI\",\n category: \"直接实验室\",\n status: \"ai-sdk\",\n docs: \"https://docs.mistral.ai\",\n envVarPrefix: \"MISTRAL\",\n requiredEnvVars: [\"MISTRAL_API_KEY\"],\n },\n {\n id: \"cohere\",\n name: \"Cohere\",\n category: \"直接实验室\",\n status: \"ai-sdk\",\n docs: \"https://docs.cohere.com\",\n envVarPrefix: \"COHERE\",\n requiredEnvVars: [\"COHERE_API_KEY\"],\n },\n {\n id: \"perplexity\",\n name: \"Perplexity\",\n category: \"直接实验室\",\n status: \"cn-compatible\",\n docs: \"https://docs.perplexity.ai\",\n baseURL: \"https://api.perplexity.ai\",\n envVarPrefix: \"PERPLEXITY\",\n requiredEnvVars: [\"PERPLEXITY_API_KEY\"],\n },\n {\n id: \"ai21\",\n name: \"AI21 Labs\",\n category: \"直接实验室\",\n status: \"cn-compatible\",\n docs: \"https://docs.ai21.com\",\n baseURL: \"https://api.ai21.com/studio/v1\",\n envVarPrefix: \"AI21\",\n requiredEnvVars: [\"AI21_API_KEY\"],\n },\n {\n id: \"upstage\",\n name: \"Upstage (Solar)\",\n category: \"直接实验室\",\n status: \"cn-compatible\",\n docs: \"https://developers.upstage.ai/docs\",\n baseURL: \"https://api.upstage.ai/v1/solar\",\n envVarPrefix: \"UPSTAGE\",\n requiredEnvVars: [\"UPSTAGE_API_KEY\"],\n },\n\n // ─── 国内平台 ───────────────────────────────────────────────\n {\n id: \"siliconflow\",\n name: \"硅基流动 SiliconFlow\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://docs.siliconflow.cn\",\n baseURL: \"https://api.siliconflow.cn/v1\",\n envVarPrefix: \"SILICONFLOW\",\n requiredEnvVars: [\"SILICONFLOW_API_KEY\"],\n },\n {\n id: \"volcengine-ark\",\n name: \"火山引擎 ARK (豆包)\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://www.volcengine.com/docs/82379\",\n baseURL: \"https://ark.cn-beijing.volces.com/api/v3\",\n envVarPrefix: \"ARK\",\n requiredEnvVars: [\"ARK_API_KEY\"],\n },\n {\n id: \"bailian\",\n name: \"百炼 Bailian (通义)\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://help.aliyun.com/zh/model-studio\",\n baseURL: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n envVarPrefix: \"DASHSCOPE\",\n requiredEnvVars: [\"DASHSCOPE_API_KEY\"],\n },\n {\n id: \"zhipu\",\n name: \"智谱 AI Zhipu\",\n category: \"国内平台\",\n status: \"ai-sdk\",\n docs: \"https://docs.bigmodel.cn\",\n baseURL: \"https://open.bigmodel.cn/api/paas/v4\",\n envVarPrefix: \"ZHIPU\",\n requiredEnvVars: [\"ZHIPU_API_KEY\"],\n },\n {\n id: \"baichuan\",\n name: \"百川智能 Baichuan\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.baichuan-ai.com/docs\",\n baseURL: \"https://api.baichuan-ai.com/v1\",\n envVarPrefix: \"BAICHUAN\",\n requiredEnvVars: [\"BAICHUAN_API_KEY\"],\n },\n {\n id: \"minimax\",\n name: \"MiniMax (ABAB)\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.minimaxi.com/document/guides\",\n baseURL: \"https://api.minimax.chat/v1\",\n envVarPrefix: \"MINIMAX\",\n requiredEnvVars: [\"MINIMAX_API_KEY\", \"MINIMAX_GROUP_ID\"],\n },\n {\n id: \"stepfun\",\n name: \"阶跃星辰 StepFun\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.stepfun.com/docs\",\n baseURL: \"https://api.stepfun.com/v1\",\n envVarPrefix: \"STEPFUN\",\n requiredEnvVars: [\"STEPFUN_API_KEY\"],\n },\n {\n id: \"sensetime\",\n name: \"商汤 SenseNova\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.sensenova.cn/docs\",\n // Token Plan OpenAI-compatible surface (NOT enterprise api.sensenova.cn)\n // Ref: https://github.com/OpenSenseNova/SenseNova6.7/blob/main/API_CN.md\n baseURL: \"https://token.sensenova.cn/v1\",\n envVarPrefix: \"SENSENOVA\",\n requiredEnvVars: [\"SENSENOVA_API_KEY\"],\n },\n {\n id: \"tencent\",\n name: \"腾讯混元 Hunyuan\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://cloud.tencent.com/document/product/1729\",\n baseURL: \"https://api.hunyuan.cloud.tencent.com/v1\",\n envVarPrefix: \"HUNYUAN\",\n requiredEnvVars: [\"HUNYUAN_API_KEY\"],\n },\n {\n id: \"lingyi\",\n name: \"零一万物 Yi\",\n category: \"国内平台\",\n status: \"cn-compatible\",\n docs: \"https://platform.lingyiwanwu.com/docs\",\n baseURL: \"https://api.lingyiwanwu.com/v1\",\n envVarPrefix: \"YI\",\n requiredEnvVars: [\"YI_API_KEY\"],\n },\n\n // ─── 统一网关 ───────────────────────────────────────────────\n {\n id: \"openrouter\",\n name: \"OpenRouter\",\n category: \"统一网关\",\n status: \"opencode\",\n docs: \"https://openrouter.ai/docs\",\n envVarPrefix: \"OPENROUTER\",\n requiredEnvVars: [\"OPENROUTER_API_KEY\"],\n },\n {\n id: \"vercel-gateway\",\n name: \"Vercel AI Gateway\",\n category: \"统一网关\",\n status: \"opencode\",\n docs: \"https://vercel.com/docs/ai-gateway\",\n envVarPrefix: \"AI_GATEWAY\",\n requiredEnvVars: [\"AI_GATEWAY_API_KEY\"],\n },\n {\n id: \"litellm\",\n name: \"LiteLLM Proxy\",\n category: \"统一网关\",\n status: \"cn-compatible\",\n docs: \"https://docs.litellm.ai\",\n baseURL: \"http://localhost:4000/v1\",\n envVarPrefix: \"LITELLM\",\n requiredEnvVars: [\"LITELLM_API_KEY\"],\n },\n {\n id: \"portkey\",\n name: \"Portkey\",\n category: \"统一网关\",\n status: \"cn-compatible\",\n docs: \"https://portkey.ai/docs\",\n baseURL: \"https://api.portkey.ai/v1\",\n envVarPrefix: \"PORTKEY\",\n requiredEnvVars: [\"PORTKEY_API_KEY\"],\n },\n {\n id: \"aws-bedrock\",\n name: \"AWS Bedrock\",\n category: \"云平台\",\n status: \"ai-sdk\",\n docs: \"https://aws.amazon.com/bedrock/\",\n envVarPrefix: \"AWS_BEDROCK\",\n requiredEnvVars: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\", \"AWS_REGION\"],\n },\n {\n id: \"azure-openai\",\n name: \"Azure OpenAI\",\n category: \"云平台\",\n status: \"ai-sdk\",\n docs: \"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/\",\n envVarPrefix: \"AZURE_OPENAI\",\n requiredEnvVars: [\"AZURE_OPENAI_API_KEY\", \"AZURE_OPENAI_ENDPOINT\"],\n },\n {\n id: \"gcp-vertex\",\n name: \"GCP Vertex AI\",\n category: \"云平台\",\n status: \"ai-sdk\",\n docs: \"https://cloud.google.com/vertex-ai\",\n envVarPrefix: \"GOOGLE_VERTEX\",\n requiredEnvVars: [\"GOOGLE_APPLICATION_CREDENTIALS\"],\n },\n\n // ─── 推理加速 ───────────────────────────────────────────────\n {\n id: \"groq\",\n name: \"Groq\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://console.groq.com/docs\",\n envVarPrefix: \"GROQ\",\n requiredEnvVars: [\"GROQ_API_KEY\"],\n },\n {\n id: \"fireworks\",\n name: \"Fireworks AI\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://docs.fireworks.ai\",\n envVarPrefix: \"FIREWORKS\",\n requiredEnvVars: [\"FIREWORKS_API_KEY\"],\n },\n {\n id: \"together\",\n name: \"Together AI\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://docs.together.ai\",\n envVarPrefix: \"TOGETHER\",\n requiredEnvVars: [\"TOGETHER_API_KEY\"],\n },\n {\n id: \"huggingface\",\n name: \"Hugging Face\",\n category: \"推理加速\",\n status: \"opencode\",\n docs: \"https://huggingface.co/docs/inference-providers\",\n envVarPrefix: \"HF\",\n requiredEnvVars: [\"HF_API_KEY\"],\n },\n {\n id: \"replicate\",\n name: \"Replicate\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://replicate.com/docs\",\n envVarPrefix: \"REPLICATE\",\n requiredEnvVars: [\"REPLICATE_API_TOKEN\"],\n },\n {\n id: \"lepton\",\n name: \"Lepton AI\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://www.lepton.ai/docs\",\n baseURL: \"https://api.lepton.ai/v1\",\n envVarPrefix: \"LEPTON\",\n requiredEnvVars: [\"LEPTON_API_KEY\"],\n },\n {\n id: \"anyscale\",\n name: \"Anyscale Endpoints\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://docs.endpoints.anyscale.com/\",\n baseURL: \"https://api.endpoints.anyscale.com/v1\",\n envVarPrefix: \"ANYSCALE\",\n requiredEnvVars: [\"ANYSCALE_API_KEY\"],\n },\n {\n id: \"octoai\",\n name: \"OctoAI\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://octoai.cloud/docs\",\n baseURL: \"https://text.octoai.run/v1\",\n envVarPrefix: \"OCTOAI\",\n requiredEnvVars: [\"OCTOAI_TOKEN\"],\n },\n {\n id: \"deepinfra\",\n name: \"DeepInfra\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://deepinfra.com/docs\",\n baseURL: \"https://api.deepinfra.com/v1/openai\",\n envVarPrefix: \"DEEPINFRA\",\n requiredEnvVars: [\"DEEPINFRA_API_KEY\"],\n },\n {\n id: \"novita\",\n name: \"Novita AI\",\n category: \"推理加速\",\n status: \"cn-compatible\",\n docs: \"https://novita.ai/docs\",\n baseURL: \"https://api.novita.ai/v3/openai\",\n envVarPrefix: \"NOVITA\",\n requiredEnvVars: [\"NOVITA_API_KEY\"],\n },\n\n // ─── 多模态 ───────────────────────────────────────────────\n {\n id: \"black-forest-labs\",\n name: \"Black Forest Labs (FLUX)\",\n category: \"多模态\",\n status: \"ai-sdk\",\n docs: \"https://docs.bfl.ml\",\n envVarPrefix: \"BFL\",\n requiredEnvVars: [\"BFL_API_KEY\"],\n },\n {\n id: \"elevenlabs\",\n name: \"ElevenLabs (TTS)\",\n category: \"多模态\",\n status: \"ai-sdk\",\n docs: \"https://elevenlabs.io/docs\",\n envVarPrefix: \"ELEVENLABS\",\n requiredEnvVars: [\"ELEVENLABS_API_KEY\"],\n },\n {\n id: \"runway\",\n name: \"Runway (Video)\",\n category: \"多模态\",\n status: \"pending\",\n docs: \"https://runwayml.com\",\n envVarPrefix: \"RUNWAY\",\n requiredEnvVars: [\"RUNWAY_API_KEY\"],\n },\n {\n id: \"luma\",\n name: \"Luma (Video)\",\n category: \"多模态\",\n status: \"pending\",\n docs: \"https://lumalabs.ai\",\n envVarPrefix: \"LUMA\",\n requiredEnvVars: [\"LUMA_API_KEY\"],\n },\n {\n id: \"midjourney\",\n name: \"Midjourney\",\n category: \"多模态\",\n status: \"pending\",\n docs: \"https://docs.midjourney.com\",\n envVarPrefix: \"MIDJOURNEY\",\n requiredEnvVars: [],\n },\n\n // ─── 本地部署 ───────────────────────────────────────────────\n {\n id: \"ollama\",\n name: \"Ollama (本地)\",\n category: \"本地部署\",\n status: \"opencode\",\n docs: \"https://github.com/ollama/ollama\",\n baseURL: \"http://localhost:11434/v1\",\n envVarPrefix: \"OLLAMA\",\n requiredEnvVars: [\"OLLAMA_BASE_URL\"],\n },\n {\n id: \"lmstudio\",\n name: \"LM Studio\",\n category: \"本地部署\",\n status: \"cn-compatible\",\n docs: \"https://lmstudio.ai/docs\",\n baseURL: \"http://localhost:1234/v1\",\n envVarPrefix: \"LMSTUDIO\",\n requiredEnvVars: [],\n },\n {\n id: \"vllm\",\n name: \"vLLM\",\n category: \"本地部署\",\n status: \"cn-compatible\",\n docs: \"https://docs.vllm.ai\",\n baseURL: \"http://localhost:8000/v1\",\n envVarPrefix: \"VLLM\",\n requiredEnvVars: [],\n },\n {\n id: \"tgi\",\n name: \"HF TGI\",\n category: \"本地部署\",\n status: \"cn-compatible\",\n docs: \"https://huggingface.co/docs/text-generation-inference\",\n baseURL: \"http://localhost:8080/v1\",\n envVarPrefix: \"TGI\",\n requiredEnvVars: [],\n },\n];\n\nexport const PROVIDERS_BY_CATEGORY = PROVIDERS.reduce<Record<ProviderCategory, ProviderMeta[]>>(\n (acc, p) => {\n if (!acc[p.category]) acc[p.category] = [];\n acc[p.category].push(p);\n return acc;\n },\n {} as Record<ProviderCategory, ProviderMeta[]>,\n);\n\nexport function getProvider(id: string): ProviderMeta | undefined {\n return PROVIDERS.find((p) => p.id === id);\n}\n\nexport const isCNCompatible = (id: string): boolean => getProvider(id)?.status === \"cn-compatible\";\n"]}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@nebutra/ai-providers",
3
- "version": "1.0.1",
3
+ "version": "3.0.0",
4
4
  "private": false,
5
5
  "description": "Provider registry metadata for Nebutra — meta-only, consumed by @nebutra/create-sailor and docs. Runtime lives in @nebutra/agents.",
6
6
  "sideEffects": false,
7
7
  "type": "module",
8
8
  "nebutra": {
9
+ "graph": "runtime",
9
10
  "featureId": "ai-providers",
10
11
  "category": "ai",
11
12
  "surface": "provider-metadata",