@juspay/neurolink 11.26.2 → 11.27.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.
@@ -63,10 +63,24 @@ export declare function clearRuntimeOutputCeilings(): void;
63
63
  * 0.5 Runtime-discovered windows (registerRuntimeContextWindow) — real
64
64
  * per-model limits fetched from the serving infrastructure (LiteLLM
65
65
  * `/model/info`)
66
- * 1. Exact model match under provider in static registry
67
- * 2. Prefix match under provider in static registry
68
- * 3. Provider's _default in static registry
69
- * 4. Global DEFAULT_CONTEXT_WINDOW
66
+ * 1. Manifest exact/alias match ONLY (resolveManifestEntryStrict no prefix, see
67
+ * src/lib/models/manifestRegistry.ts) the emerging single source of
68
+ * truth for per-model metadata. Deliberately consulted for a REAL match
69
+ * only (never the manifest's synthesized/generic default): most provider
70
+ * manifests are still "minimal" stubs holding nothing but a provider-wide
71
+ * default (e.g. vertex.ts, together-ai.ts) and none of the per-model
72
+ * overrides — including Vertex's Claude "claude-" prefix catch-all below,
73
+ * which exists specifically to stop an unlisted Claude-on-Vertex model
74
+ * from inheriting Gemini's 1,048,576 default — that still live only in
75
+ * MODEL_CONTEXT_WINDOWS. Falling back to a manifest-wide default here
76
+ * would silently drop that coverage. When a manifest DOES carry a real
77
+ * entry for this model, it wins even if it disagrees with the legacy
78
+ * table (e.g. Mistral's manifest supersedes the coarser, older
79
+ * MODEL_CONTEXT_WINDOWS.mistral values).
80
+ * 2. Exact model match under provider in static registry
81
+ * 3. Prefix match under provider in static registry
82
+ * 4. Provider's _default in static registry
83
+ * 5. Global DEFAULT_CONTEXT_WINDOW
70
84
  */
71
85
  export declare function getContextWindowSize(provider: string, model?: string): number;
72
86
  /**
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { DynamicModelProvider } from "../core/dynamicModels.js";
14
14
  import { logger } from "../utils/logger.js";
15
+ import { resolveManifestEntryStrict } from "../models/manifestRegistry.js";
15
16
  /** Default context window when provider/model is unknown */
16
17
  export const DEFAULT_CONTEXT_WINDOW = 128_000;
17
18
  /** Maximum output reserve when maxTokens not specified */
@@ -488,10 +489,24 @@ export function clearRuntimeOutputCeilings() {
488
489
  * 0.5 Runtime-discovered windows (registerRuntimeContextWindow) — real
489
490
  * per-model limits fetched from the serving infrastructure (LiteLLM
490
491
  * `/model/info`)
491
- * 1. Exact model match under provider in static registry
492
- * 2. Prefix match under provider in static registry
493
- * 3. Provider's _default in static registry
494
- * 4. Global DEFAULT_CONTEXT_WINDOW
492
+ * 1. Manifest exact/alias match ONLY (resolveManifestEntryStrict no prefix, see
493
+ * src/lib/models/manifestRegistry.ts) the emerging single source of
494
+ * truth for per-model metadata. Deliberately consulted for a REAL match
495
+ * only (never the manifest's synthesized/generic default): most provider
496
+ * manifests are still "minimal" stubs holding nothing but a provider-wide
497
+ * default (e.g. vertex.ts, together-ai.ts) and none of the per-model
498
+ * overrides — including Vertex's Claude "claude-" prefix catch-all below,
499
+ * which exists specifically to stop an unlisted Claude-on-Vertex model
500
+ * from inheriting Gemini's 1,048,576 default — that still live only in
501
+ * MODEL_CONTEXT_WINDOWS. Falling back to a manifest-wide default here
502
+ * would silently drop that coverage. When a manifest DOES carry a real
503
+ * entry for this model, it wins even if it disagrees with the legacy
504
+ * table (e.g. Mistral's manifest supersedes the coarser, older
505
+ * MODEL_CONTEXT_WINDOWS.mistral values).
506
+ * 2. Exact model match under provider in static registry
507
+ * 3. Prefix match under provider in static registry
508
+ * 4. Provider's _default in static registry
509
+ * 5. Global DEFAULT_CONTEXT_WINDOW
495
510
  */
496
511
  export function getContextWindowSize(provider, model) {
497
512
  // Step 0: Check dynamic model registry first.
@@ -521,6 +536,22 @@ export function getContextWindowSize(provider, model) {
521
536
  // Static fallback chain — normalize aliases first so "lmstudio" / "llama.cpp" /
522
537
  // "nvidianim" find their canonical entries instead of falling back to default.
523
538
  const canonical = normalizeProviderForLookup(provider);
539
+ // Step 1: Manifest real-entry lookup (exact id, alias, or longest-prefix —
540
+ // see resolveManifestEntryStrict's docblock). Tries the alias-normalized
541
+ // provider key first, then the raw provider string, mirroring the
542
+ // `MODEL_CONTEXT_WINDOWS[canonical] ?? MODEL_CONTEXT_WINDOWS[provider]`
543
+ // double-lookup below — MANIFEST_REGISTRY is keyed by the same hyphenated
544
+ // AIProviderName forms (e.g. "together-ai") that normalizeProviderForLookup
545
+ // strips and PROVIDER_ALIAS_MAP doesn't cover, so callers passing the raw
546
+ // enum value still resolve. Only a genuine match short-circuits; a miss
547
+ // falls straight through to the untouched legacy cascade below.
548
+ if (model) {
549
+ const manifestEntry = resolveManifestEntryStrict(canonical, model) ??
550
+ resolveManifestEntryStrict(provider, model);
551
+ if (manifestEntry) {
552
+ return manifestEntry.contextWindow;
553
+ }
554
+ }
524
555
  const providerWindows = MODEL_CONTEXT_WINDOWS[canonical] ?? MODEL_CONTEXT_WINDOWS[provider];
525
556
  if (!providerWindows) {
526
557
  return DEFAULT_CONTEXT_WINDOW;
@@ -2,6 +2,15 @@
2
2
  * Central configuration constants for NeuroLink
3
3
  * Single source of truth for all default values
4
4
  */
5
+ import { anthropicManifest } from "../models/manifests/anthropic.js";
6
+ import { openaiManifest } from "../models/manifests/openai.js";
7
+ import { googleAiManifest } from "../models/manifests/google-ai.js";
8
+ import { vertexManifest } from "../models/manifests/vertex.js";
9
+ import { bedrockManifest } from "../models/manifests/bedrock.js";
10
+ import { azureManifest } from "../models/manifests/azure.js";
11
+ import { mistralManifest } from "../models/manifests/mistral.js";
12
+ import { ollamaManifest } from "../models/manifests/ollama.js";
13
+ import { litellmManifest } from "../models/manifests/litellm.js";
5
14
  // Image Generation Model Identifiers
6
15
  // Used to detect if a model is an image generation model (not text generation).
7
16
  // `isImageGenerationModel(name)` (below) does boundary-aware matching:
@@ -167,34 +176,83 @@ export const PROVIDER_CONFIG = {
167
176
  temperature: 0.4,
168
177
  },
169
178
  };
170
- // Provider-specific maxTokens limits
179
+ /**
180
+ * Per-model output-token overrides for a manifest, keyed by canonical model
181
+ * id. The manifest's synthetic "_default" entry is excluded — the
182
+ * provider-level `default` in PROVIDER_MAX_TOKENS already carries that role,
183
+ * and is left as the hand-authored value below rather than replaced with
184
+ * this data (a provider's `_default` entry, when present, is a minimal
185
+ * catch-all sized for an unlisted/gateway model id, not a considered
186
+ * provider-wide ceiling).
187
+ */
188
+ function maxTokensOverridesFrom(manifest) {
189
+ const overrides = {};
190
+ for (const [id, entry] of Object.entries(manifest.models)) {
191
+ if (id === "_default") {
192
+ continue;
193
+ }
194
+ overrides[id] = entry.maxOutputTokens;
195
+ }
196
+ return overrides;
197
+ }
198
+ // Provider-specific maxTokens limits. Each provider's `default` is the
199
+ // original hand-authored ceiling (unchanged); per-model keys are generated
200
+ // from that provider's manifest (src/lib/models/manifests/) so
201
+ // getSafeMaxTokens()'s existing per-model lookup (tokenLimits.ts) actually
202
+ // has data to find instead of always falling through to the coarse
203
+ // `default`. This resolves the historical disagreement between
204
+ // getSafeMaxTokens("anthropic", "claude-opus-4-6") (used to return the flat
205
+ // 64000) and resolveClaudeMaxTokens("claude-opus-4-6") (tokenLimits.ts's
206
+ // regex ladder, correctly 32000 for the Opus family) — the manifest's own
207
+ // per-model maxOutputTokens now wins for every listed model, on every
208
+ // provider below, not just Anthropic.
209
+ //
210
+ // Manifests are imported directly from src/lib/models/manifests/ rather
211
+ // than through manifestRegistry.ts's aggregator: manifestRegistry.ts
212
+ // imports PROVIDER_MAX_TOKENS from this file (to size the synthetic
213
+ // `_default` entry for manifests that don't declare their own), so
214
+ // importing the aggregator back here would form a constants.ts <->
215
+ // manifestRegistry.ts import cycle whose safety depends on which module a
216
+ // given entrypoint happens to reach first — an ES module TDZ hazard, not
217
+ // something a type-checker catches. The individual manifest files have no
218
+ // dependency on this module (only on the ProviderModelManifest type), so
219
+ // reaching past the aggregator avoids the cycle entirely.
171
220
  export const PROVIDER_MAX_TOKENS = {
172
221
  anthropic: {
173
222
  default: 64000,
223
+ ...maxTokensOverridesFrom(anthropicManifest),
174
224
  },
175
225
  openai: {
176
226
  default: 128000,
227
+ ...maxTokensOverridesFrom(openaiManifest),
177
228
  },
178
229
  "google-ai": {
179
230
  default: 64000,
231
+ ...maxTokensOverridesFrom(googleAiManifest),
180
232
  },
181
233
  vertex: {
182
234
  default: 64000,
235
+ ...maxTokensOverridesFrom(vertexManifest),
183
236
  },
184
237
  bedrock: {
185
238
  default: 64000,
239
+ ...maxTokensOverridesFrom(bedrockManifest),
186
240
  },
187
241
  azure: {
188
242
  default: 128000,
243
+ ...maxTokensOverridesFrom(azureManifest),
189
244
  },
190
245
  mistral: {
191
246
  default: 128000,
247
+ ...maxTokensOverridesFrom(mistralManifest),
192
248
  },
193
249
  ollama: {
194
250
  default: 64000,
251
+ ...maxTokensOverridesFrom(ollamaManifest),
195
252
  },
196
253
  litellm: {
197
254
  default: 128000,
255
+ ...maxTokensOverridesFrom(litellmManifest),
198
256
  },
199
257
  default: 64000,
200
258
  };
@@ -20,6 +20,9 @@ export declare enum AnthropicModel {
20
20
  CLAUDE_3_5_SONNET_V2 = "claude-3-5-sonnet-v2-20241022",
21
21
  CLAUDE_SONNET_4 = "claude-sonnet-4-20250514",
22
22
  CLAUDE_SONNET_4_6 = "claude-sonnet-4-6",
23
+ CLAUDE_OPUS_4_5 = "claude-opus-4-5-20251101",
24
+ CLAUDE_SONNET_4_5 = "claude-sonnet-4-5-20250929",
25
+ CLAUDE_HAIKU_4_5 = "claude-haiku-4-5-20251001",
23
26
  CLAUDE_3_OPUS = "claude-3-opus-20240229",
24
27
  CLAUDE_OPUS_4 = "claude-opus-4-20250514",
25
28
  CLAUDE_OPUS_4_6 = "claude-opus-4-6"
@@ -34,12 +37,6 @@ export declare enum AnthropicModel {
34
37
  * - api: Full API access to all models (based on API access)
35
38
  */
36
39
  export declare const MODEL_TIER_ACCESS: Record<ClaudeSubscriptionTier, string[]>;
37
- /**
38
- * Model metadata by model ID
39
- *
40
- * Comprehensive mapping of each Anthropic model's metadata,
41
- * including display names, context windows, vision support, and extended thinking.
42
- */
43
40
  export declare const MODEL_METADATA: Record<string, AnthropicModelMetadata>;
44
41
  /**
45
42
  * Default model for each subscription tier
@@ -5,6 +5,7 @@
5
5
  * model capabilities, and provides helper functions for tier-based access control.
6
6
  */
7
7
  import { ModelAccessError } from "../types/index.js";
8
+ import { anthropicManifest } from "./manifests/anthropic.js";
8
9
  // Re-export runtime value for convenience
9
10
  export { ModelAccessError };
10
11
  // ============================================================================
@@ -30,6 +31,12 @@ export var AnthropicModel;
30
31
  AnthropicModel["CLAUDE_SONNET_4"] = "claude-sonnet-4-20250514";
31
32
  // Claude Sonnet 4.6
32
33
  AnthropicModel["CLAUDE_SONNET_4_6"] = "claude-sonnet-4-6";
34
+ // Claude Opus 4.5
35
+ AnthropicModel["CLAUDE_OPUS_4_5"] = "claude-opus-4-5-20251101";
36
+ // Claude Sonnet 4.5
37
+ AnthropicModel["CLAUDE_SONNET_4_5"] = "claude-sonnet-4-5-20250929";
38
+ // Claude 4.5 Haiku
39
+ AnthropicModel["CLAUDE_HAIKU_4_5"] = "claude-haiku-4-5-20251001";
33
40
  // Claude 3 Opus (Legacy flagship)
34
41
  AnthropicModel["CLAUDE_3_OPUS"] = "claude-3-opus-20240229";
35
42
  // Claude Opus 4 (Latest flagship)
@@ -76,130 +83,91 @@ export const MODEL_TIER_ACCESS = {
76
83
  // MODEL METADATA
77
84
  // ============================================================================
78
85
  /**
79
- * Model metadata by model ID
80
- *
81
- * Comprehensive mapping of each Anthropic model's metadata,
82
- * including display names, context windows, vision support, and extended thinking.
86
+ * Model metadata by model ID, derived from the anthropic manifest
87
+ * (src/lib/models/manifests/anthropic.ts) so this catalog can no longer
88
+ * silently drift from the canonical source. Family/description fields —
89
+ * not tracked by the manifest are supplied by the small per-id lookups
90
+ * below, unchanged from their pre-migration values.
83
91
  */
84
- export const MODEL_METADATA = {
85
- // Claude 3 Haiku (Legacy)
86
- [AnthropicModel.CLAUDE_3_HAIKU]: {
87
- displayName: "Claude 3 Haiku",
88
- contextWindow: 200000,
89
- maxOutputTokens: 4096,
90
- supportsVision: true,
91
- supportsExtendedThinking: false,
92
- supportsToolUse: true,
93
- supportsStreaming: true,
94
- deprecated: true,
95
- family: "haiku",
96
- description: "Fast and efficient model for simple tasks",
97
- },
98
- // Claude 3.5 Haiku
99
- [AnthropicModel.CLAUDE_3_5_HAIKU]: {
100
- displayName: "Claude 3.5 Haiku",
101
- contextWindow: 200000,
102
- maxOutputTokens: 8192,
103
- supportsVision: false,
104
- supportsExtendedThinking: false,
105
- supportsToolUse: true,
106
- supportsStreaming: true,
107
- deprecated: false,
108
- family: "haiku",
109
- description: "Improved fast model with better performance",
110
- },
111
- // Claude 3.5 Sonnet
112
- [AnthropicModel.CLAUDE_3_5_SONNET]: {
113
- displayName: "Claude 3.5 Sonnet",
114
- contextWindow: 200000,
115
- maxOutputTokens: 8192,
116
- supportsVision: true,
117
- supportsExtendedThinking: false,
118
- supportsToolUse: true,
119
- supportsStreaming: true,
120
- deprecated: false,
121
- family: "sonnet",
122
- description: "Balanced model for most tasks",
123
- },
124
- // Claude 3.5 Sonnet V2
125
- [AnthropicModel.CLAUDE_3_5_SONNET_V2]: {
126
- displayName: "Claude 3.5 Sonnet V2",
127
- contextWindow: 200000,
128
- maxOutputTokens: 8192,
129
- supportsVision: true,
130
- supportsExtendedThinking: false,
131
- supportsToolUse: true,
132
- supportsStreaming: true,
133
- deprecated: false,
134
- family: "sonnet",
135
- description: "Updated Sonnet with improved capabilities",
136
- },
137
- // Claude Sonnet 4
138
- [AnthropicModel.CLAUDE_SONNET_4]: {
139
- displayName: "Claude Sonnet 4",
140
- contextWindow: 200000,
141
- maxOutputTokens: 64000,
142
- supportsVision: true,
143
- supportsExtendedThinking: true,
144
- supportsToolUse: true,
145
- supportsStreaming: true,
146
- deprecated: false,
147
- family: "sonnet",
148
- description: "Latest Sonnet with extended thinking support",
149
- },
150
- // Claude 3 Opus (Legacy)
151
- [AnthropicModel.CLAUDE_3_OPUS]: {
152
- displayName: "Claude 3 Opus",
153
- contextWindow: 200000,
154
- maxOutputTokens: 4096,
155
- supportsVision: true,
156
- supportsExtendedThinking: false,
157
- supportsToolUse: true,
158
- supportsStreaming: true,
159
- deprecated: true,
160
- family: "opus",
161
- description: "Legacy flagship model for complex tasks",
162
- },
163
- // Claude Opus 4
164
- [AnthropicModel.CLAUDE_OPUS_4]: {
165
- displayName: "Claude Opus 4",
166
- contextWindow: 200000,
167
- maxOutputTokens: 64000,
168
- supportsVision: true,
169
- supportsExtendedThinking: true,
170
- supportsToolUse: true,
171
- supportsStreaming: true,
172
- deprecated: false,
173
- family: "opus",
174
- description: "Latest flagship model with advanced reasoning",
175
- },
176
- // Claude Sonnet 4.6
177
- [AnthropicModel.CLAUDE_SONNET_4_6]: {
178
- displayName: "Claude Sonnet 4.6",
179
- contextWindow: 1000000,
180
- maxOutputTokens: 64000,
181
- supportsVision: true,
182
- supportsExtendedThinking: true,
183
- supportsToolUse: true,
184
- supportsStreaming: true,
185
- deprecated: false,
186
- family: "sonnet",
187
- description: "Claude 4.6 Sonnet with 1M context window",
188
- },
189
- // Claude Opus 4.6
190
- [AnthropicModel.CLAUDE_OPUS_4_6]: {
191
- displayName: "Claude Opus 4.6",
192
- contextWindow: 1000000,
193
- maxOutputTokens: 64000,
194
- supportsVision: true,
195
- supportsExtendedThinking: true,
196
- supportsToolUse: true,
197
- supportsStreaming: true,
198
- deprecated: false,
199
- family: "opus",
200
- description: "Claude 4.6 Opus flagship with 1M context window",
201
- },
92
+ const FAMILY_BY_MODEL = {
93
+ [AnthropicModel.CLAUDE_3_HAIKU]: "haiku",
94
+ [AnthropicModel.CLAUDE_3_5_HAIKU]: "haiku",
95
+ [AnthropicModel.CLAUDE_3_5_SONNET]: "sonnet",
96
+ [AnthropicModel.CLAUDE_3_5_SONNET_V2]: "sonnet",
97
+ [AnthropicModel.CLAUDE_SONNET_4]: "sonnet",
98
+ [AnthropicModel.CLAUDE_SONNET_4_6]: "sonnet",
99
+ [AnthropicModel.CLAUDE_3_OPUS]: "opus",
100
+ [AnthropicModel.CLAUDE_OPUS_4]: "opus",
101
+ [AnthropicModel.CLAUDE_OPUS_4_6]: "opus",
102
+ [AnthropicModel.CLAUDE_OPUS_4_5]: "opus",
103
+ [AnthropicModel.CLAUDE_SONNET_4_5]: "sonnet",
104
+ [AnthropicModel.CLAUDE_HAIKU_4_5]: "haiku",
202
105
  };
106
+ const DESCRIPTION_BY_MODEL = {
107
+ [AnthropicModel.CLAUDE_3_HAIKU]: "Fast and efficient model for simple tasks",
108
+ [AnthropicModel.CLAUDE_3_5_HAIKU]: "Improved fast model with better performance",
109
+ [AnthropicModel.CLAUDE_3_5_SONNET]: "Balanced model for most tasks",
110
+ [AnthropicModel.CLAUDE_3_5_SONNET_V2]: "Updated Sonnet with improved capabilities",
111
+ [AnthropicModel.CLAUDE_SONNET_4]: "Latest Sonnet with extended thinking support",
112
+ [AnthropicModel.CLAUDE_3_OPUS]: "Legacy flagship model for complex tasks",
113
+ [AnthropicModel.CLAUDE_OPUS_4]: "Latest flagship model with advanced reasoning",
114
+ [AnthropicModel.CLAUDE_SONNET_4_6]: "Claude 4.6 Sonnet with 1M context window",
115
+ [AnthropicModel.CLAUDE_OPUS_4_6]: "Claude 4.6 Opus flagship with 1M context window",
116
+ [AnthropicModel.CLAUDE_OPUS_4_5]: "Claude 4.5 Opus flagship model",
117
+ [AnthropicModel.CLAUDE_SONNET_4_5]: "Claude 4.5 Sonnet balanced model",
118
+ [AnthropicModel.CLAUDE_HAIKU_4_5]: "Claude 4.5 Haiku fast model",
119
+ };
120
+ const DEPRECATED_MODELS = new Set([
121
+ AnthropicModel.CLAUDE_3_HAIKU,
122
+ AnthropicModel.CLAUDE_3_OPUS,
123
+ ]);
124
+ /**
125
+ * CLAUDE_3_5_SONNET_V2's enum value ("claude-3-5-sonnet-v2-20241022") is a
126
+ * synthetic id minted only inside this file, pre-dating the manifest — it
127
+ * never was a real Anthropic wire id and has no row anywhere else in the
128
+ * codebase (pricing.ts, contextWindows.ts, VISION_CAPABILITIES all key off
129
+ * "claude-3-5-sonnet-20241022" for both v1 and v2). Route the manifest
130
+ * lookup to the real entry instead of inventing a duplicate manifest row
131
+ * for the same model; the enum member's name and exported value are left
132
+ * untouched.
133
+ */
134
+ const MANIFEST_ID_OVERRIDES = {
135
+ [AnthropicModel.CLAUDE_3_5_SONNET_V2]: AnthropicModel.CLAUDE_3_5_SONNET,
136
+ };
137
+ /**
138
+ * displayName override applied only where routing through
139
+ * MANIFEST_ID_OVERRIDES would otherwise silently rename a model the caller
140
+ * already knows by its pre-migration display name (avoids user-visible
141
+ * naming churn, same principle the manifest itself documents for its
142
+ * pre-existing MODEL_REGISTRY-derived ids).
143
+ */
144
+ const DISPLAY_NAME_OVERRIDES = {
145
+ [AnthropicModel.CLAUDE_3_5_SONNET_V2]: "Claude 3.5 Sonnet V2",
146
+ };
147
+ function metadataFromManifest(model) {
148
+ const manifestId = MANIFEST_ID_OVERRIDES[model] ?? model;
149
+ const entry = anthropicManifest.models[manifestId];
150
+ if (!entry) {
151
+ throw new Error(`metadataFromManifest: no manifest entry for "${model}" — add it to ` +
152
+ `src/lib/models/manifests/anthropic.ts before referencing it here`);
153
+ }
154
+ return {
155
+ displayName: DISPLAY_NAME_OVERRIDES[model] ?? entry.displayName ?? model,
156
+ contextWindow: entry.contextWindow,
157
+ maxOutputTokens: entry.maxOutputTokens,
158
+ supportsVision: entry.vision,
159
+ supportsExtendedThinking: entry.reasoning ?? false,
160
+ supportsToolUse: entry.functionCalling,
161
+ supportsStreaming: true,
162
+ deprecated: DEPRECATED_MODELS.has(model),
163
+ family: FAMILY_BY_MODEL[model] ?? "sonnet",
164
+ description: DESCRIPTION_BY_MODEL[model] ?? entry.displayName ?? model,
165
+ };
166
+ }
167
+ export const MODEL_METADATA = Object.fromEntries(Object.values(AnthropicModel).map((model) => [
168
+ model,
169
+ metadataFromManifest(model),
170
+ ]));
203
171
  // ============================================================================
204
172
  // DEFAULT MODELS BY TIER
205
173
  // ============================================================================
@@ -447,15 +415,21 @@ export function getLatestModelsByFamily() {
447
415
  };
448
416
  // Priority order for each family (latest first based on model version)
449
417
  const familyPriority = {
450
- haiku: [AnthropicModel.CLAUDE_3_5_HAIKU, AnthropicModel.CLAUDE_3_HAIKU],
418
+ haiku: [
419
+ AnthropicModel.CLAUDE_HAIKU_4_5,
420
+ AnthropicModel.CLAUDE_3_5_HAIKU,
421
+ AnthropicModel.CLAUDE_3_HAIKU,
422
+ ],
451
423
  sonnet: [
452
424
  AnthropicModel.CLAUDE_SONNET_4_6,
425
+ AnthropicModel.CLAUDE_SONNET_4_5,
453
426
  AnthropicModel.CLAUDE_SONNET_4,
454
427
  AnthropicModel.CLAUDE_3_5_SONNET_V2,
455
428
  AnthropicModel.CLAUDE_3_5_SONNET,
456
429
  ],
457
430
  opus: [
458
431
  AnthropicModel.CLAUDE_OPUS_4_6,
432
+ AnthropicModel.CLAUDE_OPUS_4_5,
459
433
  AnthropicModel.CLAUDE_OPUS_4,
460
434
  AnthropicModel.CLAUDE_3_OPUS,
461
435
  ],
@@ -21,6 +21,15 @@ export declare function getAllManifestProviders(): string[];
21
21
  * "llama3.2:latest" or OpenRouter's "openai/gpt-4o"), then undefined.
22
22
  * Family rules are applied on top of whichever entry matched.
23
23
  */
24
+ /**
25
+ * Like resolveManifestEntryExact but WITHOUT the longest-prefix fallback:
26
+ * exact canonical id or declared alias only. For consumers where a prefix
27
+ * hit can steal precedence from a more-specific legacy row — boolean
28
+ * capability checks above all (a manifest "gpt-4" prefix match must never
29
+ * shadow the legacy table's explicit "gpt-4-vision-preview" vision row).
30
+ * Family rules still apply to a real match.
31
+ */
32
+ export declare function resolveManifestEntryStrict(provider: string, model: string): ProviderModelManifestEntry | undefined;
24
33
  export declare function resolveManifestEntryExact(provider: string, model: string): ProviderModelManifestEntry | undefined;
25
34
  /**
26
35
  * Resolve a model against a provider's manifest, falling back to the
@@ -105,6 +105,29 @@ function applyFamilyRules(manifest, model, base) {
105
105
  * "llama3.2:latest" or OpenRouter's "openai/gpt-4o"), then undefined.
106
106
  * Family rules are applied on top of whichever entry matched.
107
107
  */
108
+ /**
109
+ * Like resolveManifestEntryExact but WITHOUT the longest-prefix fallback:
110
+ * exact canonical id or declared alias only. For consumers where a prefix
111
+ * hit can steal precedence from a more-specific legacy row — boolean
112
+ * capability checks above all (a manifest "gpt-4" prefix match must never
113
+ * shadow the legacy table's explicit "gpt-4-vision-preview" vision row).
114
+ * Family rules still apply to a real match.
115
+ */
116
+ export function resolveManifestEntryStrict(provider, model) {
117
+ const manifest = MANIFEST_REGISTRY[provider];
118
+ if (!manifest) {
119
+ return undefined;
120
+ }
121
+ const exact = manifest.models[model];
122
+ if (exact) {
123
+ return applyFamilyRules(manifest, model, exact);
124
+ }
125
+ const aliasMatch = Object.entries(manifest.models).find(([canonicalId, entry]) => canonicalId !== "_default" && entry.aliases.includes(model));
126
+ if (aliasMatch) {
127
+ return applyFamilyRules(manifest, model, aliasMatch[1]);
128
+ }
129
+ return undefined;
130
+ }
108
131
  export function resolveManifestEntryExact(provider, model) {
109
132
  const manifest = MANIFEST_REGISTRY[provider];
110
133
  if (!manifest) {
@@ -28,9 +28,11 @@ export const anthropicManifest = {
28
28
  displayName: "Claude Sonnet 5",
29
29
  contextWindow: 1_000_000,
30
30
  maxOutputTokens: 64_000,
31
- // No pricingPerMTok: genuinely absent from PRICING.anthropic today.
32
- // Do not invent a rate — hasPricing()/findRates() (pricing.ts) must
33
- // keep reporting this model as unpriced.
31
+ // No pricingPerMTok DELIBERATELY: PRICING.anthropic carries real
32
+ // rates for this id, and findRates() is manifest-first with a legacy
33
+ // fallback omitting the rate here defers to that table instead of
34
+ // duplicating it, and keeps this entry out of the manifest-derived
35
+ // MODEL_REGISTRY rows (which only admit priced entries).
34
36
  vision: true,
35
37
  functionCalling: true,
36
38
  reasoning: true,
@@ -77,7 +77,12 @@ export const azureManifest = {
77
77
  contextWindow: 200000,
78
78
  maxOutputTokens: 32768,
79
79
  pricingPerMTok: { input: 1.25, output: 10 },
80
- vision: true,
80
+ // Azure publishes no such model (the registry row is deprecated for
81
+ // exactly that reason, modelRegistry.ts ~2550), and vision was
82
+ // deliberately removed from VISION_CAPABILITIES for it — this entry
83
+ // was generated from the registry BEFORE that fix and must not
84
+ // re-advertise a capability an undeployable id cannot have.
85
+ vision: false,
81
86
  functionCalling: true,
82
87
  reasoning: true,
83
88
  jsonMode: true,
@@ -6,7 +6,16 @@
6
6
  import { AIProviderName } from "../constants/enums.js";
7
7
  import type { JsonValue, ModelCapabilities, ModelInfo } from "../types/index.js";
8
8
  /**
9
- * Comprehensive model registry
9
+ * Public MODEL_REGISTRY: the manifest-derived entries merged over the
10
+ * hand-authored LEGACY_MODEL_REGISTRY base. Spread order matters — manifest
11
+ * entries are spread last, so a manifest-priced model wins outright
12
+ * (whole-entry replacement, not a per-field merge) over a legacy row with
13
+ * the same id. Any legacy id the manifest doesn't also price (no
14
+ * pricingPerMTok, or no manifest entry at all — e.g. AnthropicModels.
15
+ * CLAUDE_OPUS_5/CLAUDE_FABLE_5, which have no manifest counterpart yet)
16
+ * passes through unchanged, which is what keeps resolveSamplingParams/
17
+ * modelSupportsSamplingParams behaviour identical for those "non-manifest"
18
+ * models: their capabilities.samplingParams (or absence of it) is untouched.
10
19
  */
11
20
  export declare const MODEL_REGISTRY: Record<string, ModelInfo>;
12
21
  /**
@@ -61,7 +70,18 @@ export declare function resolveSamplingParams(provider: string, model: string |
61
70
  */
62
71
  export declare function getModelsByProvider(provider: AIProviderName): ModelInfo[];
63
72
  /**
64
- * Get available providers
73
+ * Get available providers.
74
+ *
75
+ * Deliberately does NOT derive from MODEL_REGISTRY membership: a registry
76
+ * keyed only by "real, priced, named models" under-reports providers whose
77
+ * manifest carries only a `_default` entry (the minimal-tier providers —
78
+ * e.g. voyage, jina, stability — have no priced named model and so
79
+ * contribute zero MODEL_REGISTRY rows). Reading getAllManifestProviders()
80
+ * directly gives every provider NeuroLink actually knows a manifest for,
81
+ * decoupled from MODEL_REGISTRY membership entirely. Manifest keys are
82
+ * literally the AIProviderName enum's kebab-case values by construction
83
+ * (see manifestRegistry.ts), so the cast is a same-value relabel, not a
84
+ * narrowing assumption.
65
85
  */
66
86
  export declare function getAvailableProviders(): AIProviderName[];
67
87
  /**