openclaw - 💡(How to fix) Fix feat(models): use provider/name as internal key to decouple from API model ID [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#59168Fetched 2026-04-08 02:27:52
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
1
Author
Participants

The id field on provider model entries is overloaded: it serves as both the OC internal key (provider/id) and the literal model name sent to upstream APIs. This makes it impossible to define multiple configurations of the same API model (e.g., different reasoning levels, temperature defaults, or context profiles) under a single provider.

Proposed fix: Use provider/name as the internal key instead of provider/id. The name field (already required) becomes the unique-per-provider identifier. The id field becomes exclusively the API model name. No new fields needed.

Root Cause

Because the API requires an exact model name (e.g., OpenRouter rejects anything other than qwen/qwen3.6-plus-preview:free), you can't vary id to create distinct configurations. And duplicate id values within the same provider collide on the internal key.

Fix Action

Fix / Workaround

Current workarounds (all awkward):

  • Create duplicate providers with different names but the same baseUrl and credentials and name them like OpenRouter-Low (for your low-reasoning models) or OpenRouter-High (for your high-reasoning models) - works, but awkward and departs from the clean provider/model schema for viewing and selecting models per provider
  • Use agents.defaults.models[key].params.thinking — but only one value per model key, and configured in a separate location from the model entry

Code Example

{
  "id": "qwen/qwen3.6-plus-preview:free",  // <-- OC internal key AND API model name
  "name": "Qwen 3.6 Plus",                 // <-- display label only (wasted)
  "reasoning": true
}

---

// BROKEN: duplicate id within provider
{ "id": "qwen/qwen3.6-plus-preview:free", "name": "Low reasoning" },
{ "id": "qwen/qwen3.6-plus-preview:free", "name": "High reasoning" }

---

"ollama-cloud-thinking": {
  "models": [
    { "id": "minimax-m2.7", "name": "Minimax M2.7 Thinking", "reasoning": true }
  ]
},
"ollama-cloud": {
  "models": [
    { "id": "minimax-m2.7", "name": "Minimax M2.7", "reasoning": false }
  ]
}

---

"ollama-cloud": {
  "models": [
    { "name": "minimax-m2.7",          "id": "minimax-m2.7", "reasoning": "off" },
    { "name": "minimax-m2.7-thinking", "id": "minimax-m2.7", "reasoning": "medium" }
  ]
}

---

"openrouter": {
  "models": [
    { "name": "qwen36-minimal", "id": "qwen/qwen3.6-plus-preview:free", "reasoning": "minimal" },
    { "name": "qwen36-high",    "id": "qwen/qwen3.6-plus-preview:free", "reasoning": "high" }
  ]
}

---

"reasoning": "high"     // new: specific level
"reasoning": true       // existing: model supports reasoning, use default level
"reasoning": false      // existing: model doesn't support reasoning
RAW_BUFFERClick to expand / collapse

Proposal: Use provider/name as Internal Model Key (decouple from API model ID)

Target: openclaw/openclaw Type: Feature proposal (architectural) Related issues: #49355, #35476, #50651, #36003, #29007, #27988, #52646, #14494, #47852, #41594, #38256, #48876, #39253


Summary

The id field on provider model entries is overloaded: it serves as both the OC internal key (provider/id) and the literal model name sent to upstream APIs. This makes it impossible to define multiple configurations of the same API model (e.g., different reasoning levels, temperature defaults, or context profiles) under a single provider.

Proposed fix: Use provider/name as the internal key instead of provider/id. The name field (already required) becomes the unique-per-provider identifier. The id field becomes exclusively the API model name. No new fields needed.

Problem

The id field serves two incompatible purposes

{
  "id": "qwen/qwen3.6-plus-preview:free",  // <-- OC internal key AND API model name
  "name": "Qwen 3.6 Plus",                 // <-- display label only (wasted)
  "reasoning": true
}
  1. Internal key: Combined as provider/id for model selection, agents.defaults.models lookup, fallback chains, UI state, context window caching, etc.
  2. API payload: Sent literally as { "model": "<id>" } to the upstream provider.

Because the API requires an exact model name (e.g., OpenRouter rejects anything other than qwen/qwen3.6-plus-preview:free), you can't vary id to create distinct configurations. And duplicate id values within the same provider collide on the internal key.

Real-world impact

Can't define multiple reasoning profiles for the same model:

// BROKEN: duplicate id within provider
{ "id": "qwen/qwen3.6-plus-preview:free", "name": "Low reasoning" },
{ "id": "qwen/qwen3.6-plus-preview:free", "name": "High reasoning" }

Current workarounds (all awkward):

  • Create duplicate providers with different names but the same baseUrl and credentials and name them like OpenRouter-Low (for your low-reasoning models) or OpenRouter-High (for your high-reasoning models) - works, but awkward and departs from the clean provider/model schema for viewing and selecting models per provider
  • Use agents.defaults.models[key].params.thinking — but only one value per model key, and configured in a separate location from the model entry

Broader pattern

This same id overloading is the root cause behind a cluster of open issues:

CategoryIssuesSymptom
Bare id collisions#49355, #35476, #50651, #29007Wrong context window, broken UI selection, failed failover when same model slug exists across providers
Provider prefix leaking to API#38256, #48876, #39253OC sends provider/id to APIs that only accept bare id, causing 404s
Multiple configs of same model#27988, #52646, #14494, #47852, #41594Can't vary reasoning level, temperature, context profile, or cache retention per-model

Proposed Solution

Use provider/name as the internal key

The name field already exists and is already required. Give it the functional role it should have:

FieldCurrent roleProposed role
idInternal key AND API model nameAPI model name only
nameDisplay label (no functional role)Internal key (must be unique per provider)

Example: before and after

Before (two providers needed for one model with two configs):

"ollama-cloud-thinking": {
  "models": [
    { "id": "minimax-m2.7", "name": "Minimax M2.7 Thinking", "reasoning": true }
  ]
},
"ollama-cloud": {
  "models": [
    { "id": "minimax-m2.7", "name": "Minimax M2.7", "reasoning": false }
  ]
}

After (one provider, two configs):

"ollama-cloud": {
  "models": [
    { "name": "minimax-m2.7",          "id": "minimax-m2.7", "reasoning": "off" },
    { "name": "minimax-m2.7-thinking", "id": "minimax-m2.7", "reasoning": "medium" }
  ]
}

Internal keys: ollama-cloud/minimax-m2.7 and ollama-cloud/minimax-m2.7-thinking. Both send {"model": "minimax-m2.7"} to the API.

OpenRouter example (same API model, different reasoning levels):

"openrouter": {
  "models": [
    { "name": "qwen36-minimal", "id": "qwen/qwen3.6-plus-preview:free", "reasoning": "minimal" },
    { "name": "qwen36-high",    "id": "qwen/qwen3.6-plus-preview:free", "reasoning": "high" }
  ]
}

Internal keys: openrouter/qwen36-minimal and openrouter/qwen36-high. Both send {"model": "qwen/qwen3.6-plus-preview:free"} to the API.

Secondary change: expand reasoning from boolean to level

Currently, the reasoning capability (reasoning: true/false) is on the model entry, but the reasoning level is configured separately in agents.defaults.models[key].params.thinking or the global thinkingDefault. This splits related configuration across two locations.

Expanding reasoning to accept level strings ("off", "minimal", "low", "medium", "high", "xhigh", "adaptive") in addition to true/false keeps backwards compatibility while allowing the reasoning level to travel with the model profile:

"reasoning": "high"     // new: specific level
"reasoning": true       // existing: model supports reasoning, use default level
"reasoning": false      // existing: model doesn't support reasoning

This eliminates the need for agents.defaults.models[key].params.thinking as a separate configuration surface.

Migration

Backwards compatibility

To avoid breaking existing configs:

  1. Resolution order: Look up by provider/name first. If not found, fall back to provider/id (current behavior). Emit a deprecation warning when the fallback is used.
  2. Auto-migration: When name equals id (the common case today), the internal key doesn't change. Only configs that define a name different from id would see new behavior.
  3. Deprecation timeline: Remove provider/id fallback in a future major version.

Scope of code changes

The internal key provider/id is used in:

  • Model selection and resolution (src/agents/model-selection.ts)
  • Context window cache (src/agents/context-window-cache.ts or equivalent)
  • UI model switcher and status display
  • agents.defaults.models key lookup
  • Session storage (activeModel field)
  • Fallback chain resolution
  • Agent model assignment (agents.list[].model)

Each location would need to switch from modelKey(provider, model.id) to modelKey(provider, model.name), with a fallback to model.id during the migration period.

Issues this fixes

With this change:

  • #49355, #35476, #50651: No bare-id collisions — internal key is always provider-qualified via name
  • #38256, #48876, #39253: No provider prefix leaking — id is never prefixed; it's always the raw API model name
  • #29007: Failover works — different providers' models have distinct keys even if they share an API model name
  • #27988: Multiple configs of same model — just use different names
  • #52646, #14494, #47852: Per-model reasoning/temperature/params — each profile is a separate named entry
  • #41594: Per-model context profiles — each named entry can carry its own context config

Alternatives considered

  1. Add apiModelId field: New optional field for the API model name; id stays as internal key. Works but adds a field when the existing name field is already available.
  2. Add key field: New optional field for the internal key; defaults to id. Less disruptive but adds complexity without cleaning up the underlying design.
  3. Add variant suffix: Allow id + variant to form a composite key. More targeted but invents a new concept instead of using existing fields.

All three work, but option 1-3 add new fields. The provider/name approach uses what's already there.

extent analysis

TL;DR

Use provider/name as the internal key instead of provider/id to decouple the internal key from the API model ID.

Guidance

  • Update the internal key to use provider/name in model selection, context window cache, UI model switcher, and other relevant locations.
  • Implement a fallback to provider/id during the migration period to maintain backwards compatibility.
  • Expand the reasoning field to accept level strings in addition to boolean values to simplify configuration.
  • Review and update all locations using the internal key, including src/agents/model-selection.ts, src/agents/context-window-cache.ts, and UI components.

Example

"ollama-cloud": {
  "models": [
    { "name": "minimax-m2.7",          "id": "minimax-m2.7", "reasoning": "off" },
    { "name": "minimax-m2.7-thinking", "id": "minimax-m2.7", "reasoning": "medium" }
  ]
}

Notes

The proposed solution requires updates to multiple locations in the codebase, and a fallback mechanism is necessary to ensure backwards compatibility during the migration period.

Recommendation

Apply the proposed workaround by using provider/name as the internal key, as it utilizes existing fields and simplifies the configuration.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix feat(models): use provider/name as internal key to decouple from API model ID [1 participants]