()
| 142 | * Called once on first access; cached thereafter. |
| 143 | */ |
| 144 | export function loadEmbeddingConfig(): EmbeddingConfig { |
| 145 | if (_config) return _config; |
| 146 | |
| 147 | // ── Provider ──────────────────────────────────────────────────────── |
| 148 | const rawProvider = process.env.EMBEDDING_PROVIDER || "ollama"; |
| 149 | if ( |
| 150 | rawProvider !== "ollama" && |
| 151 | rawProvider !== "openai" && |
| 152 | rawProvider !== "google" && |
| 153 | rawProvider !== "lmstudio" && |
| 154 | rawProvider !== "litellm" |
| 155 | ) { |
| 156 | throw new Error( |
| 157 | `Invalid EMBEDDING_PROVIDER: "${rawProvider}". Must be "ollama", "openai", "google", "lmstudio", or "litellm".`, |
| 158 | ); |
| 159 | } |
| 160 | const embeddingProvider: EmbeddingProvider = rawProvider; |
| 161 | const providerDefaults = PROVIDER_DEFAULTS[embeddingProvider]; |
| 162 | |
| 163 | // LM Studio has no sensible defaults — model and dimensions vary per loaded model. |
| 164 | // Fail fast with an actionable message rather than silently sending empty values. |
| 165 | if (embeddingProvider === "lmstudio") { |
| 166 | if (!process.env.EMBEDDING_MODEL) { |
| 167 | throw new Error( |
| 168 | "EMBEDDING_MODEL is required when EMBEDDING_PROVIDER=lmstudio. " + |
| 169 | "LM Studio has no built-in default — set it to the model identifier shown in " + |
| 170 | "LM Studio's Local Server tab (e.g. EMBEDDING_MODEL=nomic-embed-text-v1.5).", |
| 171 | ); |
| 172 | } |
| 173 | if (!process.env.EMBEDDING_DIMENSIONS) { |
| 174 | throw new Error( |
| 175 | "EMBEDDING_DIMENSIONS is required when EMBEDDING_PROVIDER=lmstudio. " + |
| 176 | "Different LM Studio models have different output dimensions — check the model card " + |
| 177 | "and set EMBEDDING_DIMENSIONS accordingly (e.g. 768 for nomic-embed-text-v1.5, " + |
| 178 | "1024 for bge-large-en-v1.5, 4096 for qwen3-embedding-8b).", |
| 179 | ); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | // LiteLLM proxy aliases are user-defined in config.yaml — there is no canonical |
| 184 | // default model name and the underlying dimension depends on which provider the |
| 185 | // alias resolves to. Authentication is also mandatory (the proxy enforces it |
| 186 | // even for read-only /v1/models). Fail fast on each missing piece so the |
| 187 | // operator gets a single, specific error rather than a generic 401 / 404 from |
| 188 | // the proxy at first embed(). |
| 189 | if (embeddingProvider === "litellm") { |
| 190 | if (!process.env.LITELLM_API_KEY) { |
| 191 | throw new Error( |
| 192 | "LITELLM_API_KEY is required when EMBEDDING_PROVIDER=litellm. " + |
| 193 | "Set it to the proxy's master key (general_settings.master_key in config.yaml) " + |
| 194 | "or to a virtual key issued via LiteLLM's /key/generate endpoint.", |
| 195 | ); |
| 196 | } |
| 197 | if (!process.env.EMBEDDING_MODEL) { |
| 198 | throw new Error( |
| 199 | "EMBEDDING_MODEL is required when EMBEDDING_PROVIDER=litellm. " + |
| 200 | "Set it to a model_name from your LiteLLM config.yaml (e.g. EMBEDDING_MODEL=text-embedding-3-small " + |
| 201 | "if your proxy aliases that name; LiteLLM rewrites the call to whichever litellm_params.model " + |
no test coverage detected