#272·dexter

# Bug: `modelProvider` field in AgentConfig is defined but never used for routing

Author: netloaferCreated May 28, 2026Updated May 28, 2026

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

  1. Configure .dexter/settings.json with provider: "volcengine" and modelId: "deepseek-v4-flash"
  2. Run headless mode: DEXTER_MODEL=deepseek-v4-flash bun run src/headless.ts "test"
  3. The call goes to api.deepseek.com (deepseek provider) instead of ark.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-73Agent.create() only reads config.model, ignores config.modelProvider:

typescript
const model = config.model ?? DEFAULT_MODEL;
// config.modelProvider is never used

2. src/model/llm.ts:153-161getChatModel() only uses resolveProvider(modelName) for prefix matching:

typescript
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-105resolveProvider() has no mechanism for explicit provider override:

typescript
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():

typescript
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).