Skip to content

Model fallback ignores reasoning and uses wrong contextWindow/maxTokens #25636

@mcaxtr

Description

@mcaxtr

OpenClaw version: 2026.2.23

Describe the bug

The generic fallback in resolveModel() has two bugs when constructing a model from provider config:

  1. reasoning: false hardcoded — ignores the matched model's reasoning setting, preventing reasoning.effort from being forwarded to the API (e.g., Ollama models with reasoning: true)
  2. models[0] for contextWindow/maxTokens — reads from the first model in the provider's array instead of the matched model, causing incorrect context limits when multiple models are configured

Impact: Users running Ollama models with reasoning: true get minimal thinking output (~30 tokens) instead of deep reasoning (200+ tokens). Users with multiple models under a custom provider get wrong context windows.

Steps to reproduce

  1. Configure Ollama provider with a reasoning-capable model:
{
  "models": {
    "providers": {
      "ollama": {
        "baseUrl": "http://localhost:11434/v1",
        "api": "openai-responses",
        "models": [{
          "id": "thinking-model",
          "reasoning": true,
          "contextWindow": 128000
        }]
      }
    }
  },
  "agents": {
    "defaults": {
      "model": { "primary": "ollama/thinking-model" },
      "thinkingDefault": "high"
    }
  }
}
  1. Send a message that requires reasoning
  2. Check session history or gateway logs

Expected behavior

  • reasoning.effort: "high" should be included in API request
  • Model should produce detailed reasoning (200+ tokens)
  • Context window should be 128000 (from config)

Actual behavior

  • Gateway logs show thinking=high but reasoning.effort is not forwarded
  • Model produces minimal thinking (~30 tokens)
  • Context window may be incorrect if multiple models configured

Root cause

src/agents/pi-embedded-runner/model.ts lines 103-115:

const fallbackModel: Model<Api> = normalizeModelCompat({
  // ... other fields ...
  reasoning: false,  // ❌ Hardcoded, ignores matched model config
  contextWindow: providerCfg?.models?.[0]?.contextWindow ?? DEFAULT_CONTEXT_TOKENS,  // ❌ Wrong model
  maxTokens: providerCfg?.models?.[0]?.maxTokens ?? DEFAULT_CONTEXT_TOKENS,  // ❌ Wrong model
} as Model<Api>);

Proposed fix

Find the matched model by ID and propagate its properties:

const matchedModel = (providerCfg?.models ?? []).find((m) => m.id.trim() === modelId.trim());
const fallbackModel: Model<Api> = normalizeModelCompat({
  // ... other fields ...
  reasoning: matchedModel?.reasoning ?? false,  // ✅ From matched model
  contextWindow: matchedModel?.contextWindow ?? DEFAULT_CONTEXT_TOKENS,  // ✅ Correct model
  maxTokens: matchedModel?.maxTokens ?? DEFAULT_CONTEXT_TOKENS,  // ✅ Correct model
} as Model<Api>);

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions