bug(oss): ConfigManager injects OpenAI's baseURL and model into every other LLM provider, so documented non-OpenAI configs are sent to api.openai.com
Component
TypeScript SDK
Description
Summary
DEFAULT_MEMORY_CONFIG.llm.config holds OpenAI's own values (baseURL: "https://api.openai.com/v1" at mem0-ts/src/oss/src/config/defaults.ts:23, model: "gpt-5-mini" at :25), and ConfigManager.mergeConfig treats them as universal defaults (mem0-ts/src/oss/src/config/manager.ts:126-136):
const llmBaseURL =
userConf?.baseURL ??
...
userConf?.url ??
(provider.toLowerCase() === "vllm" ? undefined : defaultConf.baseURL);
So any config that omits baseURL gets OpenAI's, for every provider except vLLM. The providers then prefer it over their own default and over their documented env fallback:
llms/deepseek.ts:12-16:config.baseURL || process.env.DEEPSEEK_API_BASE || "https://api.deepseek.com"llms/xai.ts:23-24: same shape, soXAI_API_BASEis dead codellms/ollama.ts:15:config.url || config.baseURL || "http://localhost:11434"llms/anthropic.ts:22-25:if (config.baseURL) clientArgs.baseURL = config.baseURL
model has the same shape: manager.ts:116 defaults it to gpt-5-mini for every provider, which makes each provider's own fallback unreachable (deepseek-chat, grok-4.3, llama3.1:8b, gemini-2.0-flash, claude-sonnet-4-6, ...).
The documented snippets are what break. docs/components/llms/models/deepseek.mdx:44-55 shows:
llm: {
provider: 'deepseek',
config: {
apiKey: process.env.DEEPSEEK_API_KEY || '',
model: 'deepseek-chat',
temperature: 0.2,
maxTokens: 2000,
top_p: 1.0,
},
}
There is no baseURL, so copying the docs sends a DeepSeek key to api.openai.com.
The repo already knows about this class of bug and has patched it twice, one provider at a time: manager.ts carves out vllm for baseURL (the ternary above), and the embedder block carves out fastembed for model (manager.ts:18-21, with a comment saying the OpenAI model "only makes sense for API-based providers"). Both carve-outs are correct; every other provider needs the same treatment.
Note this is only about baseURL and model. apiKey is the same shape (manager.ts:152-155 falls back to process.env.OPENAI_API_KEY || "") and the embedder model default is the same shape too. I left both alone here to keep the diff reviewable and because narrowing apiKey has a compatibility wrinkle (llms/azure.ts has no env fallback of its own and currently relies on that injection). Happy to extend the fix to either if you want it in scope.
Steps to Reproduce
import { ConfigManager } from "../src/config/manager";
import { LLMFactory } from "../src/utils/factory";
const cfg = ConfigManager.mergeConfig({
llm: { provider: "deepseek", config: { apiKey: "k" } },
} as any);
console.log(cfg.llm.config.baseURL); // https://api.openai.com/v1
console.log(cfg.llm.config.model); // gpt-5-mini
const llm = LLMFactory.create("deepseek", cfg.llm.config);
console.log((llm as any).openai.baseURL); // https://api.openai.com/v1
Or run the two tests that currently assert the old behaviour:
pnpm exec jest --config jest.config.js --testPathPattern config-manager.
Expected Behavior
For a non-OpenAI provider, mergeConfig should leave baseURL and model unset unless the user supplied them, so the provider applies its own default and its *_API_BASE env fallback.
Actual Behavior
baseURL is https://api.openai.com/v1 and model is gpt-5-mini for every non-vLLM provider, including deepseek, xai, ollama, lmstudio, anthropic, google, groq, mistral, together, minimax, sarvam, and aws_bedrock.
Environment
- mem0 version:
mem0-ts@main(0df3e4b8) - Node version: 24.18.0 (CI targets 20 / 22)
- OS: macOS 15 (darwin), arm64
How You Verified This
What I Ran
Two tests in mem0-ts/src/oss/tests/config-manager.test.ts already asserted the OpenAI default being injected for a non-OpenAI provider, so they fail as soon as the behaviour is corrected:
config-manager.test.ts:145(providerollama) expectshttps://api.openai.com/v1config-manager.test.ts:370(providerlmstudio) expectshttps://api.openai.com/v1
I ran pnpm exec jest --config jest.config.js --testPathPattern config-manager before touching manager.ts and both failed against the corrected expectation, which is the reproduction.
I also read every provider's baseURL/model resolution to confirm the injected values are what win: llms/deepseek.ts:12-16, llms/xai.ts:23-24, llms/ollama.ts:15, llms/lmstudio.ts:15-16, llms/anthropic.ts:22-25, llms/litellm.ts:9-13, plus the embedder block at manager.ts:18-21.
What I Saw
ConfigManager.mergeConfig({ llm: { provider: "deepseek", config: { apiKey: "k" } } })
// cfg.llm.config.baseURL === "https://api.openai.com/v1"
// cfg.llm.config.model === "gpt-5-mini"
and via the real provider:
LLMFactory.create("deepseek", cfg.llm.config) // client baseURL === "https://api.openai.com/v1"
Why This Is a Bug
llms/deepseek.ts:12-16andllms/xai.ts:23-24both list their own default and their env fallback afterconfig.baseURL, so the injected value makesDEEPSEEK_API_BASE/XAI_API_BASEunreachable. Those fallbacks are documented and tested.- Every LLM provider has its own
modeldefault (deepseek-chat,grok-4.3,llama3.1:8b,gemini-2.0-flash,claude-sonnet-4-6,mistral-tiny-latest,MiniMax-M2.7,anthropic.claude-3-5-sonnet-20240620-v1:0, ...), and the injectedgpt-5-minimakes all of them dead code. - The repo carves out
vllmfor exactly this reason andfastembedfor the embedder equivalent, so the intended rule is "provider-specific defaults stay with their provider". The other providers were simply missed. docs/components/llms/models/deepseek.mdx:44-55ships a TypeScript config with nobaseURL, so the documented path is affected.
What I Ruled Out
- Not the provider's own default.
DeepSeekLLMdefaults tohttps://api.deepseek.com, butconfig.baseURLis checked first, so it never runs. - Not the factory.
LLMFactory.create(utils/factory.ts:108-150) passes the config straight to the provider with no injection.mergeConfigis the only place the OpenAI defaults are applied. (RerankerFactory.buildLLMRerankerLLMatutils/factory.ts:252-253does something similar for the LLM reranker, but that is a separate path and not part of this report.) - Not
openai_structured. It shares the OpenAI defaults legitimately, so it is included in the provider test alongsideopenai. - Not the embedder's
baseURL. The embedder block does not fall back to an OpenAI baseURL at all (manager.ts:30-35), so only the LLM endpoint is affected. The embeddermodeldefault is a real sibling issue, noted above and left out of this change. - No provider loses its model. I checked all 18 LLM providers and each one supplies its own fallback when
modelis undefined, includingaws_bedrock(anthropic.claude-3-5-sonnet-20240620-v1:0) andlangchain, which requires themodelfield to be a LangChain instance and throws the same error either way.
Suggested fix
Give the OpenAI defaults only to the OpenAI providers, which also subsumes the vLLM carve-out:
const usesOpenAIDefaults =
provider.toLowerCase() === "openai" ||
provider.toLowerCase() === "openai_structured";
let finalModel: string | any = usesOpenAIDefaults ? defaultConf.model : undefined;
// ...
userConf?.url ?? (usesOpenAIDefaults ? defaultConf.baseURL : undefined);
The two tests that assert the old behaviour change with it. I have this written, with regression tests covering deepseek, xai, lmstudio, ollama, openai and openai_structured, and the full TypeScript suite passes (68 suites, 1024 tests).
AI Assistance
AI found and wrote this, and I have not reproduced it myself
Source: mem0ai/mem0