@x12i/ai-profiles 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -16
- package/dist/buildCatalogFromOpenRouterApi.d.ts +29 -0
- package/dist/buildCatalogFromOpenRouterApi.d.ts.map +1 -0
- package/dist/buildCatalogFromOpenRouterApi.js +113 -0
- package/dist/buildCatalogFromOpenRouterApi.js.map +1 -0
- package/dist/buildModelsRegistry.d.ts +25 -0
- package/dist/buildModelsRegistry.d.ts.map +1 -0
- package/dist/buildModelsRegistry.js +190 -0
- package/dist/buildModelsRegistry.js.map +1 -0
- package/dist/buildResolvedProfile.d.ts +3 -1
- package/dist/buildResolvedProfile.d.ts.map +1 -1
- package/dist/buildResolvedProfile.js +94 -11
- package/dist/buildResolvedProfile.js.map +1 -1
- package/dist/catalogEnrichment.d.ts +19 -0
- package/dist/catalogEnrichment.d.ts.map +1 -0
- package/dist/catalogEnrichment.js +87 -0
- package/dist/catalogEnrichment.js.map +1 -0
- package/dist/computeRegistryStatistics.d.ts +15 -0
- package/dist/computeRegistryStatistics.d.ts.map +1 -0
- package/dist/computeRegistryStatistics.js +168 -0
- package/dist/computeRegistryStatistics.js.map +1 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -1
- package/dist/data/models-catalog.json +2219 -0
- package/dist/data/models-profiles.json +1361 -193
- package/dist/data/models-profiles.schema.json +130 -0
- package/dist/data/models-registry.json +1606 -0
- package/dist/data/openrouter-models-catalog.json +857 -0
- package/dist/data/profiles-shortcuts.json +101 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/formatRegistryStatistics.d.ts +4 -0
- package/dist/formatRegistryStatistics.d.ts.map +1 -0
- package/dist/formatRegistryStatistics.js +34 -0
- package/dist/formatRegistryStatistics.js.map +1 -0
- package/dist/getAIRegistryStatistics.d.ts +11 -0
- package/dist/getAIRegistryStatistics.d.ts.map +1 -0
- package/dist/getAIRegistryStatistics.js +52 -0
- package/dist/getAIRegistryStatistics.js.map +1 -0
- package/dist/index.d.ts +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/listAIProfiles.d.ts +6 -0
- package/dist/listAIProfiles.d.ts.map +1 -1
- package/dist/listAIProfiles.js +6 -0
- package/dist/listAIProfiles.js.map +1 -1
- package/dist/loadModelsRegistry.d.ts +6 -0
- package/dist/loadModelsRegistry.d.ts.map +1 -0
- package/dist/loadModelsRegistry.js +43 -0
- package/dist/loadModelsRegistry.js.map +1 -0
- package/dist/loader/catalogMemoryCache.d.ts +12 -0
- package/dist/loader/catalogMemoryCache.d.ts.map +1 -0
- package/dist/loader/catalogMemoryCache.js +14 -0
- package/dist/loader/catalogMemoryCache.js.map +1 -0
- package/dist/loader/loadBundledModelsCatalog.d.ts +3 -0
- package/dist/loader/loadBundledModelsCatalog.d.ts.map +1 -0
- package/dist/loader/loadBundledModelsCatalog.js +20 -0
- package/dist/loader/loadBundledModelsCatalog.js.map +1 -0
- package/dist/loader/loadBundledModelsProfiles.d.ts +4 -0
- package/dist/loader/loadBundledModelsProfiles.d.ts.map +1 -0
- package/dist/loader/loadBundledModelsProfiles.js +14 -0
- package/dist/loader/loadBundledModelsProfiles.js.map +1 -0
- package/dist/loader/loadBundledRegistry.d.ts.map +1 -1
- package/dist/loader/loadBundledRegistry.js +2 -1
- package/dist/loader/loadBundledRegistry.js.map +1 -1
- package/dist/loader/loadModelsCatalog.d.ts +9 -0
- package/dist/loader/loadModelsCatalog.d.ts.map +1 -0
- package/dist/loader/loadModelsCatalog.js +72 -0
- package/dist/loader/loadModelsCatalog.js.map +1 -0
- package/dist/loader/loadModelsProfilesJson.d.ts +3 -0
- package/dist/loader/loadModelsProfilesJson.d.ts.map +1 -0
- package/dist/loader/loadModelsProfilesJson.js +18 -0
- package/dist/loader/loadModelsProfilesJson.js.map +1 -0
- package/dist/loader/loadRemoteModelsCatalog.d.ts +3 -0
- package/dist/loader/loadRemoteModelsCatalog.d.ts.map +1 -0
- package/dist/loader/loadRemoteModelsCatalog.js +23 -0
- package/dist/loader/loadRemoteModelsCatalog.js.map +1 -0
- package/dist/loader/loadRemoteModelsProfiles.d.ts +3 -0
- package/dist/loader/loadRemoteModelsProfiles.d.ts.map +1 -0
- package/dist/loader/loadRemoteModelsProfiles.js +21 -0
- package/dist/loader/loadRemoteModelsProfiles.js.map +1 -0
- package/dist/loader/loadRemoteRegistry.d.ts.map +1 -1
- package/dist/loader/loadRemoteRegistry.js +2 -1
- package/dist/loader/loadRemoteRegistry.js.map +1 -1
- package/dist/mergeCatalogSupplement.d.ts +4 -0
- package/dist/mergeCatalogSupplement.d.ts.map +1 -0
- package/dist/mergeCatalogSupplement.js +26 -0
- package/dist/mergeCatalogSupplement.js.map +1 -0
- package/dist/modelIdentity.d.ts +33 -0
- package/dist/modelIdentity.d.ts.map +1 -0
- package/dist/modelIdentity.js +150 -0
- package/dist/modelIdentity.js.map +1 -0
- package/dist/modelsCatalog.d.ts +21 -0
- package/dist/modelsCatalog.d.ts.map +1 -0
- package/dist/modelsCatalog.js +187 -0
- package/dist/modelsCatalog.js.map +1 -0
- package/dist/profileCatalogSupplement.d.ts +7 -0
- package/dist/profileCatalogSupplement.d.ts.map +1 -0
- package/dist/profileCatalogSupplement.js +292 -0
- package/dist/profileCatalogSupplement.js.map +1 -0
- package/dist/profileRegistryV2.d.ts +19 -0
- package/dist/profileRegistryV2.d.ts.map +1 -0
- package/dist/profileRegistryV2.js +257 -0
- package/dist/profileRegistryV2.js.map +1 -0
- package/dist/profileRegistryV3.d.ts +11 -0
- package/dist/profileRegistryV3.d.ts.map +1 -0
- package/dist/profileRegistryV3.js +526 -0
- package/dist/profileRegistryV3.js.map +1 -0
- package/dist/resolveAIProfile.d.ts +8 -0
- package/dist/resolveAIProfile.d.ts.map +1 -1
- package/dist/resolveAIProfile.js +62 -18
- package/dist/resolveAIProfile.js.map +1 -1
- package/dist/resolveModelRouting.d.ts +2 -1
- package/dist/resolveModelRouting.d.ts.map +1 -1
- package/dist/resolveModelRouting.js +9 -10
- package/dist/resolveModelRouting.js.map +1 -1
- package/dist/types.d.ts +339 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/dist/validateProfileVendorCoverage.d.ts +12 -0
- package/dist/validateProfileVendorCoverage.d.ts.map +1 -0
- package/dist/validateProfileVendorCoverage.js +58 -0
- package/dist/validateProfileVendorCoverage.js.map +1 -0
- package/dist/validation/openRouterAlignment.d.ts +6 -15
- package/dist/validation/openRouterAlignment.d.ts.map +1 -1
- package/dist/validation/openRouterAlignment.js +9 -57
- package/dist/validation/openRouterAlignment.js.map +1 -1
- package/dist/validation/validateModelsCatalog.d.ts +3 -0
- package/dist/validation/validateModelsCatalog.d.ts.map +1 -0
- package/dist/validation/validateModelsCatalog.js +42 -0
- package/dist/validation/validateModelsCatalog.js.map +1 -0
- package/dist/validation/validateModelsProfilesJson.d.ts.map +1 -1
- package/dist/validation/validateModelsProfilesJson.js +38 -2
- package/dist/validation/validateModelsProfilesJson.js.map +1 -1
- package/dist/vendorPolicy.d.ts +20 -0
- package/dist/vendorPolicy.d.ts.map +1 -0
- package/dist/vendorPolicy.js +71 -0
- package/dist/vendorPolicy.js.map +1 -0
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -2,6 +2,86 @@
|
|
|
2
2
|
|
|
3
3
|
Resolve AI profile names into concrete model choices. The package ships JSON in `src/data/` as the offline fallback. By default, registry data is fetched from public assets, validated, and cached in memory.
|
|
4
4
|
|
|
5
|
+
## Why AI profiles
|
|
6
|
+
|
|
7
|
+
Any system that runs many different AI tasks—classification, architecture review, JSON extraction, tool-using agents—faces the same problem: **tasks are not interchangeable**. They differ in reasoning quality, cost sensitivity, latency, output shape (prose vs schema vs citations), and which provider traits matter (instruction following, long context, tool use).
|
|
8
|
+
|
|
9
|
+
Hardcoding one model everywhere leads to one of three failures: overpaying for simple high-volume work, under-serving hard problems with a weak model, or locking the product to a single vendor with no fallback when prices or availability change.
|
|
10
|
+
|
|
11
|
+
### What a profile is
|
|
12
|
+
|
|
13
|
+
An **AI profile** is **not** a model. It is a **capability contract**: a stable name for *what kind of work* you need, not *which SKU* fulfills it.
|
|
14
|
+
|
|
15
|
+
When a graph node or function asks for `deep`, it means: high reasoning effort, complex work—resolve that intent to a concrete `provider` + `modelId` + runtime defaults **today**, without the caller caring whether that is Claude, GPT, or Gemini. The registry (`models-profiles.json`) is the mapping layer; profiles are the interface tasks depend on.
|
|
16
|
+
|
|
17
|
+
### Profiles vs choices
|
|
18
|
+
|
|
19
|
+
| Layer | Role | Example |
|
|
20
|
+
|-------|------|---------|
|
|
21
|
+
| **Profile** | Semantic intent (cost, speed, reasoning tier, output mode, agentic behavior) | `cheap`, `balanced`, `json`, `agentic` |
|
|
22
|
+
| **Choice** | Implementation option within that profile (provider, model, batch vs sync, cost/quality trade-off) | `default`, `cost_floor`, `safer_provider` |
|
|
23
|
+
|
|
24
|
+
Every profile has a **`defaultChoice`** (recommended) and typically **two alternatives** so operators can tune without changing task code:
|
|
25
|
+
|
|
26
|
+
- **Provider diversity** — Anthropic (writing/reasoning), OpenAI (code/instruction following), Google (cost, long context) within the same profile.
|
|
27
|
+
- **Cost flexibility** — e.g. `balanced` + `cost_balanced` vs `reasoning_balanced` for the same “normal work” intent.
|
|
28
|
+
- **Capability variants** — e.g. `long_context_deep` when the constraint is window size, not reasoning tier alone.
|
|
29
|
+
- **Execution mode** — e.g. `cheap` + `cost_floor` with `executionMode: batch` for background bulk work.
|
|
30
|
+
- **Resilience** — swap choice or fall back within the same profile when a provider is down or rate-limited.
|
|
31
|
+
|
|
32
|
+
**Shortcuts** (`profiles-shortcuts.json`) are named entry points that may pin a profile and optionally a choice (e.g. `cheapest` → `cheap` + `cost_floor`). **Aliases** on each profile are alternate names for the same profile (e.g. `economy` → `cheap`).
|
|
33
|
+
|
|
34
|
+
Tasks should declare **requirements** (`profile` + optional `choice`), not model names. Profiles are the vocabulary; choices are how that vocabulary is implemented under different constraints.
|
|
35
|
+
|
|
36
|
+
### Profile catalog (bundled)
|
|
37
|
+
|
|
38
|
+
Profiles are grouped by **what kind of work** they represent—not only by price/quality tier. Several axes are orthogonal (structured output, agents, research, local inference).
|
|
39
|
+
|
|
40
|
+
| Profile | Category | When to use it |
|
|
41
|
+
|---------|----------|----------------|
|
|
42
|
+
| `cheap` | cost | High-volume, non-critical work: routing, classification, simple transforms. “Cheap” means low-cost selection, not always the absolute cheapest SKU. |
|
|
43
|
+
| `fast` | speed | Interactive UX where latency matters more than maximum reasoning depth. |
|
|
44
|
+
| `balanced` | standard | Default workhorse: general graph execution, coding, product logic (~80% of tasks). |
|
|
45
|
+
| `deep` | reasoning | Hard problems: architecture, planning, analysis, code review—work that needs real reasoning, not pattern matching. |
|
|
46
|
+
| `pro` | premium | Critical outputs where quality or a wrong answer has high downstream cost; use sparingly. |
|
|
47
|
+
| `json` | output | Strict JSON/schema, extraction, classification—machine-readable pipelines (`outputMode: schema`, low temperature). |
|
|
48
|
+
| `agentic` | agentic | Tool use, planning, multi-step execution (`toolPolicy` allows writes with approval). |
|
|
49
|
+
| `research` | research | Source-heavy reading, synthesis, citations (read-only tools, web/file search). |
|
|
50
|
+
| `cyber` | security | Threat analysis, TPRM, contract verification, security log parsing. |
|
|
51
|
+
| `content` | creative | Prose, marketing copy, programmatic SEO, brand voice. |
|
|
52
|
+
| `summarization` | analysis | Document triage, digests, compressing very large corpora. |
|
|
53
|
+
| `vision` | multimodal | Image understanding, OCR, charts, scanned PDF structure extraction. |
|
|
54
|
+
| `coding` | engineering | Software engineering: implementation, refactors, reviews, debugging. |
|
|
55
|
+
| `extraction` | extraction | NER, classification, taxonomy mapping, entity extraction, document labeling. |
|
|
56
|
+
| `routing` | routing | Intent classification, record routing, workflow branching (cheapest viable). |
|
|
57
|
+
| `open_weights` | resilience | Hosted open-source models (Llama, Qwen) as vendor hedge and self-host path (`deploymentType: openWeightsHosted`). |
|
|
58
|
+
| `local` | local | On-device / offline CPU inference (privacy, edge, dev without API keys). |
|
|
59
|
+
|
|
60
|
+
### Typical choices per profile
|
|
61
|
+
|
|
62
|
+
Each profile’s `choices` in `models-profiles.json` follow a consistent pattern: **`default`** (recommended), plus variants along cost, provider family, or a specific capability.
|
|
63
|
+
|
|
64
|
+
| Profile | Choices (bundled) | What the variants optimize |
|
|
65
|
+
|---------|-------------------|------------------------------|
|
|
66
|
+
| `cheap` | `default`, `cost_floor`, `safer_provider`, `absolute_floor` | Normal cheap vs batch floor vs safer Haiku vs DeepSeek V4 Flash volume floor |
|
|
67
|
+
| `fast` | `default`, `cheaper_fast`, `quality_fast`, `grok_fast` | Interactive default vs lower cost vs stronger quality vs Grok 4.1 Fast (2M context) |
|
|
68
|
+
| `balanced` | `default`, `reasoning_balanced`, `cost_balanced` | General default vs stronger reasoning vs lower cost |
|
|
69
|
+
| `deep` | `default`, `openai_deep`, `long_context_deep`, `deepseek_pro`, `grok_flagship` | High reasoning default vs OpenAI vs long context vs cost-efficient DeepSeek vs Grok 4.3 |
|
|
70
|
+
| `pro` | `default`, `claude_pro`, `cost_pro` | Premium default vs Claude premium vs Sonnet-level “pro-like” at lower cost |
|
|
71
|
+
| `json` | `default`, `cheap_json`, `quality_json` | Reliable schema output vs volume/cost vs complex extraction needing reasoning |
|
|
72
|
+
| `agentic` | `default`, `openai_agentic`, `cheap_agentic` | Agent default vs OpenAI orchestration vs lower-cost moderate agents |
|
|
73
|
+
| `research` | `default`, `premium_research`, `long_context_research` | Synthesis default vs highest quality vs very large context |
|
|
74
|
+
| `cyber` | `default`, `log_analysis`, `deep_forensics` | Policy/contract review vs bulk log JSON vs max forensics |
|
|
75
|
+
| `content` | `default`, `pseo_bulk`, `brand_voice` | Natural prose vs batch pSEO schema vs premium copy |
|
|
76
|
+
| `summarization` | `default`, `mega_context` | Fast digest (Haiku) vs multi-hundred-k token compression |
|
|
77
|
+
| `vision` | `default`, `document_ocr` | General multimodal vs schema OCR from scans/PDFs |
|
|
78
|
+
| `open_weights` | `default`, `multilingual_code` | Llama 3.3 70B vs Qwen 2.5 72B (coding / multilingual) |
|
|
79
|
+
| `local` | `llama_cpp_gguf`, `transformersjs_default` | GGUF via llama.cpp vs Hugging Face Transformers.js on CPU |
|
|
80
|
+
|
|
81
|
+
Bundled **shortcuts** that pin a choice include `cheapest`, `absolute-floor`, `grok-fast`, `deepseek-pro`, `qwen`, `cheap-json`, `audit`, `pseo`, `ocr`, and FuncX-style `weak` / `normal` / `strong` / `ultra` (see [Profile matrix](#profile-matrix-funcx-contract)).
|
|
82
|
+
|
|
83
|
+
Registry fields on each choice—`runtime` (`reasoningEffort`, `toolPolicy`, `outputMode`, `instructionTier`, `backend`), `modelStatus` (`verified` | `predicted`), `api` (vendor + OpenRouter slugs), and `reason`—document *why* that model backs the choice. **Pricing and capabilities** come from `models-catalog.json` at resolve time. Profile-level `requiredCapabilities` describes intent for future dynamic selection (Optimixer). Consumers use `resolveAIProfile` to materialize the merged result.
|
|
84
|
+
|
|
5
85
|
## Install
|
|
6
86
|
|
|
7
87
|
```bash
|
|
@@ -13,14 +93,30 @@ npm install @x12i/ai-profiles
|
|
|
13
93
|
| Export | Sync? | Purpose |
|
|
14
94
|
|--------|-------|---------|
|
|
15
95
|
| `listAIProfiles(options?)` | No (async) | Profile summaries: primary `profile` key, `aliases[]`, choices, defaults |
|
|
96
|
+
| `getAIRegistryStatistics(options?)` | No (async) | Counts: profiles, choices, providers, shortcuts, catalog models, linkage health |
|
|
97
|
+
| `formatRegistryStatistics(stats)` | Yes | Human-readable summary for logs / CLI |
|
|
98
|
+
| `loadModelsCatalog(options?)` | No (async) | Unified models catalog (`auto` / `remote` / `bundled`, same cache pattern as profiles) |
|
|
16
99
|
| `listAIShortcuts()` | Yes | Shortcut table: `{ key, profile, choice?, description? }[]` |
|
|
17
100
|
| `resolveAIProfile(input, options?)` | No (async) | Resolve input → model, pricing, runtime, canonical `profile` key |
|
|
18
101
|
| `resolveUseOpenRouter(explicit?)` | Yes | Resolve OpenRouter vs vendor-direct routing (option → env → default) |
|
|
19
102
|
| `buildOpenRouterModelId(provider, modelId)` | Yes | Build an OpenRouter slug (`openai/gpt-5.4`) from registry fields |
|
|
103
|
+
| `validateBundledProfileVendorCoverage(options?)` | Yes | Advisory report of leading-vendor coverage per profile |
|
|
104
|
+
| `isProfileVendorTrimReady(profile, allowedProviders)` | Yes | Whether auto-pick can satisfy a vendor filter |
|
|
105
|
+
| `listFaultyProfileChoices()` | Yes | Profile choices with faulty OpenRouter catalog linkage |
|
|
20
106
|
| `isKnownProfileOrShortcut(input)` | Yes (bundled only) | Whether input is a known primary key, shortcut, or alias |
|
|
21
107
|
| `AIProfilesError` | — | Typed errors (`UNKNOWN_PROFILE`, …) |
|
|
22
108
|
|
|
23
|
-
Registry JSON lives in
|
|
109
|
+
Registry JSON lives in three assets:
|
|
110
|
+
|
|
111
|
+
| File | Schema | Role |
|
|
112
|
+
|------|--------|------|
|
|
113
|
+
| `models-profiles.json` | `x12i.ai-profiles.model-registry.v3` | Profile intent, choices, `requiredCapabilities`, `modelStatus` — **no embedded pricing** |
|
|
114
|
+
| `models-catalog.json` | `x12i.ai-profiles.models-catalog` | Per-model pricing, context window, capabilities (OpenRouter-normalized) |
|
|
115
|
+
| `profiles-shortcuts.json` | `x12i.ai-profiles.shortcuts` | Named entry points |
|
|
116
|
+
|
|
117
|
+
**v3 contract:** `modelId` is **vendor-native** (e.g. `claude-sonnet-4-5`). OpenRouter slugs live under `choice.api.openRouterModelId`. `resolveAIProfile` merges catalog data into `resolved.pricing`, `resolved.limits`, and `resolved.capabilities`. Default choices use **verified** SKUs; forward-looking models remain as alternates with `modelStatus: predicted`.
|
|
118
|
+
|
|
119
|
+
Validate with `src/data/models-profiles.schema.json`. Maintainer scripts: `npm run migrate:profiles-v3`, `npm run registry:finish` (catalog supplement + verified defaults + linkage rebuild).
|
|
24
120
|
|
|
25
121
|
## Basic usage
|
|
26
122
|
|
|
@@ -39,11 +135,40 @@ console.log(model.profile); // canonical primary key: cheap
|
|
|
39
135
|
console.log(model.provider); // "openrouter" by default for remote profiles
|
|
40
136
|
console.log(model.modelId); // "google/gemini-2.5-flash-lite"
|
|
41
137
|
console.log(model.routing); // "openrouter" | "direct"
|
|
42
|
-
console.log(model.pricing);
|
|
138
|
+
console.log(model.pricing); // from models-catalog.json
|
|
139
|
+
console.log(model.modelStatus); // verified | predicted
|
|
140
|
+
console.log(model.requiredCapabilities); // profile intent
|
|
141
|
+
console.log(model.capabilities); // contextWindow, supportsTools, …
|
|
43
142
|
console.log(model.instructionTier);
|
|
44
143
|
console.log(model.backend);
|
|
45
144
|
```
|
|
46
145
|
|
|
146
|
+
## Registry statistics
|
|
147
|
+
|
|
148
|
+
Use **`getAIRegistryStatistics()`** for discovery dashboards, CI summaries, and operator tooling. It loads profiles and the models catalog with the same `source` / `refresh` options as `resolveAIProfile`, then returns structured counts.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import {
|
|
152
|
+
formatRegistryStatistics,
|
|
153
|
+
getAIRegistryStatistics,
|
|
154
|
+
} from "@x12i/ai-profiles";
|
|
155
|
+
|
|
156
|
+
const { statistics, warnings } = await getAIRegistryStatistics({
|
|
157
|
+
source: "bundled", // or "auto" | "remote"
|
|
158
|
+
includeCatalog: true,
|
|
159
|
+
includeLinkage: true,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
console.log(statistics.profiles.total);
|
|
163
|
+
console.log(statistics.profiles.providers);
|
|
164
|
+
console.log(statistics.catalog?.totalModels);
|
|
165
|
+
console.log(statistics.linkage?.faulty);
|
|
166
|
+
|
|
167
|
+
console.log(formatRegistryStatistics(statistics));
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
CLI (after build): `npm run stats` (text) or `npm run stats:json`.
|
|
171
|
+
|
|
47
172
|
## Naming: many names, one profile
|
|
48
173
|
|
|
49
174
|
Each **profile** has exactly one **primary key** (e.g. `balanced`, `cheap`) and **many aliases** — alternate strings that mean the same profile. Data is in `models-profiles.json` under each profile’s `aliases` array.
|
|
@@ -249,15 +374,25 @@ const modelConfig = {
|
|
|
249
374
|
|
|
250
375
|
## Profile matrix (FuncX contract)
|
|
251
376
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
|
255
|
-
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
259
|
-
| `
|
|
260
|
-
| `
|
|
377
|
+
Default choice per profile (vendor-native ids in registry; OpenRouter slugs when `useOpenRouter` is true):
|
|
378
|
+
|
|
379
|
+
| Profile | Inference | Instruction tier | Default choice | Example model |
|
|
380
|
+
|---------|-----------|------------------|----------------|---------------|
|
|
381
|
+
| `cheap` | Remote | **`default`** | `default` | `gemini-2.5-flash-lite` |
|
|
382
|
+
| `fast` | Remote | **`default`** | `default` | `gpt-5.4-mini` |
|
|
383
|
+
| `balanced` | Remote | **`default`** | `default` | `gpt-5.4` |
|
|
384
|
+
| `deep` | Remote | **`reasoning`** | `default` | `claude-sonnet-4.6` |
|
|
385
|
+
| `pro` | Remote | **`default`** | `default` | `gpt-5.5` |
|
|
386
|
+
| `json` | Remote | **`default`** | `default` | `gpt-5.4-mini` (schema output) |
|
|
387
|
+
| `agentic` | Remote | **`default`** | `default` | `claude-sonnet-4.6` |
|
|
388
|
+
| `research` | Remote | **`reasoning`** | `default` | `claude-sonnet-4.6` |
|
|
389
|
+
| `cyber` | Remote | **`reasoning`** | `default` | `claude-sonnet-4.6` |
|
|
390
|
+
| `content` | Remote | **`default`** | `default` | `claude-sonnet-4.6` |
|
|
391
|
+
| `summarization` | Remote | **`default`** | `default` | `claude-haiku-4.5` |
|
|
392
|
+
| `vision` | Remote | **`default`** | `default` | `gpt-5.4` |
|
|
393
|
+
| `open_weights` | Remote | **`default`** | `default` | `meta-llama/Llama-3.3-70b-instruct` |
|
|
394
|
+
| `local` | CPU | **`cpu`** | `llama_cpp_gguf` | `funcx:llama-cpp:gguf` |
|
|
395
|
+
| `local` | CPU | **`cpu`** | `transformersjs_default` | `Xenova/distilbart-cnn-6-6` |
|
|
261
396
|
|
|
262
397
|
FuncX mode shortcuts (bundled): `weak` → local, `normal` → balanced, `strong` → pro, `ultra` → deep, `default` → balanced.
|
|
263
398
|
|
|
@@ -271,22 +406,57 @@ Remote profile choices in `models-profiles.json` use vendor-native `modelId` val
|
|
|
271
406
|
- `modelId`: full slug such as `openai/gpt-5.4`
|
|
272
407
|
- `routing`: `"openrouter"`
|
|
273
408
|
|
|
274
|
-
Pass `useOpenRouter: false` (or `USE_OPENROUTER=0`) to get vendor-native `provider` + `modelId` for direct API calls.
|
|
409
|
+
Pass `useOpenRouter: false` (or `USE_OPENROUTER=0`) to get vendor-native `provider` + `modelId` for direct API calls.
|
|
410
|
+
|
|
411
|
+
Every resolved profile includes **`invocation`** with both call shapes:
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
resolved.invocation.direct // { provider: "openai", modelId: "gpt-5.4" }
|
|
415
|
+
resolved.invocation.openrouter // { provider: "openrouter", modelId: "openai/gpt-5.4" } | null
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Top-level `provider` / `modelId` / `routing` remain the **active** path selected by `useOpenRouter`.
|
|
419
|
+
|
|
420
|
+
### Vendor trimming
|
|
421
|
+
|
|
422
|
+
Restrict resolution to specific vendors (auto-picks within the profile when the default is outside the set). **Leading vendors** (`openai`, `anthropic`, `google`, `deepseek`, `xai`) are **preferred** during auto-pick, not forced — explicit `choice` still honors `together` and other providers.
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
await resolveAIProfile("balanced", {
|
|
426
|
+
allowedProviders: ["google", "openai"],
|
|
427
|
+
preferLeadingVendors: true, // default
|
|
428
|
+
});
|
|
429
|
+
```
|
|
275
430
|
|
|
276
431
|
### Keeping models aligned over time
|
|
277
432
|
|
|
433
|
+
Bundled linkage lives in `src/data/models-registry.json`, built from `models-profiles.json` and `src/data/models-catalog.json`.
|
|
434
|
+
|
|
435
|
+
Each catalog model has **two explicit call shapes** (never `provider: "openrouter"` with `modelId: "qwen/..."` as the only view):
|
|
436
|
+
|
|
437
|
+
- `direct` — vendor-native (`{ provider: "google", modelId: "gemini-3.5-flash" }`)
|
|
438
|
+
- `openrouter` — transport (`{ provider: "openrouter", modelId: "google/gemini-3.5-flash" }`)
|
|
439
|
+
|
|
440
|
+
Legacy open-assets files named `openrouter-models-catalog.json` are normalized into this shape on `catalog:pull`.
|
|
441
|
+
|
|
278
442
|
When OpenRouter deprecates or renames a model slug:
|
|
279
443
|
|
|
280
444
|
1. Update `src/data/models-profiles.json` to a current vendor-native id (preferred).
|
|
281
|
-
2. Refresh
|
|
445
|
+
2. Refresh `src/data/models-catalog.json` via `npm run catalog:pull` (fetched from open-assets, normalized locally).
|
|
282
446
|
3. Run alignment checks before publishing:
|
|
283
447
|
|
|
284
448
|
```bash
|
|
285
|
-
npm run
|
|
286
|
-
npm run
|
|
449
|
+
npm run registry:finish # merge profile catalog supplement, verified defaults, rebuild linkage
|
|
450
|
+
npm run catalog:sync # pull catalog, rebuild models-registry.json, verify
|
|
451
|
+
npm run catalog:pull # fetch open-assets catalog into src/data/
|
|
452
|
+
npm run catalog:build # rebuild models-registry.json + reports/fault-report.json
|
|
453
|
+
npm run verify:openrouter # alias for catalog:verify (fails on faulty OR linkage only)
|
|
454
|
+
npm run verify:openrouter:live # legacy script vs live catalog
|
|
287
455
|
```
|
|
288
456
|
|
|
289
|
-
|
|
457
|
+
With the seed catalog (<50 models), choices not yet listed are marked `catalog-miss` in the registry (warnings in `reports/fault-report.json`, not CI failures). A full catalog promotes true `faulty` rows for slug mismatches.
|
|
458
|
+
|
|
459
|
+
Unit tests in `test/openrouterAlignment.test.ts`, `test/modelIdentity.test.ts`, and `test/resolveModelRouting.test.ts` guard resolution rules offline.
|
|
290
460
|
|
|
291
461
|
Vendor-direct-only choices (not routed via OpenRouter) can opt out:
|
|
292
462
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ModelsCatalogJson } from "./types.js";
|
|
2
|
+
export type OpenRouterApiModel = {
|
|
3
|
+
id: string;
|
|
4
|
+
canonical_slug?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
created?: number;
|
|
7
|
+
description?: string;
|
|
8
|
+
context_length?: number;
|
|
9
|
+
architecture?: {
|
|
10
|
+
modality?: string;
|
|
11
|
+
input_modalities?: string[];
|
|
12
|
+
output_modalities?: string[];
|
|
13
|
+
};
|
|
14
|
+
pricing?: Record<string, string | null | undefined>;
|
|
15
|
+
top_provider?: {
|
|
16
|
+
max_completion_tokens?: number;
|
|
17
|
+
};
|
|
18
|
+
supported_parameters?: string[];
|
|
19
|
+
knowledge_cutoff?: string | null;
|
|
20
|
+
expiration_date?: string | null;
|
|
21
|
+
};
|
|
22
|
+
export type OpenRouterApiModelsResponse = {
|
|
23
|
+
data: OpenRouterApiModel[];
|
|
24
|
+
};
|
|
25
|
+
/** Build the legacy OpenRouter-only catalog document (fed into {@link normalizeModelsCatalog}). */
|
|
26
|
+
export declare function buildLegacyCatalogFromOpenRouterApi(response: OpenRouterApiModelsResponse): Record<string, unknown>;
|
|
27
|
+
/** Fetch response → unified {@link ModelsCatalogJson}. */
|
|
28
|
+
export declare function buildModelsCatalogFromOpenRouterApi(response: OpenRouterApiModelsResponse): ModelsCatalogJson;
|
|
29
|
+
//# sourceMappingURL=buildCatalogFromOpenRouterApi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildCatalogFromOpenRouterApi.d.ts","sourceRoot":"","sources":["../src/buildCatalogFromOpenRouterApi.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiC,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE;QACb,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,kBAAkB,EAAE,CAAC;CAC5B,CAAC;AA8FF,mGAAmG;AACnG,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,2BAA2B,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAED,0DAA0D;AAC1D,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,2BAA2B,GACpC,iBAAiB,CAoBnB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { OPENROUTER_MODELS_API_URL } from "./constants.js";
|
|
2
|
+
import { MODELS_CATALOG_REMOTE_URL, normalizeModelsCatalog, } from "./modelsCatalog.js";
|
|
3
|
+
function perTokenToPerMillion(tokenPrice) {
|
|
4
|
+
if (tokenPrice === null || tokenPrice === undefined || tokenPrice === "") {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const perToken = Number(tokenPrice);
|
|
8
|
+
if (!Number.isFinite(perToken)) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return perToken * 1_000_000;
|
|
12
|
+
}
|
|
13
|
+
function inferFamily(modelId) {
|
|
14
|
+
const slash = modelId.indexOf("/");
|
|
15
|
+
if (slash <= 0) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
let prefix = modelId.slice(0, slash).toLowerCase();
|
|
19
|
+
if (prefix.startsWith("~")) {
|
|
20
|
+
prefix = prefix.slice(1);
|
|
21
|
+
}
|
|
22
|
+
return prefix || undefined;
|
|
23
|
+
}
|
|
24
|
+
function mapModalities(model) {
|
|
25
|
+
const input = model.architecture?.input_modalities ?? ["text"];
|
|
26
|
+
const output = model.architecture?.output_modalities ?? ["text"];
|
|
27
|
+
return {
|
|
28
|
+
input: input.map((m) => m.toLowerCase()),
|
|
29
|
+
output: output.map((m) => m.toLowerCase()),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function inferCapabilities(supported) {
|
|
33
|
+
if (!supported?.length) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const caps = {};
|
|
37
|
+
if (supported.some((p) => p === "reasoning" || p === "include_reasoning")) {
|
|
38
|
+
caps.reasoning = true;
|
|
39
|
+
}
|
|
40
|
+
if (supported.some((p) => p === "tools" || p === "tool_choice")) {
|
|
41
|
+
caps.toolCalling = true;
|
|
42
|
+
}
|
|
43
|
+
if (supported.some((p) => p === "structured_outputs")) {
|
|
44
|
+
caps.structuredOutputs = true;
|
|
45
|
+
}
|
|
46
|
+
return Object.keys(caps).length > 0 ? caps : undefined;
|
|
47
|
+
}
|
|
48
|
+
function mapLegacyEntry(model) {
|
|
49
|
+
const pricing = model.pricing ?? {};
|
|
50
|
+
return {
|
|
51
|
+
modelId: model.id,
|
|
52
|
+
canonicalSlug: model.canonical_slug,
|
|
53
|
+
displayName: model.name,
|
|
54
|
+
family: inferFamily(model.id),
|
|
55
|
+
status: model.expiration_date ? "deprecated" : "active",
|
|
56
|
+
modalities: mapModalities(model),
|
|
57
|
+
contextWindow: model.context_length,
|
|
58
|
+
maxCompletionTokens: model.top_provider?.max_completion_tokens,
|
|
59
|
+
knowledgeCutoff: model.knowledge_cutoff ?? undefined,
|
|
60
|
+
pricing: {
|
|
61
|
+
input: perTokenToPerMillion(pricing.prompt),
|
|
62
|
+
output: perTokenToPerMillion(pricing.completion),
|
|
63
|
+
cachedInput: perTokenToPerMillion(pricing.input_cache_read),
|
|
64
|
+
cacheWrite: perTokenToPerMillion(pricing.input_cache_write),
|
|
65
|
+
imageInput: perTokenToPerMillion(pricing.image),
|
|
66
|
+
audioInput: perTokenToPerMillion(pricing.audio),
|
|
67
|
+
internalReasoning: perTokenToPerMillion(pricing.internal_reasoning),
|
|
68
|
+
webSearchPerRequest: perTokenToPerMillion(pricing.web_search),
|
|
69
|
+
},
|
|
70
|
+
capabilities: inferCapabilities(model.supported_parameters),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Build the legacy OpenRouter-only catalog document (fed into {@link normalizeModelsCatalog}). */
|
|
74
|
+
export function buildLegacyCatalogFromOpenRouterApi(response) {
|
|
75
|
+
const models = (response.data ?? []).filter((m) => m?.id?.trim());
|
|
76
|
+
return {
|
|
77
|
+
schema: "x12i.openrouter-models.catalog",
|
|
78
|
+
version: "1.0.0",
|
|
79
|
+
generatedAt: new Date().toISOString(),
|
|
80
|
+
source: {
|
|
81
|
+
catalogUrl: OPENROUTER_MODELS_API_URL,
|
|
82
|
+
pricingUrl: "https://openrouter.ai/pricing",
|
|
83
|
+
verifiedAt: new Date().toISOString().slice(0, 10),
|
|
84
|
+
normalization: "OpenRouter API pricing fields are per-token decimal strings. Values are normalized to USD per 1M tokens.",
|
|
85
|
+
},
|
|
86
|
+
notes: [
|
|
87
|
+
"Generated from https://openrouter.ai/api/v1/models.",
|
|
88
|
+
"Upload the normalized models-catalog.json to open-assets for remote refresh.",
|
|
89
|
+
],
|
|
90
|
+
models: models.map(mapLegacyEntry),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Fetch response → unified {@link ModelsCatalogJson}. */
|
|
94
|
+
export function buildModelsCatalogFromOpenRouterApi(response) {
|
|
95
|
+
const legacy = buildLegacyCatalogFromOpenRouterApi(response);
|
|
96
|
+
const catalog = normalizeModelsCatalog(legacy);
|
|
97
|
+
return {
|
|
98
|
+
...catalog,
|
|
99
|
+
sources: {
|
|
100
|
+
openrouter: {
|
|
101
|
+
catalogUrl: OPENROUTER_MODELS_API_URL,
|
|
102
|
+
pricingUrl: "https://openrouter.ai/pricing",
|
|
103
|
+
verifiedAt: new Date().toISOString().slice(0, 10),
|
|
104
|
+
normalization: "OpenRouter API pricing fields are per-token decimal strings. Values are normalized to USD per 1M tokens.",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
notes: [
|
|
108
|
+
...(catalog.notes ?? []),
|
|
109
|
+
`Remote asset URL: ${MODELS_CATALOG_REMOTE_URL}`,
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=buildCatalogFromOpenRouterApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildCatalogFromOpenRouterApi.js","sourceRoot":"","sources":["../src/buildCatalogFromOpenRouterApi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ModelRegistryEntry, ModelsProfilesJson, ModelsRegistryJson, ModelsCatalogJson } from "./types.js";
|
|
2
|
+
import { validateProfileVendorCoverage } from "./validateProfileVendorCoverage.js";
|
|
3
|
+
export interface FaultyProfileChoice {
|
|
4
|
+
profile: string;
|
|
5
|
+
choice: string;
|
|
6
|
+
registryKey: string;
|
|
7
|
+
invocation: ModelRegistryEntry["invocation"];
|
|
8
|
+
candidates: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface CatalogMissChoice {
|
|
11
|
+
profile: string;
|
|
12
|
+
choice: string;
|
|
13
|
+
registryKey: string;
|
|
14
|
+
invocation: ModelRegistryEntry["invocation"];
|
|
15
|
+
candidates: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface ModelsRegistryBuildResult {
|
|
18
|
+
registry: ModelsRegistryJson;
|
|
19
|
+
faulty: FaultyProfileChoice[];
|
|
20
|
+
catalogMiss: CatalogMissChoice[];
|
|
21
|
+
vendorWarnings: ReturnType<typeof validateProfileVendorCoverage>;
|
|
22
|
+
}
|
|
23
|
+
export declare function buildModelsRegistry(profiles: ModelsProfilesJson, catalog: ModelsCatalogJson | unknown): ModelsRegistryBuildResult;
|
|
24
|
+
export declare function listFaultyFromRegistry(registry: ModelsRegistryJson): FaultyProfileChoice[];
|
|
25
|
+
//# sourceMappingURL=buildModelsRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildModelsRegistry.d.ts","sourceRoot":"","sources":["../src/buildModelsRegistry.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,cAAc,EAAE,UAAU,CAAC,OAAO,6BAA6B,CAAC,CAAC;CAClE;AAgBD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,iBAAiB,GAAG,OAAO,GACnC,yBAAyB,CA6J3B;AAmBD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,kBAAkB,GAC3B,mBAAmB,EAAE,CAoBvB"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { buildModelsCatalogIndex, normalizeModelsCatalog } from "./modelsCatalog.js";
|
|
2
|
+
import { buildRegistryKey, matchOpenRouterCatalog, shouldCheckOpenRouterAlignment, } from "./modelIdentity.js";
|
|
3
|
+
import { buildModelInvocationFromChoice, getChoiceVendorModelId, } from "./profileRegistryV2.js";
|
|
4
|
+
import { validateProfileVendorCoverage } from "./validateProfileVendorCoverage.js";
|
|
5
|
+
const SEED_CATALOG_MODEL_THRESHOLD = 50;
|
|
6
|
+
function isSeedCatalog(catalog) {
|
|
7
|
+
return catalog.models.length < SEED_CATALOG_MODEL_THRESHOLD;
|
|
8
|
+
}
|
|
9
|
+
function countChoices(profiles) {
|
|
10
|
+
let count = 0;
|
|
11
|
+
for (const profile of Object.values(profiles.profiles)) {
|
|
12
|
+
count += Object.keys(profile.choices).length;
|
|
13
|
+
}
|
|
14
|
+
return count;
|
|
15
|
+
}
|
|
16
|
+
export function buildModelsRegistry(profiles, catalog) {
|
|
17
|
+
const normalizedCatalog = normalizeModelsCatalog(catalog);
|
|
18
|
+
const catalogIndex = buildModelsCatalogIndex(normalizedCatalog);
|
|
19
|
+
const models = {};
|
|
20
|
+
const faulty = [];
|
|
21
|
+
const catalogMiss = [];
|
|
22
|
+
const seedCatalog = isSeedCatalog(normalizedCatalog);
|
|
23
|
+
const referencedOrIds = new Set();
|
|
24
|
+
for (const [profileKey, profile] of Object.entries(profiles.profiles)) {
|
|
25
|
+
for (const [choiceKey, choice] of Object.entries(profile.choices)) {
|
|
26
|
+
const invocation = buildModelInvocationFromChoice(choice);
|
|
27
|
+
const registryKey = buildRegistryKey(choice.provider, getChoiceVendorModelId(choice));
|
|
28
|
+
const ref = { profile: profileKey, choice: choiceKey };
|
|
29
|
+
if (choice.provider === "local") {
|
|
30
|
+
const entry = {
|
|
31
|
+
registryKey,
|
|
32
|
+
kind: "local-only",
|
|
33
|
+
vendor: invocation.direct,
|
|
34
|
+
invocation,
|
|
35
|
+
linkage: { status: "skipped", exemptReason: "local" },
|
|
36
|
+
usage: { profileChoices: [ref] },
|
|
37
|
+
};
|
|
38
|
+
mergeRegistryEntry(models, entry);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const checkAlignment = shouldCheckOpenRouterAlignment(choice);
|
|
42
|
+
let linkage;
|
|
43
|
+
let kind = "linked";
|
|
44
|
+
if (!checkAlignment) {
|
|
45
|
+
linkage = {
|
|
46
|
+
status: "skipped",
|
|
47
|
+
exemptReason: choice.metadata?.openRouter === "vendor-direct"
|
|
48
|
+
? "vendor-direct"
|
|
49
|
+
: choice.metadata?.skipOpenRouterAlignment
|
|
50
|
+
? "skipOpenRouterAlignment"
|
|
51
|
+
: choice.runtime?.backend !== "openrouter"
|
|
52
|
+
? "non-openrouter-backend"
|
|
53
|
+
: "exempt",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const match = matchOpenRouterCatalog(choice.provider, choice.modelId, catalogIndex);
|
|
58
|
+
if (match.matched && match.openRouterModelId) {
|
|
59
|
+
referencedOrIds.add(match.openRouterModelId.toLowerCase());
|
|
60
|
+
linkage = {
|
|
61
|
+
status: "matched",
|
|
62
|
+
openRouterCatalogId: match.openRouterModelId,
|
|
63
|
+
canonicalSlug: match.canonicalSlug,
|
|
64
|
+
matchMethod: match.matchMethod,
|
|
65
|
+
};
|
|
66
|
+
invocation.openrouter = {
|
|
67
|
+
provider: "openrouter",
|
|
68
|
+
modelId: match.openRouterModelId,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
else if (seedCatalog) {
|
|
72
|
+
linkage = { status: "catalog-miss", exemptReason: "seed-catalog" };
|
|
73
|
+
catalogMiss.push({
|
|
74
|
+
profile: profileKey,
|
|
75
|
+
choice: choiceKey,
|
|
76
|
+
registryKey,
|
|
77
|
+
invocation,
|
|
78
|
+
candidates: match.candidates,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
kind = "faulty";
|
|
83
|
+
linkage = { status: "faulty" };
|
|
84
|
+
faulty.push({
|
|
85
|
+
profile: profileKey,
|
|
86
|
+
choice: choiceKey,
|
|
87
|
+
registryKey,
|
|
88
|
+
invocation,
|
|
89
|
+
candidates: match.candidates,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
mergeRegistryEntry(models, {
|
|
94
|
+
registryKey,
|
|
95
|
+
kind,
|
|
96
|
+
vendor: invocation.direct,
|
|
97
|
+
invocation,
|
|
98
|
+
linkage,
|
|
99
|
+
usage: { profileChoices: [ref] },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const catalogModel of normalizedCatalog.models) {
|
|
104
|
+
const modelId = catalogModel.openrouter?.modelId?.trim();
|
|
105
|
+
if (!modelId)
|
|
106
|
+
continue;
|
|
107
|
+
const normalizedId = modelId.toLowerCase();
|
|
108
|
+
if (referencedOrIds.has(normalizedId))
|
|
109
|
+
continue;
|
|
110
|
+
const orKey = `or:${normalizedId.replace(/\//g, ":")}`;
|
|
111
|
+
if (models[orKey])
|
|
112
|
+
continue;
|
|
113
|
+
const direct = catalogModel.direct;
|
|
114
|
+
models[orKey] = {
|
|
115
|
+
registryKey: orKey,
|
|
116
|
+
kind: "openrouter-only",
|
|
117
|
+
vendor: direct,
|
|
118
|
+
invocation: {
|
|
119
|
+
direct,
|
|
120
|
+
openrouter: {
|
|
121
|
+
provider: "openrouter",
|
|
122
|
+
modelId: catalogModel.openrouter.modelId,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
linkage: {
|
|
126
|
+
status: "matched",
|
|
127
|
+
openRouterCatalogId: modelId,
|
|
128
|
+
canonicalSlug: catalogModel.openrouter?.canonicalSlug,
|
|
129
|
+
},
|
|
130
|
+
usage: { profileChoices: [] },
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const registry = {
|
|
134
|
+
schema: "x12i.ai-profiles.models-registry",
|
|
135
|
+
version: profiles.version,
|
|
136
|
+
generatedAt: new Date().toISOString(),
|
|
137
|
+
inputs: {
|
|
138
|
+
openRouterCatalog: {
|
|
139
|
+
schema: normalizedCatalog.schema,
|
|
140
|
+
version: normalizedCatalog.version,
|
|
141
|
+
generatedAt: normalizedCatalog.generatedAt,
|
|
142
|
+
modelCount: normalizedCatalog.models.length,
|
|
143
|
+
},
|
|
144
|
+
modelsProfiles: {
|
|
145
|
+
version: profiles.version,
|
|
146
|
+
generatedAt: profiles.generatedAt,
|
|
147
|
+
choiceCount: countChoices(profiles),
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
models,
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
registry,
|
|
154
|
+
faulty,
|
|
155
|
+
catalogMiss,
|
|
156
|
+
vendorWarnings: validateProfileVendorCoverage(profiles),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function mergeRegistryEntry(models, entry) {
|
|
160
|
+
const existing = models[entry.registryKey];
|
|
161
|
+
if (!existing) {
|
|
162
|
+
models[entry.registryKey] = entry;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
existing.usage.profileChoices.push(...entry.usage.profileChoices);
|
|
166
|
+
if (entry.kind === "faulty") {
|
|
167
|
+
existing.kind = "faulty";
|
|
168
|
+
existing.linkage = entry.linkage;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export function listFaultyFromRegistry(registry) {
|
|
172
|
+
const faulty = [];
|
|
173
|
+
for (const entry of Object.values(registry.models)) {
|
|
174
|
+
if (entry.linkage.status !== "faulty")
|
|
175
|
+
continue;
|
|
176
|
+
for (const ref of entry.usage.profileChoices) {
|
|
177
|
+
faulty.push({
|
|
178
|
+
profile: ref.profile,
|
|
179
|
+
choice: ref.choice,
|
|
180
|
+
registryKey: entry.registryKey,
|
|
181
|
+
invocation: entry.invocation,
|
|
182
|
+
candidates: entry.invocation.openrouter
|
|
183
|
+
? [entry.invocation.openrouter.modelId]
|
|
184
|
+
: [],
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return faulty;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=buildModelsRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildModelsRegistry.js","sourceRoot":"","sources":["../src/buildModelsRegistry.ts"],"names":[],"mappings":"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"}
|