@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.
@@ -6,10 +6,15 @@
6
6
  import { DEFAULT_MODEL_ALIASES } from "../types/index.js";
7
7
  import { logger } from "../utils/logger.js";
8
8
  import { AIProviderName, OpenAIModels, AzureOpenAIModels, GoogleAIModels, AnthropicModels, BedrockModels, MistralModels, OllamaModels, } from "../constants/enums.js";
9
+ import { getAllManifestProviders, getManifestForProvider, } from "./manifestRegistry.js";
9
10
  /**
10
- * Comprehensive model registry
11
+ * Comprehensive model registry — hand-authored base. Every id defined here
12
+ * that the manifest doesn't also carry a priced entry for stays exactly as
13
+ * authored below; see buildManifestDerivedEntries()/MODEL_REGISTRY's
14
+ * docblock further down for how the two are merged and why (Task 9, model
15
+ * metadata consolidation plan).
11
16
  */
12
- export const MODEL_REGISTRY = {
17
+ const LEGACY_MODEL_REGISTRY = {
13
18
  // OpenAI Models
14
19
  [OpenAIModels.GPT_4O]: {
15
20
  id: OpenAIModels.GPT_4O,
@@ -2450,6 +2455,129 @@ export const MODEL_REGISTRY = {
2450
2455
  },
2451
2456
  // Note: Azure models like O3, O4-mini, GPT-4o share IDs with OpenAI and use the OpenAI registry entries
2452
2457
  };
2458
+ function deriveSpeed(id) {
2459
+ if (/mini|nano|haiku|flash|lite/i.test(id)) {
2460
+ return "fast";
2461
+ }
2462
+ if (/opus|^o1|^o3|-pro$/i.test(id)) {
2463
+ return "slow";
2464
+ }
2465
+ return "medium";
2466
+ }
2467
+ function deriveQuality(entry) {
2468
+ return entry.reasoning ? "high" : "medium";
2469
+ }
2470
+ /**
2471
+ * Builds the manifest-derived slice of MODEL_REGISTRY: every manifest model
2472
+ * that carries pricingPerMTok (ModelInfo.pricing is a required field, and
2473
+ * the manifest's pricingPerMTok is deliberately optional for un-priced
2474
+ * models like claude-sonnet-5 — see ProviderModelManifestEntry's docblock),
2475
+ * excluding each provider's synthetic "_default" entry.
2476
+ *
2477
+ * performance/useCases/category use entry.curated verbatim when present —
2478
+ * the ids that already had a hand-tuned LEGACY_MODEL_REGISTRY row before
2479
+ * this migration (5 Anthropic, 20 OpenAI) carry that exact triple forward
2480
+ * unchanged. Every other entry has no curated block and falls back to
2481
+ * mechanical derivation from the tracked capability flags.
2482
+ */
2483
+ function buildManifestDerivedEntries() {
2484
+ const entries = {};
2485
+ for (const provider of getAllManifestProviders()) {
2486
+ const manifest = getManifestForProvider(provider);
2487
+ if (!manifest) {
2488
+ continue;
2489
+ }
2490
+ for (const [id, entry] of Object.entries(manifest.models)) {
2491
+ if (id === "_default" || !entry.pricingPerMTok) {
2492
+ continue;
2493
+ }
2494
+ // Keep-first on cross-manifest id collisions (Azure/OpenAI share ids):
2495
+ // getAllManifestProviders() order is deterministic, and silently
2496
+ // overwriting an earlier provider's row would make "which provider owns
2497
+ // this id" depend on iteration order.
2498
+ if (entries[id]) {
2499
+ logger.debug(`[modelRegistry] Manifest id collision: "${id}" already derived from ${entries[id].provider}; keeping first, skipping ${provider}`);
2500
+ continue;
2501
+ }
2502
+ const reasoningScore = entry.reasoning ? 9 : 6;
2503
+ const legacyRow = LEGACY_MODEL_REGISTRY[id];
2504
+ entries[id] = {
2505
+ id,
2506
+ name: entry.displayName ?? id,
2507
+ provider: provider,
2508
+ description: entry.displayName ?? id,
2509
+ capabilities: {
2510
+ vision: entry.vision,
2511
+ functionCalling: entry.functionCalling,
2512
+ codeGeneration: true,
2513
+ reasoning: entry.reasoning ?? false,
2514
+ multimodal: entry.vision || entry.nativeAudio === true,
2515
+ streaming: true,
2516
+ jsonMode: entry.jsonMode ?? false,
2517
+ samplingParams: entry.samplingParams,
2518
+ },
2519
+ pricing: {
2520
+ inputCostPer1K: entry.pricingPerMTok.input / 1000,
2521
+ outputCostPer1K: entry.pricingPerMTok.output / 1000,
2522
+ currency: "USD",
2523
+ },
2524
+ performance: entry.curated?.performance ?? {
2525
+ speed: deriveSpeed(id),
2526
+ quality: deriveQuality(entry),
2527
+ accuracy: deriveQuality(entry),
2528
+ },
2529
+ limits: {
2530
+ maxContextTokens: entry.contextWindow,
2531
+ maxOutputTokens: entry.maxOutputTokens,
2532
+ // Rate limits are operational knowledge the manifests don't track;
2533
+ // carry the hand-authored value forward rather than dropping it.
2534
+ ...(legacyRow?.limits?.maxRequestsPerMinute !== undefined
2535
+ ? { maxRequestsPerMinute: legacyRow.limits.maxRequestsPerMinute }
2536
+ : {}),
2537
+ },
2538
+ useCases: entry.curated?.useCases ?? {
2539
+ coding: entry.functionCalling ? 8 : 5,
2540
+ creative: entry.vision ? 7 : 6,
2541
+ analysis: reasoningScore,
2542
+ conversation: 7,
2543
+ reasoning: reasoningScore,
2544
+ translation: 6,
2545
+ summarization: 7,
2546
+ },
2547
+ aliases: entry.aliases,
2548
+ // Deprecation and release dates are editorial facts the manifests do
2549
+ // not track — a manifest-priced model must not silently un-deprecate
2550
+ // a row the hand-authored registry explicitly flagged.
2551
+ deprecated: legacyRow?.deprecated ?? false,
2552
+ ...(legacyRow?.releaseDate !== undefined
2553
+ ? { releaseDate: legacyRow.releaseDate }
2554
+ : {}),
2555
+ isLocal: provider === "ollama" ||
2556
+ provider === "lm-studio" ||
2557
+ provider === "llamacpp",
2558
+ category: entry.curated?.category ??
2559
+ (entry.reasoning ? "reasoning" : "general"),
2560
+ };
2561
+ }
2562
+ }
2563
+ return entries;
2564
+ }
2565
+ /**
2566
+ * Public MODEL_REGISTRY: the manifest-derived entries merged over the
2567
+ * hand-authored LEGACY_MODEL_REGISTRY base. Spread order matters — manifest
2568
+ * entries are spread last, so a manifest-priced model wins outright
2569
+ * (whole-entry replacement, not a per-field merge) over a legacy row with
2570
+ * the same id. Any legacy id the manifest doesn't also price (no
2571
+ * pricingPerMTok, or no manifest entry at all — e.g. AnthropicModels.
2572
+ * CLAUDE_OPUS_5/CLAUDE_FABLE_5, which have no manifest counterpart yet)
2573
+ * passes through unchanged, which is what keeps resolveSamplingParams/
2574
+ * modelSupportsSamplingParams behaviour identical for those "non-manifest"
2575
+ * models: their capabilities.samplingParams (or absence of it) is untouched.
2576
+ */
2577
+ export const MODEL_REGISTRY = {
2578
+ ...LEGACY_MODEL_REGISTRY,
2579
+ ...buildManifestDerivedEntries(),
2580
+ };
2453
2581
  /**
2454
2582
  * Model aliases registry for quick resolution
2455
2583
  */
@@ -2644,14 +2772,21 @@ export function getModelsByProvider(provider) {
2644
2772
  return Object.values(MODEL_REGISTRY).filter((model) => model.provider === provider);
2645
2773
  }
2646
2774
  /**
2647
- * Get available providers
2775
+ * Get available providers.
2776
+ *
2777
+ * Deliberately does NOT derive from MODEL_REGISTRY membership: a registry
2778
+ * keyed only by "real, priced, named models" under-reports providers whose
2779
+ * manifest carries only a `_default` entry (the minimal-tier providers —
2780
+ * e.g. voyage, jina, stability — have no priced named model and so
2781
+ * contribute zero MODEL_REGISTRY rows). Reading getAllManifestProviders()
2782
+ * directly gives every provider NeuroLink actually knows a manifest for,
2783
+ * decoupled from MODEL_REGISTRY membership entirely. Manifest keys are
2784
+ * literally the AIProviderName enum's kebab-case values by construction
2785
+ * (see manifestRegistry.ts), so the cast is a same-value relabel, not a
2786
+ * narrowing assumption.
2648
2787
  */
2649
2788
  export function getAvailableProviders() {
2650
- const providers = new Set();
2651
- Object.values(MODEL_REGISTRY).forEach((model) => {
2652
- providers.add(model.provider);
2653
- });
2654
- return Array.from(providers);
2789
+ return getAllManifestProviders();
2655
2790
  }
2656
2791
  /**
2657
2792
  * Calculate estimated cost for a request
@@ -91,7 +91,7 @@ export declare class AmazonSageMakerProvider extends BaseProvider {
91
91
  provider: string;
92
92
  specificationVersion: "v2";
93
93
  endpointName: string;
94
- modelType: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
94
+ modelType: "huggingface" | "mistral" | "custom" | "llama" | "claude" | "jumpstart" | undefined;
95
95
  region: string;
96
96
  } | {
97
97
  capabilities: {
@@ -153,7 +153,7 @@ export declare class SageMakerLanguageModel implements SageMakerAsLanguageModel
153
153
  provider: string;
154
154
  specificationVersion: "v2";
155
155
  endpointName: string;
156
- modelType: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
156
+ modelType: "huggingface" | "mistral" | "custom" | "llama" | "claude" | "jumpstart" | undefined;
157
157
  region: string;
158
158
  };
159
159
  /**
@@ -200,7 +200,7 @@ export declare class SageMakerLanguageModel implements SageMakerAsLanguageModel
200
200
  provider: string;
201
201
  specificationVersion: "v2";
202
202
  endpointName: string;
203
- modelType: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
203
+ modelType: "huggingface" | "mistral" | "custom" | "llama" | "claude" | "jumpstart" | undefined;
204
204
  region: string;
205
205
  };
206
206
  }
@@ -1,3 +1,4 @@
1
+ import { getManifestForProvider, resolveManifestEntryExact, } from "../models/manifestRegistry.js";
1
2
  /**
2
3
  * Per-token pricing data (USD per token). Updated Feb 2026.
3
4
  * Sources:
@@ -728,6 +729,22 @@ function isVersionOnlySuffix(model, key) {
728
729
  }
729
730
  return VERSION_SUFFIX_RE.test(model.slice(key.length));
730
731
  }
732
+ /**
733
+ * Whether `model` matches a provider manifest by the manifest's own
734
+ * canonical id or by one of its declared aliases — deliberately excludes
735
+ * resolveManifestEntryExact's longest-prefix fallback. See the call site in
736
+ * findRates for why the prefix path is unsafe to use for pricing precedence.
737
+ */
738
+ function isManifestNamedMatch(provider, model) {
739
+ const manifest = getManifestForProvider(provider);
740
+ if (!manifest) {
741
+ return false;
742
+ }
743
+ if (Object.prototype.hasOwnProperty.call(manifest.models, model)) {
744
+ return true;
745
+ }
746
+ return Object.values(manifest.models).some((entry) => entry.aliases.includes(model));
747
+ }
731
748
  function findRates(provider, model,
732
749
  /**
733
750
  * Set to true when the rates came from a literal table key rather than a
@@ -778,6 +795,40 @@ matchKind) {
778
795
  const modelKey = stripped === "bedrock" || stripped === "amazonbedrock"
779
796
  ? model.replace(/^.*\banthropic\./, "")
780
797
  : model;
798
+ // Manifest-backed exact/alias match takes precedence over legacy PRICING
799
+ // (see Task 8 of the model metadata consolidation plan). Deliberately
800
+ // gated to a NAMED match only (manifest's own id or a declared alias) —
801
+ // never resolveManifestEntryExact's longest-prefix fallback. The
802
+ // manifest's model set is coarser-grained than PRICING's for several
803
+ // live ids today (e.g. openai's manifest names only "gpt-5" while
804
+ // PRICING.openai carries distinct exact entries for
805
+ // gpt-5.1/5.4/5.5/5.6-sol/terra/luna); letting the manifest's prefix
806
+ // match run first would silently steal a more specific PRICING entry's
807
+ // rate — confirmed: resolveManifestEntryExact("openai", "gpt-5.4")
808
+ // prefix-matches onto the "gpt-5" entry, $1.25/$10 instead of the
809
+ // correct $2.5/$15. Everything the manifest doesn't name exactly or
810
+ // alias — including a named entry with no pricingPerMTok, e.g.
811
+ // claude-sonnet-5 — falls straight through to PRICING's unchanged
812
+ // exact+prefix match below, which is exactly "legacy PRICING as the
813
+ // fallback for entries the manifest lacks."
814
+ if (isManifestNamedMatch(normalizedProvider, modelKey)) {
815
+ const manifestEntry = resolveManifestEntryExact(normalizedProvider, modelKey);
816
+ if (manifestEntry?.pricingPerMTok) {
817
+ if (matchKind) {
818
+ matchKind.exact = true;
819
+ }
820
+ return {
821
+ input: manifestEntry.pricingPerMTok.input / 1_000_000,
822
+ output: manifestEntry.pricingPerMTok.output / 1_000_000,
823
+ cacheRead: manifestEntry.pricingPerMTok.cacheRead !== undefined
824
+ ? manifestEntry.pricingPerMTok.cacheRead / 1_000_000
825
+ : undefined,
826
+ cacheCreation: manifestEntry.pricingPerMTok.cacheWrite !== undefined
827
+ ? manifestEntry.pricingPerMTok.cacheWrite / 1_000_000
828
+ : undefined,
829
+ };
830
+ }
831
+ }
781
832
  // Exact match
782
833
  if (providerPricing[modelKey]) {
783
834
  if (matchKind) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.26.2",
3
+ "version": "11.27.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {