# Bug: `modelProvider` field in AgentConfig is defined but never used for routing
Repository: virattt/dexter New Issue URL: https://github.com/virattt/dexter/issues/new
Bug Description
AgentConfig.modelProvider is defined in src/agent/types.ts and accepted by Agent.create(), but the entire call chain from headless/CLI → Agent → LLM never reads it. Provider resolution is done solely by resolveProvider(modelName), which matches model name prefixes (e.g., deepseek- → deepseek provider). This means setting a specific provider in settings.json (provider: "volcengine", modelId: "deepseek-v4-flash") has no effect on routing — the call still goes to whichever provider matches the model name prefix.
To reproduce
- Configure
.dexter/settings.jsonwithprovider: "volcengine"andmodelId: "deepseek-v4-flash" - Run headless mode:
DEXTER_MODEL=deepseek-v4-flash bun run src/headless.ts "test" - The call goes to
api.deepseek.com(deepseek provider) instead ofark.cn-beijing.volces.com/api/coding/v3(volcengine provider)
Expected behavior
When a provider is explicitly set (via modelProvider in AgentConfig or provider in settings.json), it should take precedence over prefix-based inference from the model name. The provider field should select the correct factory/endpoint, while the model name is just passed to that endpoint as the model identifier.
Root cause (3 locations)
1. src/agent/agent.ts:72-73 — Agent.create() only reads config.model, ignores config.modelProvider:
const model = config.model ?? DEFAULT_MODEL;
// config.modelProvider is never used2. src/model/llm.ts:153-161 — getChatModel() only uses resolveProvider(modelName) for prefix matching:
export function getChatModel(modelName: string = DEFAULT_MODEL, streaming: boolean = false): BaseChatModel {
const opts: ModelOpts = { streaming };
const provider = resolveProvider(modelName); // <-- only cares about model name prefix
const factory = MODEL_FACTORIES[provider.id] ?? DEFAULT_FACTORY;
return factory(modelName, opts);
}3. src/providers.ts:100-105 — resolveProvider() has no mechanism for explicit provider override:
export function resolveProvider(modelName: string): ProviderDef {
return PROVIDERS.find((p) => p.modelPrefix && modelName.startsWith(p.modelPrefix)) ?? defaultProvider;
}Suggested fix
Option A (minimal): Add a providerOverride parameter to getChatModel() and resolveProvider():
export function getChatModel(modelName: string, streaming = false, providerOverride?: string): BaseChatModel {
const opts = { streaming };
const provider = providerOverride
? getProviderById(providerOverride) ?? resolveProvider(modelName)
: resolveProvider(modelName);
const factory = MODEL_FACTORIES[provider.id] ?? DEFAULT_FACTORY;
return factory(modelName, opts);
}Option B (more thorough): Thread modelProvider from AgentConfig through the full call stack: Agent.create() → Agent.run() → callLlmWithMessages() / streamLlmWithMessages() → getChatModel().
Environment
- Dexter version: 2026.5.20 (commit 90b802c)
- Bun runtime (tested on macOS)
Additional context
This becomes visible when using a provider like Volcengine Ark that serves a model with a mismatched prefix. For example, Volcengine Ark exposes deepseek-v4-flash via endpoint ark.cn-beijing.volces.com, but if a user sets the model name to deepseek-v4-flash directly, the deepseek- prefix incorrectly routes to DeepSeek's own API endpoint (api.deepseek.com).
Source: virattt/dexter