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

688 lines (687 loc) • 26.7 kB
/** * Centralized model choices for CLI commands * Derives choices from model enums to ensure consistency */ import { AIProviderName, OpenAIModels, AnthropicModels, GoogleAIModels, BedrockModels, VertexModels, OllamaModels, AzureOpenAIModels, LiteLLMModels, SageMakerModels, OpenRouterModels, NvidiaNimModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, TypeSafeModels, LayaModels, XorModels, PerplexityDeciderModels, CloudflareClefModels, ReplicateModels, } from "../constants/enums.js"; import { getCatalogJsonEntries } from "../providers/catalog/loader.js"; /** * Looks up the JSON catalog entry for a provider, if it is one of the 11 * JSON-catalog providers. Used to short-circuit the hand tables below so a * catalog provider's models are always sourced from its catalog JSON, never * from stale hand-written duplicates. */ function catalogEntryFor(provider) { return getCatalogJsonEntries().find((entry) => entry.id === provider); } /** * Derives CLI-prompt entries for a catalog provider. Uses the curated * `models.topModels` ordering when the JSON provides one (mapping each id to * its own `models.catalog[id].description`); falls back to the full * `models.catalog` in file key-order when `topModels` is absent, matching * the loader's documented fallback contract. */ function catalogTopModels(entry) { if (entry.models.topModels) { return entry.models.topModels.map((model) => ({ model, description: entry.models.catalog[model].description, })); } return Object.entries(entry.models.catalog).map(([model, spec]) => ({ model, description: spec.description, })); } /** * Top models per provider with descriptions for CLI prompts * These are curated lists of the most commonly used/recommended models * * Covers every provider EXCEPT the JSON-catalog providers (see * `CatalogProviderName`) — their entries are derived from * `models.catalog[*].description` by `catalogTopModels()` and merged in at * the accessor-function level below, never hand-duplicated here. */ const TOP_MODELS_CONFIG = { [AIProviderName.OPENAI]: [ { model: OpenAIModels.GPT_4O, description: "Recommended - Latest multimodal model", }, { model: OpenAIModels.GPT_4O_MINI, description: "Cost-effective, fast" }, { model: OpenAIModels.GPT_5_2, description: "Latest flagship with deep reasoning", }, { model: OpenAIModels.O3, description: "Advanced reasoning model" }, { model: OpenAIModels.GPT_4_TURBO, description: "Previous generation" }, { model: OpenAIModels.GPT_3_5_TURBO, description: "Legacy, most cost-effective", }, ], [AIProviderName.ANTHROPIC]: [ { model: AnthropicModels.CLAUDE_SONNET_4_5, description: "Recommended - Latest and most capable", }, { model: AnthropicModels.CLAUDE_4_5_HAIKU, description: "Fast and cost-effective", }, { model: AnthropicModels.CLAUDE_OPUS_4_5, description: "Most powerful for complex tasks", }, { model: AnthropicModels.CLAUDE_3_5_SONNET, description: "Excellent reasoning and coding", }, { model: AnthropicModels.CLAUDE_3_5_HAIKU, description: "Fast and economical", }, { model: AnthropicModels.CLAUDE_3_OPUS, description: "Previous gen, powerful", }, ], [AIProviderName.GOOGLE_AI]: [ { model: GoogleAIModels.GEMINI_2_5_FLASH, description: "Recommended - Fast and efficient", }, { model: GoogleAIModels.GEMINI_2_5_PRO, description: "Most capable, large context", }, { model: GoogleAIModels.GEMINI_2_0_FLASH, description: "Stable production model", }, { model: GoogleAIModels.GEMINI_3_PRO_PREVIEW, description: "Latest preview", }, ], [AIProviderName.VERTEX]: [ { model: VertexModels.GEMINI_2_5_FLASH, description: "Recommended - Fast and efficient", }, { model: VertexModels.GEMINI_2_5_PRO, description: "Most capable, large context", }, { model: VertexModels.CLAUDE_4_5_SONNET, description: "Claude on Vertex" }, { model: VertexModels.GEMINI_2_0_FLASH, description: "Stable production model", }, { model: VertexModels.CLAUDE_3_5_SONNET, description: "Claude 3.5 on Vertex", }, ], [AIProviderName.BEDROCK]: [ { model: BedrockModels.CLAUDE_4_5_SONNET, description: "Recommended - Latest Claude", }, { model: BedrockModels.NOVA_PRO, description: "Amazon Nova balanced" }, { model: BedrockModels.NOVA_LITE, description: "Fast and cost-effective" }, { model: BedrockModels.CLAUDE_3_5_SONNET, description: "Excellent coding" }, { model: BedrockModels.LLAMA_4_MAVERICK_17B, description: "Meta Llama 4" }, { model: BedrockModels.MISTRAL_LARGE_3, description: "Mistral flagship" }, ], [AIProviderName.AZURE]: [ { model: AzureOpenAIModels.GPT_4O, description: "Recommended - Latest multimodal", }, { model: AzureOpenAIModels.GPT_4O_MINI, description: "Cost-effective, fast", }, { model: AzureOpenAIModels.GPT_5_1, description: "Latest flagship" }, { model: AzureOpenAIModels.O3, description: "Advanced reasoning" }, { model: AzureOpenAIModels.GPT_4_TURBO, description: "Previous generation", }, { model: AzureOpenAIModels.GPT_3_5_TURBO, description: "Legacy model" }, ], [AIProviderName.OLLAMA]: [ { model: OllamaModels.LLAMA4_LATEST, description: "Recommended - Latest Llama 4", }, { model: OllamaModels.LLAMA3_3_LATEST, description: "High-performance local", }, { model: OllamaModels.DEEPSEEK_R1_70B, description: "Advanced reasoning" }, { model: OllamaModels.QWEN3_72B, description: "Multilingual reasoning" }, { model: OllamaModels.MISTRAL_LARGE_LATEST, description: "Mistral local" }, { model: OllamaModels.LLAMA3_2_LATEST, description: "Efficient local model", }, ], [AIProviderName.LITELLM]: [ { model: LiteLLMModels.OPENAI_GPT_4O, description: "OpenAI via LiteLLM" }, { model: LiteLLMModels.ANTHROPIC_CLAUDE_SONNET_4_5, description: "Anthropic via LiteLLM", }, { model: LiteLLMModels.GEMINI_2_5_PRO, description: "Google via LiteLLM" }, { model: LiteLLMModels.GROQ_LLAMA_3_1_70B_VERSATILE, description: "Groq via LiteLLM", }, { model: LiteLLMModels.MISTRAL_LARGE, description: "Mistral via LiteLLM" }, { model: LiteLLMModels.VERTEX_GEMINI_2_5_PRO, description: "Vertex via LiteLLM", }, ], [AIProviderName.SAGEMAKER]: [ { model: SageMakerModels.LLAMA_4_MAVERICK_17B_128E, description: "Recommended - Llama 4", }, { model: SageMakerModels.LLAMA_3_70B, description: "Meta Llama 3 70B" }, { model: SageMakerModels.MISTRAL_SMALL_24B, description: "Mistral Small" }, { model: SageMakerModels.MIXTRAL_8X7B, description: "Mixtral MoE" }, { model: SageMakerModels.FALCON_3_10B, description: "Falcon 3" }, { model: SageMakerModels.CODE_LLAMA_34B, description: "Code Llama" }, ], [AIProviderName.OPENROUTER]: [ { model: OpenRouterModels.CLAUDE_SONNET_4_5, description: "Anthropic via OpenRouter", }, { model: OpenRouterModels.GPT_4O, description: "OpenAI via OpenRouter" }, { model: OpenRouterModels.GEMINI_2_0_FLASH, description: "Google via OpenRouter", }, { model: OpenRouterModels.LLAMA_3_1_70B, description: "Meta Llama" }, { model: OpenRouterModels.MISTRAL_LARGE, description: "Mistral via OpenRouter", }, { model: OpenRouterModels.MIXTRAL_8X7B, description: "Mixtral MoE" }, ], [AIProviderName.OPENAI_COMPATIBLE]: [ { model: "gpt-4o", description: "OpenAI-compatible model" }, { model: "gpt-4o-mini", description: "Fast compatible model" }, { model: "gpt-4-turbo", description: "Turbo compatible model" }, { model: "gpt-3.5-turbo", description: "Legacy compatible model" }, ], [AIProviderName.NVIDIA_NIM]: [ { model: "meta/llama-3.3-70b-instruct", description: "Recommended - Llama 3.3 70B", }, { model: "nvidia/llama-3.3-nemotron-super-49b-v1", description: "Nemotron Super (reasoning)", }, { model: "deepseek-ai/deepseek-r1", description: "DeepSeek-R1 hosted on NIM", }, { model: "meta/llama-3.2-90b-vision-instruct", description: "Llama 3.2 vision", }, { model: "mistralai/mixtral-8x22b-instruct-v0.1", description: "Mixtral 8x22B", }, ], [AIProviderName.LM_STUDIO]: [ { model: "", description: "Auto-discover loaded model from /v1/models", }, ], [AIProviderName.LLAMACPP]: [ { model: "", description: "Use whatever model llama-server has loaded", }, ], [AIProviderName.COHERE]: [ { model: CohereModels.COMMAND_R_PLUS, description: "Recommended - Flagship RAG-tuned chat", }, { model: CohereModels.COMMAND_R, description: "Smaller RAG-tuned chat" }, { model: CohereModels.COMMAND_R7B, description: "Most compact" }, ], [AIProviderName.REPLICATE]: [ { model: "meta/meta-llama-3-70b-instruct", description: "Recommended - Llama 3 70B Instruct (stable)", }, { model: "meta/meta-llama-3.1-405b-instruct", description: "Llama 3.1 405B (flagship)", }, { model: "mistralai/mixtral-8x7b-instruct-v0.1", description: "Mixtral 8x7B v0.1", }, { model: "black-forest-labs/flux-1.1-pro", description: "FLUX 1.1 Pro (image-gen)", }, { model: "stability-ai/stable-diffusion-3.5-large", description: "SD 3.5 Large (image-gen)", }, ], [AIProviderName.VOYAGE]: [ { model: VoyageModels.VOYAGE_3_5, description: "Recommended - General-purpose embeddings", }, { model: VoyageModels.VOYAGE_3_LARGE, description: "Flagship — highest accuracy", }, { model: VoyageModels.VOYAGE_CODE_3, description: "Code-tuned embeddings", }, { model: VoyageModels.VOYAGE_MULTILINGUAL_2, description: "Multilingual embeddings", }, { model: VoyageModels.VOYAGE_3_5_LITE, description: "Smaller / cheaper", }, ], [AIProviderName.JINA]: [ { model: JinaModels.JINA_EMBEDDINGS_V3, description: "Recommended - Multilingual flagship", }, { model: JinaModels.JINA_EMBEDDINGS_V2_BASE_EN, description: "v2 base English", }, { model: JinaModels.JINA_EMBEDDINGS_V2_BASE_CODE, description: "v2 base code", }, { model: JinaModels.JINA_RERANKER_V2_BASE_MULTILINGUAL, description: "Reranker (multilingual)", }, { model: JinaModels.JINA_COLBERT_V2, description: "Late-interaction retrieval", }, ], [AIProviderName.STABILITY]: [ { model: StabilityModels.STABLE_IMAGE_ULTRA, description: "Recommended - Flagship quality", }, { model: StabilityModels.STABLE_IMAGE_CORE, description: "Fast tier", }, { model: StabilityModels.SD_3_5_LARGE, description: "SD 3.5 Large" }, { model: StabilityModels.SD_3_5_LARGE_TURBO, description: "SD 3.5 Large Turbo", }, { model: StabilityModels.SD_3_5_MEDIUM, description: "SD 3.5 Medium" }, ], [AIProviderName.IDEOGRAM]: [ { model: IdeogramModels.IDEOGRAM_V3, description: "Recommended - V3" }, { model: IdeogramModels.IDEOGRAM_V2, description: "V2" }, { model: IdeogramModels.IDEOGRAM_V2_TURBO, description: "V2 Turbo (fast)" }, { model: IdeogramModels.IDEOGRAM_V1, description: "V1" }, ], [AIProviderName.RECRAFT]: [ { model: RecraftModels.RECRAFT_V3, description: "Recommended - Raster V3", }, { model: RecraftModels.RECRAFT_V3_SVG, description: "Vector / SVG output", }, { model: RecraftModels.RECRAFT_V2, description: "V2" }, ], [AIProviderName.TYPESAFE]: [ { model: TypeSafeModels.JEV_LATEST, description: "Recommended - Jev (decisions, not text)", }, { model: TypeSafeModels.JEV_PREVIEW, description: "Preview build of Jev", }, ], [AIProviderName.LAYA]: [ { model: LayaModels.TYPED_DECISIONS, description: "Recommended - Laya fine-tuned for typed decisions", }, { model: LayaModels.MULTILINGUAL, description: "Laya, 100+ languages", }, { model: LayaModels.ENGLISH, description: "Laya, English base checkpoint (512-token window)", }, { model: LayaModels.AUTO, description: "Let Laya pick a checkpoint by language", }, ], [AIProviderName.XOR]: [ { model: XorModels.XOR_1_1, description: "Recommended - XOR 1.1, decision model that also reads images and video", }, ], [AIProviderName.PERPLEXITY_DECIDER]: [ { model: PerplexityDeciderModels.PPLX_DECIDER_V1_27B, description: "Recommended - Perplexity decision model that also reads images", }, ], [AIProviderName.CLOUDFLARE_CLEF]: [ { model: CloudflareClefModels.CLEF, description: "Recommended - Cloudflare Clef 27B decision model that also reads images (state limited to ~2,000 tokens)", }, { model: CloudflareClefModels.CLEF_FLASH, description: "Clef-flash 9B - the faster, cheaper Cloudflare decision model (same ~2,000-token state limit)", }, ], [AIProviderName.AUTO]: [], }; /** * Default models per provider (first choice/recommended). * * Covers every provider EXCEPT the JSON-catalog providers (see * `CatalogProviderName`) — those default models come from `models.default` * in the catalog JSON, read directly by `getDefaultModel()` below. (cerebras, * sambanova — never had a hand entry here at all; `getDefaultModel()` now * resolves them too.) * * AUTO is also excluded — it never had an entry here either (matches * pre-existing behavior: `getDefaultModel(AUTO)` returns `undefined`). */ export const DEFAULT_MODELS = { [AIProviderName.OPENAI]: OpenAIModels.GPT_4O, [AIProviderName.ANTHROPIC]: AnthropicModels.CLAUDE_SONNET_4_5, [AIProviderName.GOOGLE_AI]: GoogleAIModels.GEMINI_2_5_FLASH, [AIProviderName.VERTEX]: VertexModels.GEMINI_2_5_FLASH, [AIProviderName.BEDROCK]: BedrockModels.CLAUDE_4_5_SONNET, [AIProviderName.AZURE]: AzureOpenAIModels.GPT_4O, [AIProviderName.OLLAMA]: OllamaModels.LLAMA4_LATEST, [AIProviderName.LITELLM]: LiteLLMModels.OPENAI_GPT_4O, [AIProviderName.SAGEMAKER]: SageMakerModels.LLAMA_4_MAVERICK_17B_128E, [AIProviderName.OPENROUTER]: OpenRouterModels.CLAUDE_SONNET_4_5, [AIProviderName.OPENAI_COMPATIBLE]: "gpt-4o", [AIProviderName.NVIDIA_NIM]: NvidiaNimModels.GPT_OSS_20B, // LM Studio + llama.cpp auto-discover their loaded model from /v1/models; // an empty default is the documented signal to use that path. [AIProviderName.LM_STUDIO]: "", [AIProviderName.LLAMACPP]: "", [AIProviderName.COHERE]: CohereModels.COMMAND_R_PLUS, [AIProviderName.REPLICATE]: "meta/meta-llama-3-70b-instruct", [AIProviderName.VOYAGE]: VoyageModels.VOYAGE_3_5, [AIProviderName.JINA]: JinaModels.JINA_EMBEDDINGS_V3, [AIProviderName.STABILITY]: StabilityModels.STABLE_IMAGE_ULTRA, [AIProviderName.IDEOGRAM]: IdeogramModels.IDEOGRAM_V3, [AIProviderName.RECRAFT]: RecraftModels.RECRAFT_V3, [AIProviderName.TYPESAFE]: TypeSafeModels.JEV_LATEST, [AIProviderName.LAYA]: LayaModels.TYPED_DECISIONS, [AIProviderName.XOR]: XorModels.XOR_1_1, [AIProviderName.PERPLEXITY_DECIDER]: PerplexityDeciderModels.PPLX_DECIDER_V1_27B, [AIProviderName.CLOUDFLARE_CLEF]: CloudflareClefModels.CLEF, }; /** * Model enum mappings for getAllModels. * * Covers every provider EXCEPT the JSON-catalog providers (see * `CatalogProviderName`) — those model lists come from * `Object.keys(models.catalog)` in the catalog JSON, read directly by * `getAllModels()` below. */ const MODEL_ENUMS = { [AIProviderName.OPENAI]: OpenAIModels, [AIProviderName.ANTHROPIC]: AnthropicModels, [AIProviderName.GOOGLE_AI]: GoogleAIModels, [AIProviderName.VERTEX]: VertexModels, [AIProviderName.BEDROCK]: BedrockModels, [AIProviderName.AZURE]: AzureOpenAIModels, [AIProviderName.OLLAMA]: OllamaModels, [AIProviderName.LITELLM]: LiteLLMModels, [AIProviderName.SAGEMAKER]: SageMakerModels, [AIProviderName.OPENROUTER]: OpenRouterModels, [AIProviderName.OPENAI_COMPATIBLE]: null, [AIProviderName.NVIDIA_NIM]: NvidiaNimModels, [AIProviderName.LM_STUDIO]: null, [AIProviderName.LLAMACPP]: null, [AIProviderName.COHERE]: CohereModels, [AIProviderName.REPLICATE]: ReplicateModels, [AIProviderName.VOYAGE]: VoyageModels, [AIProviderName.JINA]: JinaModels, [AIProviderName.STABILITY]: StabilityModels, [AIProviderName.IDEOGRAM]: IdeogramModels, [AIProviderName.RECRAFT]: RecraftModels, [AIProviderName.TYPESAFE]: TypeSafeModels, [AIProviderName.LAYA]: LayaModels, [AIProviderName.XOR]: XorModels, [AIProviderName.PERPLEXITY_DECIDER]: PerplexityDeciderModels, [AIProviderName.CLOUDFLARE_CLEF]: CloudflareClefModels, [AIProviderName.AUTO]: null, }; /** * Get top model choices for a provider (for CLI prompts) * Returns models formatted for inquirer list prompts * * @param provider - The AI provider to get models for * @param limit - Maximum number of models to return (default: 5) * @returns Array of ModelChoice objects for CLI prompts */ export function getTopModelChoices(provider, limit = 5) { const catalogEntry = catalogEntryFor(provider); const config = catalogEntry ? catalogTopModels(catalogEntry) : TOP_MODELS_CONFIG[provider]; if (!config || config.length === 0) { return []; } const choices = config.slice(0, limit).map((item) => ({ // Empty-string entries are auto-discovery sentinels for LM Studio / // llama.cpp. Surface them with a friendly label so the CLI doesn't show a // blank row, but keep `value: ""` so it matches `DEFAULT_MODELS` (which // also uses `""`) and any caller that preselects the active choice via // the default model still resolves to this entry. name: item.model.length > 0 ? `${item.model} (${item.description})` : `Auto-discover loaded model (${item.description})`, value: item.model, description: item.description, })); // Always add custom option at the end choices.push({ name: "Custom model (enter manually)", value: "custom", description: "Enter a custom model name", }); return choices; } /** * Get all available models for a provider * Returns all values from the provider's model enum * * For a JSON-catalog provider, excludes retired ids. The `catalog` map keeps * retired entries as raw table keys (they still resolve for a caller who * pins that id explicitly — see `isValidModel`'s dedicated retired-id * allowance below), but a listing/selection surface should not offer them. * Mirrors the `selectableModels` filter in catalog/schema.ts. * * @param provider - The AI provider to get models for * @returns Array of model identifier strings */ export function getAllModels(provider) { const catalogEntry = catalogEntryFor(provider); if (catalogEntry) { return Object.entries(catalogEntry.models.catalog) .filter(([, spec]) => spec.status !== "retired") .map(([model]) => model); } const modelEnum = MODEL_ENUMS[provider]; if (!modelEnum) { return []; } return Object.values(modelEnum); } /** * Get available provider choices for CLI * Returns all provider names except AUTO * * @returns Array of provider name strings */ export function getProviderChoices() { return Object.values(AIProviderName).filter((p) => p !== AIProviderName.AUTO); } /** * Get all provider choices including AUTO * * @returns Array of all provider name strings */ export function getAllProviderChoices() { return Object.values(AIProviderName); } /** * Get the default model for a provider * * @param provider - The AI provider * @returns Default model string for the provider */ export function getDefaultModel(provider) { const catalogEntry = catalogEntryFor(provider); if (catalogEntry) { return catalogEntry.models.default; } return DEFAULT_MODELS[provider]; } /** * Check if a model is valid for a given provider * * `getAllModels` excludes retired catalog ids (it is a listing/selection * surface), but a caller who pins a retired id explicitly must not have * that pin start failing validation just because it dropped out of the * listing — so this falls back to the full catalog (all statuses) before * rejecting a JSON-catalog provider's model. * * The empty-listing case is checked against catalog membership, not * accepted outright: a catalog provider whose every entry happens to be * retired still has a real catalog to validate against, and a fabricated * id must not slip through just because the *selectable* listing is empty. * Only a provider with no catalog entry at all (openai-compatible, * lm-studio, llamacpp — providers without a strict model list) keeps the * permissive "allow any model" behavior. * * @param provider - The AI provider * @param model - The model identifier to check * @returns true if the model exists in the provider's model enum */ export function isValidModel(provider, model) { const models = getAllModels(provider); if (models.includes(model)) { return true; } const catalogEntry = catalogEntryFor(provider); if (catalogEntry) { // Falls back to the FULL catalog (all statuses) so an explicitly pinned // retired id still validates, and — critically — so an all-retired // catalog (getAllModels() === []) is checked against real membership // instead of accepting any string. Object.hasOwn (not `in`) so an // inherited Object.prototype member name (e.g. "toString") is never // mistaken for a real catalog key. return Object.hasOwn(catalogEntry.models.catalog, model); } // No JSON catalog and no strict MODEL_ENUMS entry (openai-compatible, // lm-studio, llamacpp): keep the permissive "allow any model" behavior. return models.length === 0; } /** * Get model choices formatted for inquirer prompts with a specific default * * @param provider - The AI provider * @param currentModel - Current/existing model to mark as default * @param limit - Maximum number of models to return * @returns Array of ModelChoice objects with the current model marked */ export function getModelChoicesWithDefault(provider, currentModel, limit = 5) { const choices = getTopModelChoices(provider, limit); if (currentModel && !choices.some((c) => c.value === currentModel)) { // Insert current model at the top if not already in the list choices.unshift({ name: `${currentModel} (current)`, value: currentModel, description: "Currently configured model", }); } else if (currentModel) { // Mark the current model in the list const idx = choices.findIndex((c) => c.value === currentModel); if (idx !== -1) { choices[idx].name = `${choices[idx].name.replace(/\)$/, ", current)")}`; } } return choices; } /** * Get a flat list of popular models across all providers * Useful for model suggestions and auto-complete * * @returns Array of { provider, model, description } objects */ export function getPopularModelsAcrossProviders() { const popularModels = []; // Take top 2 from each provider, ignoring blank auto-discovery sentinels. // (Auto-discovery is surfaced separately by `getTopModelChoices` for // LM Studio / llama.cpp; we don't want it to appear in the cross- // provider popular-models list as an empty value.) const pushTop2 = (provider, config) => { config .filter((item) => item.model.length > 0) .slice(0, 2) .forEach((item) => { popularModels.push({ provider, model: item.model, description: item.description, }); }); }; for (const [provider, config] of Object.entries(TOP_MODELS_CONFIG)) { if (config && config.length > 0) { pushTop2(provider, config); } } // JSON-catalog providers are excluded from TOP_MODELS_CONFIG above; merge // their models in here, derived the same way getTopModelChoices() does. for (const entry of getCatalogJsonEntries()) { pushTop2(entry.id, catalogTopModels(entry)); } return popularModels; }