UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

304 lines (303 loc) • 13.5 kB
import { PROVIDER_MAX_TOKENS } from "../core/constants.js"; import { getCatalogJsonEntries, getDecideCatalogEntries, } from "../providers/catalog/loader.js"; import { anthropicManifest } from "./manifests/anthropic.js"; import { openaiManifest } from "./manifests/openai.js"; import { azureManifest } from "./manifests/azure.js"; import { bedrockManifest } from "./manifests/bedrock.js"; import { ollamaManifest } from "./manifests/ollama.js"; import { mistralManifest } from "./manifests/mistral.js"; import { googleAiManifest } from "./manifests/google-ai.js"; import { openaiCompatibleManifest } from "./manifests/openai-compatible.js"; import { openrouterManifest } from "./manifests/openrouter.js"; import { vertexManifest } from "./manifests/vertex.js"; import { huggingfaceManifest } from "./manifests/huggingface.js"; import { litellmManifest } from "./manifests/litellm.js"; import { sagemakerManifest } from "./manifests/sagemaker.js"; import { deepseekManifest } from "./manifests/deepseek.js"; import { nvidiaNimManifest } from "./manifests/nvidia-nim.js"; import { lmStudioManifest } from "./manifests/lm-studio.js"; import { llamacppManifest } from "./manifests/llamacpp.js"; import { xaiManifest } from "./manifests/xai.js"; import { groqManifest } from "./manifests/groq.js"; import { typesafeManifest } from "./manifests/typesafe.js"; import { perplexityDeciderManifest } from "./manifests/perplexityDecider.js"; import { cloudflareClefManifest } from "./manifests/cloudflareClef.js"; import { cohereManifest } from "./manifests/cohere.js"; import { togetherAiManifest } from "./manifests/together-ai.js"; import { fireworksManifest } from "./manifests/fireworks.js"; import { perplexityManifest } from "./manifests/perplexity.js"; import { cloudflareManifest } from "./manifests/cloudflare.js"; import { replicateManifest } from "./manifests/replicate.js"; import { voyageManifest } from "./manifests/voyage.js"; import { jinaManifest } from "./manifests/jina.js"; import { stabilityManifest } from "./manifests/stability.js"; import { ideogramManifest } from "./manifests/ideogram.js"; import { recraftManifest } from "./manifests/recraft.js"; /** * Builds a manifest for a JSON-catalog provider from its catalog entry. * * Only used for cerebras and sambanova (see MANIFEST_REGISTRY below) — the * catalog JSON carries no per-model `maxOutputTokens`, only a provider-wide * `models.defaultMaxOutputTokens`, so every derived entry uses that ceiling. * For cerebras/sambanova this is 8192, identical to their old hand-written * manifests' `maxOutputTokens`. The other 7 catalog providers' hand * manifests carry higher documented ceilings — 64000 for groq, xai, * together-ai, fireworks and perplexity, 8192 for mistral and cloudflare — * that the JSON's `defaultMaxOutputTokens` (a generic 4096 placeholder for * all 7) would silently regress, so they stay hand-written, untouched. * * `contextWindow` falls back to `models.defaultContextWindow` when a model * spec omits its own (matches resolveManifestEntry's synthesized-`_default` * fallback pattern above). `aliases` is always `[]` — CatalogModelSpec has * no alias field. */ function buildCatalogManifest(entry) { // capabilities.tools is "model-dependent" for huggingface, but // huggingface uses its own hand-written manifest (see MANIFEST_REGISTRY // below), never this generic path — cerebras/sambanova, the only two // providers actually routed through buildCatalogManifest() today, both // have a plain boolean here. Narrowed rather than widening // ProviderModelManifestEntry.functionCalling itself: a provider entering // this generic (non-hand-curated) path with "model-dependent" tools gets // the conservative false, not an unearned optimistic true. const functionCalling = entry.capabilities.tools === true; const namedModels = Object.fromEntries(Object.entries(entry.models.catalog).map(([modelId, spec]) => [ modelId, { aliases: [], contextWindow: spec.contextWindow ?? entry.models.defaultContextWindow, maxOutputTokens: entry.models.defaultMaxOutputTokens, vision: spec.vision, functionCalling, }, ])); return { defaultContextWindow: entry.models.defaultContextWindow, models: { _default: { aliases: [], contextWindow: entry.models.defaultContextWindow, maxOutputTokens: entry.models.defaultMaxOutputTokens, vision: false, functionCalling, }, ...namedModels, }, }; } const catalogManifest = (id) => { const entry = getCatalogJsonEntries().find((e) => e.id === id); if (!entry) { throw new Error(`No catalog JSON entry found for provider "${id}"`); } return buildCatalogManifest(entry); }; /** * Builds a manifest for a decide catalog provider from its catalog entry. * * A decide model emits no text, but the manifest type requires a * `maxOutputTokens`, so the figure a decide entry declares is carried over * as is. A model that omits `contextWindow` or `maxOutputTokens` takes the * default model's, and `_default` is the default model's own row. * `functionCalling` is always false: a decide model has no tool calling. * No row gets a `displayName`: the decide catalog has no field for one and * no reader shows a decide model's manifest name. */ function buildDecideManifest(entry) { const base = entry.models.catalog[entry.models.default]; if (base === undefined || base.contextWindow === undefined || base.maxOutputTokens === undefined) { throw new Error(`Decide catalog entry "${entry.id}": models.default needs contextWindow and maxOutputTokens for its manifest`); } const baseWindow = base.contextWindow; const baseMaxOutput = base.maxOutputTokens; const toEntry = (spec) => ({ aliases: [], contextWindow: spec.contextWindow ?? baseWindow, maxOutputTokens: spec.maxOutputTokens ?? baseMaxOutput, vision: spec.vision, functionCalling: false, }); return { defaultContextWindow: baseWindow, models: { _default: toEntry(base), ...Object.fromEntries(Object.entries(entry.models.catalog).map(([modelId, spec]) => [ modelId, toEntry(spec), ])), }, }; } /** * Every provider's model manifest, keyed by the exact AIProviderName enum * value (kebab-case) — e.g. "google-ai", "nvidia-nim". Manifests are pure * data with zero heavy dependencies, so they are imported statically here * (Critical Rule 1's dynamic-import mandate targets providerRegistry.ts's * *provider* factories, which pull in real SDK clients — not this). */ export const MANIFEST_REGISTRY = { anthropic: anthropicManifest, openai: openaiManifest, azure: azureManifest, bedrock: bedrockManifest, ollama: ollamaManifest, mistral: mistralManifest, "google-ai": googleAiManifest, "openai-compatible": openaiCompatibleManifest, openrouter: openrouterManifest, vertex: vertexManifest, huggingface: huggingfaceManifest, litellm: litellmManifest, sagemaker: sagemakerManifest, deepseek: deepseekManifest, "nvidia-nim": nvidiaNimManifest, "lm-studio": lmStudioManifest, llamacpp: llamacppManifest, xai: xaiManifest, groq: groqManifest, typesafe: typesafeManifest, ...Object.fromEntries(getDecideCatalogEntries().map((e) => [e.id, buildDecideManifest(e)])), "perplexity-decider": perplexityDeciderManifest, "cloudflare-clef": cloudflareClefManifest, cerebras: catalogManifest("cerebras"), sambanova: catalogManifest("sambanova"), cohere: cohereManifest, "together-ai": togetherAiManifest, fireworks: fireworksManifest, perplexity: perplexityManifest, cloudflare: cloudflareManifest, replicate: replicateManifest, voyage: voyageManifest, jina: jinaManifest, stability: stabilityManifest, ideogram: ideogramManifest, recraft: recraftManifest, }; export function getManifestForProvider(provider) { return MANIFEST_REGISTRY[provider]; } export function getAllManifestProviders() { return Object.keys(MANIFEST_REGISTRY); } /** * Apply every matching family rule's patch, in declaration order, on top of * a base entry. Later rules win on overlapping fields (last patch applied * wins), matching the "later registrations overwrite earlier ones" idiom * used elsewhere in this subsystem (see registerRuntimeContextWindow's * docblock, src/lib/constants/contextWindows.ts). */ function applyFamilyRules(manifest, model, base) { if (!manifest.familyRules) { return base; } let result = base; for (const rule of manifest.familyRules) { if (rule.pattern.test(model)) { result = { ...result, ...rule.patch }; } } return result; } /** * Resolve a model against a provider's manifest WITHOUT ever falling back to * the provider's `_default` entry. Used by callers that need to insert their * own special-case fallback (cross-provider pricing, proxy pass-through) * between "no real match" and "give up" — see resolveManifestEntry's * docblock for why the split exists. * * Resolution order: exact canonical id, then a declared alias, then * longest-prefix match (for tagged/gateway-shaped ids like Ollama's * "llama3.2:latest" or OpenRouter's "openai/gpt-4o"), then undefined. * Family rules are applied on top of whichever entry matched. */ /** * Like resolveManifestEntryExact but WITHOUT the longest-prefix fallback: * exact canonical id or declared alias only. For consumers where a prefix * hit can steal precedence from a more-specific legacy row — boolean * capability checks above all (a manifest "gpt-4" prefix match must never * shadow the legacy table's explicit "gpt-4-vision-preview" vision row). * Family rules still apply to a real match. */ export function resolveManifestEntryStrict(provider, model) { const manifest = MANIFEST_REGISTRY[provider]; if (!manifest) { return undefined; } const exact = manifest.models[model]; if (exact) { return applyFamilyRules(manifest, model, exact); } const aliasMatch = Object.entries(manifest.models).find(([canonicalId, entry]) => canonicalId !== "_default" && entry.aliases.includes(model)); if (aliasMatch) { return applyFamilyRules(manifest, model, aliasMatch[1]); } return undefined; } export function resolveManifestEntryExact(provider, model) { const manifest = MANIFEST_REGISTRY[provider]; if (!manifest) { return undefined; } const exact = manifest.models[model]; if (exact) { return applyFamilyRules(manifest, model, exact); } const aliasMatch = Object.entries(manifest.models).find(([canonicalId, entry]) => canonicalId !== "_default" && entry.aliases.includes(model)); if (aliasMatch) { return applyFamilyRules(manifest, model, aliasMatch[1]); } const sortedKeys = Object.keys(manifest.models) .filter((k) => k !== "_default") .sort((a, b) => b.length - a.length); const prefixKey = sortedKeys.find((k) => model.startsWith(k)); if (prefixKey) { return applyFamilyRules(manifest, model, manifest.models[prefixKey]); } return undefined; } /** * Provider-specific output-token ceiling used to synthesize a `_default` * entry when a manifest declares no explicit one (currently every * MODEL_REGISTRY-generated manifest — azure, bedrock, ollama, mistral, * google-ai — since `generateFullManifest` only emits real model ids). * Mirrors the same table + fallback chain the minimal-manifest generator * already uses (scripts/generate-remaining-manifests.ts), so a provider * with no PROVIDER_MAX_TOKENS entry still gets an honest ceiling instead of * `undefined`. Clamped against the manifest's own `defaultContextWindow` — * an output ceiling can never legitimately exceed total context. */ function defaultMaxOutputTokens(provider, defaultContextWindow) { const providerLimits = PROVIDER_MAX_TOKENS[provider]; const rawMax = typeof providerLimits === "number" ? providerLimits : (providerLimits?.default ?? PROVIDER_MAX_TOKENS.default); return Math.min(rawMax, defaultContextWindow); } /** * Resolve a model against a provider's manifest, falling back to the * provider's `_default` entry (synthesized from `PROVIDER_MAX_TOKENS`, * clamped to `defaultContextWindow`, when no explicit `_default` model * entry exists) when no real model matches. Family rules are tested * against the ORIGINAL model string even on the `_default` path, so an * unmatched gateway-shaped id still gets patched. */ export function resolveManifestEntry(provider, model) { const exact = resolveManifestEntryExact(provider, model); if (exact) { return exact; } const manifest = MANIFEST_REGISTRY[provider]; if (!manifest) { return undefined; } const defaultEntry = manifest.models._default ?? { aliases: [], contextWindow: manifest.defaultContextWindow, maxOutputTokens: defaultMaxOutputTokens(provider, manifest.defaultContextWindow), vision: false, functionCalling: false, }; return applyFamilyRules(manifest, model, defaultEntry); }