Skip to content

feat(agents): derive a provider-declared default utility model when unset#103769

Merged
steipete merged 4 commits into
mainfrom
claude/utility-model-defaults
Jul 10, 2026
Merged

feat(agents): derive a provider-declared default utility model when unset#103769
steipete merged 4 commits into
mainfrom
claude/utility-model-defaults

Conversation

@steipete

Copy link
Copy Markdown
Contributor

Closes #103768. Follow-up to #103463 / #103461.

What Problem This Solves

utilityModel (titles, progress narration) is opt-in config, so most installs pay primary-model prices for internal one-shots and never get narration. We already know from the agent's primary provider which vendor the user can call — and each major vendor ships a tiny model for exactly this.

Why This Change Was Made

  • Provider-declared defaults, no ids in core. New manifest field modelCatalog.providers.<id>.defaultUtilityModel (typed + normalized in @openclaw/model-catalog-core), mirroring the mediaUnderstandingProviderMetadata.defaultModels pattern. Seeded: OpenAI → gpt-5.6-luna (cheapest current-gen), Anthropic → claude-haiku-4-5. Google deferred (its catalog lives in plugin TS, not the manifest providers record).
  • Derivation follows the primary provider (src/agents/utility-model.ts): unset utilityModel resolves the primary provider's declared default via the process-current plugin metadata snapshot — synchronous, cached, no probing or extra model calls; auth is inherent because the agent already runs on that provider. Explicit utilityModel wins; utilityModel: "" (agent- or defaults-level) disables utility routing entirely (titles → primary, narration off).
  • Narration auto-activates when a default resolves (attachProgressNarratorToReplyOptions now gates on the shared resolver) and still never falls back to the primary model.
  • Transport fix (live-test finding): the GPT-5.6 family rejects temperature with a 400 on the Responses API while 5.5/5.4 accept it — utility callers send temperature: 0.3, so the derived default would have failed every call. supportsOpenAITemperature (catalog compat.supportsTemperature override + family fallback, declared for all four gpt-5.6 manifest entries) now gates all three OpenAI payload builders (responses, shared, chatgpt-responses).

User Impact

  • OpenAI/Anthropic users get cheap titles and live progress narration with zero config; other providers keep exactly today's behavior (titles → primary, narration off) until they declare a default or set utilityModel.
  • New opt-out: utilityModel: "". Docs updated: docs/gateway/config-agents.md, docs/concepts/models.md, docs/concepts/progress-drafts.md, docs/plugins/manifest.md (new provider field).
  • Existing explicit utilityModel configs behave identically.

Evidence

  • Live proof on Testbox against real APIs: openai/gpt-5.6-luna completes with temperature requested-but-stripped (reproduced the 400 without the fix), openai/gpt-5.5 keeps temperature, anthropic/claude-haiku-4-5 completes with real auth (all stopReason=stop with sensible status text).
  • Testbox gates green at head: oxfmt, targeted oxlint, plugins:inventory:check, docs:map:check, check:import-cycles, check:test-types, and test files: src/agents/utility-model.test.ts (new), simple-completion-runtime.selection.test.ts, progress-narrator.test.ts, model-catalog-normalize.test.ts, openai-reasoning-effort.test.ts, openai-responses-shared.test.ts, test/scripts/plugin-sdk-surface-report.test.ts.
  • New tests: setting parsing (unset/auto vs "" disable vs explicit, agent-level override), provider-default derivation from injected snapshots, primary-provider derivation, empty-utility selection fallback, narration gating both directions, catalog normalization passthrough, temperature family/compat matrix, payload-builder temperature drop/keep.

steipete added 2 commits July 10, 2026 08:24
…nset

When agents.defaults.utilityModel is not set, utility tasks (titles, progress
narration) now use the primary provider's declared small model
(modelCatalog.providers.<id>.defaultUtilityModel: OpenAI -> gpt-5.6-luna,
Anthropic -> claude-haiku-4-5). Auth is inherent because derivation follows
the agent's primary provider. Setting utilityModel to an empty string
disables utility routing entirely; narration turns on automatically when a
default resolves and stays off otherwise.

Formatting/lint/tests verified on Testbox (oxfmt, oxlint, plugins:inventory,
docs:map, import-cycles, check:test-types, 4 test files).
The GPT-5.6 family 400s on temperature via the Responses API (live-verified:
gpt-5.6-luna/-terra reject it; gpt-5.5 and gpt-5.4-mini/nano accept it).
supportsOpenAITemperature gates all three OpenAI payload builders, with a
catalog compat override (compat.supportsTemperature) declared for the 5.6
family in the openai manifest. Without this, utility tasks (titles,
narration) would fail once gpt-5.6-luna becomes the derived default.

Gates on Testbox: oxfmt, oxlint, plugins:inventory, import-cycles,
check:test-types, 6 test files. Live proof on Testbox: gpt-5.6-luna and
claude-haiku-4-5 one-shot completions succeed with temperature requested.
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime agents Agent runtime and tooling extensions: anthropic extensions: openai size: M maintainer Maintainer-authored PR labels Jul 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9601834136

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const defaultUtilityModel = plugin.modelCatalog?.providers?.[provider]?.defaultUtilityModel;
const modelId = defaultUtilityModel?.trim();
if (modelId) {
return `${provider}/${modelId}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve auth profiles on auto utility models

When the primary model is profile-qualified (for example openai/gpt-5.4@work) and utilityModel is unset, this derives openai/gpt-5.6-luna without the @work suffix. resolveSimpleCompletionSelectionForAgent only extracts a profile from the returned ref, so generated titles/progress narration now use the default OpenAI credential, or fail if only the selected profile exists, instead of reusing the primary model's profile. Preserve the primary ref's auth profile when deriving the provider default.

Useful? React with 👍 / 👎.

Comment on lines +44 to +45
/** Whether the model accepts the temperature parameter (GPT-5.6 family rejects it). */
supportsTemperature?: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow temperature compat in user model config

This adds supportsTemperature to manifest model-catalog compat, but the strict user config compat schema still does not accept that key (src/config/zod-schema.core.ts's ModelCompatSchema has no supportsTemperature). For custom/OpenAI-compatible provider rows that need to disable temperature, or override the GPT-5.6 default, openclaw.json validation rejects the config before the request builders can use the new flag. Add the same field to the public model compat type and Zod schema.

Useful? React with 👍 / 👎.

…utility default

A primary like openai/gpt-5.5@work previously reached utility tasks via the
profiled fallback; the derived default shares the provider, so its trailing
auth profile carries over instead of silently switching to default
credentials (Codex review). Gates on Testbox: oxfmt, check:test-types, tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52498f7aef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

if (options?.temperature !== undefined) {
if (options?.temperature !== undefined && supportsOpenAITemperature(model)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate temperature in OpenClaw transport too

When an OpenAI Responses model is routed through OpenClaw's managed transport (for example configs using request.proxy, request.tls, or a local service; see src/agents/provider-transport-stream.ts:102), this new helper is bypassed and src/agents/openai-transport-stream.ts:2665 still always sends temperature. The new default utility model is openai/gpt-5.6-luna, and title/narration callers pass temperature: 0.3, so those proxied/transport-aware OpenAI setups still hit the GPT-5.6 400 that this change is trying to avoid; apply the same supportsOpenAITemperature check in the OpenClaw transport builders.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 10, 2026
@clawsweeper

clawsweeper Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 10, 2026, 11:49 AM ET / 15:49 UTC.

Summary
This PR derives an unset utility model from provider metadata, enables narration from that derived model, adds an empty-string opt-out, and suppresses unsupported OpenAI temperature parameters.

PR surface: Source +132, Tests +226, Docs +4. Total +362 across 24 files.

Reproducibility: yes. for the patch defects: direct source inspection shows the strict config contracts and managed Responses transport omit the new capability. The external GPT-5.6 rejection itself lacks inspectable live proof.

Review metrics: 2 noteworthy metrics.

  • Configuration defaults: 1 unset default changed. Existing OpenAI and Anthropic installations would begin automatic utility-model calls and progress narration.
  • Compatibility surfaces: 2 capability fields added. defaultUtilityModel and supportsTemperature must remain consistent across plugin metadata, public config, and every request transport.

Stored data model
Persistent data-model change detected: vector/embedding metadata: src/agents/utility-model.test.ts, vector/embedding metadata: src/agents/utility-model.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #103768
Summary: This PR is the explicit implementation candidate for the open smart-default utility-model issue; the earlier narration work is adjacent rather than duplicative.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🦐 gold shrimp
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Fix the strict config and managed transport capability paths with focused regression tests.
  • Attach redacted live Testbox evidence for both standard and transport-aware requests.
  • Record explicit maintainer acceptance of automatic unset behavior.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The body claims live Testbox calls but provides no inspectable redacted logs, terminal output, or linked artifact. Add passing-after output for standard and managed-transport paths, redact private data, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.

Risk before merge

  • [P1] Changing unset utilityModel from opt-in to automatic calls changes existing users' cost, content routing, and visible narration behavior.
  • [P2] The GPT-5.6 temperature premise is supported only by an unlinked live-test claim, so reviewers cannot verify the model-family fallback or affected request paths.
  • [P1] The branch is behind current main and needs an exact-head review refresh after the blockers are repaired.

Maintainer options:

  1. Complete contracts, then approve the default (recommended)
    Align public config and managed transport behavior, attach upgrade and live proof, and explicitly accept automatic calls for unset configuration.
  2. Preserve opt-in semantics
    Remove automatic defaulting while retaining the provider metadata seam for a later maintainer-approved mode.

Next step before merge

  • [P1] A maintainer must first accept the automatic default behavior; after that, the two line-level compatibility defects are narrow repairs and the contributor must attach real behavior proof.

Maintainer decision needed

  • Question: Should an unset utilityModel automatically trigger provider-selected utility calls and progress narration for existing OpenAI and Anthropic installations?
  • Rationale: This intentionally changes default cost, content routing, and user-visible behavior; implementation correctness alone cannot decide that policy.
  • Likely owner: steipete — Current-main history ties this person to the narration feature and the affected provider/config decision surfaces.
  • Options:
    • Adopt automatic defaults (recommended): Accept the new behavior after the transport and config defects are fixed and inspectable fresh-install, upgrade, and live API proof is attached.
    • Keep explicit opt-in: Preserve current unset semantics and expose provider defaults only through a separately approved automatic mode.

Security
Cleared: No dependency, workflow, permission, secret, download, publishing, or other supply-chain regression was introduced; the blockers are functional provider compatibility and product defaults.

Review findings

  • [P1] Gate temperature in the managed Responses transport — packages/ai/src/providers/openai-reasoning-effort.ts:143-152
  • [P2] Expose temperature compatibility in user model config — packages/model-catalog-core/src/model-catalog-types.ts:44-45
Review details

Best possible solution:

Use one public temperature-capability contract across every OpenAI Responses transport, preserve provider-owned utility defaults and auth profiles, and ship automatic unset behavior only after explicit acceptance plus inspectable fresh-install and upgrade proof.

Do we have a high-confidence way to reproduce the issue?

Yes for the patch defects: direct source inspection shows the strict config contracts and managed Responses transport omit the new capability. The external GPT-5.6 rejection itself lacks inspectable live proof.

Is this the best way to solve the issue?

No in its current form. Provider-owned metadata is the right boundary, but the implementation must cover the public config and managed transport siblings, and the changed default needs explicit product acceptance.

Full review comments:

  • [P1] Gate temperature in the managed Responses transport — packages/ai/src/providers/openai-reasoning-effort.ts:143-152
    The new helper is only wired into the package-level builders. buildOpenAIResponsesParams in src/agents/openai-transport-stream.ts still sends options.temperature unconditionally, and that path is selected for transport-aware setups such as request.proxy, request.tls, or local services. With gpt-5.6-luna as the automatic default, title and narration calls can still hit the 400 this patch intends to prevent. Apply the same capability check there and add a transport regression test.
    Confidence: 0.98
  • [P2] Expose temperature compatibility in user model config — packages/model-catalog-core/src/model-catalog-types.ts:44-45
    Adding supportsTemperature only to model-catalog metadata leaves the public ModelCompatConfig pick and strict ModelCompatSchema unchanged. Custom or OpenAI-compatible model rows therefore cannot disable temperature or override the family fallback because openclaw.json rejects the key. Add it to both public contracts and cover accepted configuration in the schema tests.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.97

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against e0df20b0b39e.

Label changes

Label changes:

  • add P2: This useful feature has definite but bounded compatibility blockers before merge.
  • add merge-risk: 🚨 compatibility: The PR changes unset configuration semantics and leaves the new model capability inconsistent across supported config and transport paths.
  • add merge-risk: 🚨 auth-provider: Automatic utility routing changes provider model and credential selection, and an affected managed transport path can fail the provider call.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The body claims live Testbox calls but provides no inspectable redacted logs, terminal output, or linked artifact. Add passing-after output for standard and managed-transport paths, redact private data, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.

Label justifications:

  • P2: This useful feature has definite but bounded compatibility blockers before merge.
  • merge-risk: 🚨 compatibility: The PR changes unset configuration semantics and leaves the new model capability inconsistent across supported config and transport paths.
  • merge-risk: 🚨 auth-provider: Automatic utility routing changes provider model and credential selection, and an affected managed transport path can fail the provider call.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The body claims live Testbox calls but provides no inspectable redacted logs, terminal output, or linked artifact. Add passing-after output for standard and managed-transport paths, redact private data, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +132, Tests +226, Docs +4. Total +362 across 24 files.

View PR surface stats
Area Files Added Removed Net
Source 14 164 32 +132
Tests 6 227 1 +226
Docs 4 13 9 +4
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 24 404 42 +362

What I checked:

  • Current-main baseline: Current main only attaches progress narration when an explicit utility model resolves; the PR changes this established unset behavior to provider-derived automatic selection. (src/auto-reply/reply/progress-narrator.ts:367, e0df20b0b39e)
  • Provider-derived default: The proposed resolver reads a provider-owned manifest default and preserves the primary model's trailing auth profile after the earlier review finding. (src/agents/utility-model.ts:71, 52498f7aef82)
  • Managed transport gap: The separate OpenClaw Responses payload builder still assigns temperature unconditionally, so request.proxy, request.tls, and local-service routes bypass the new capability check. (src/agents/openai-transport-stream.ts:2665, 52498f7aef82)
  • Strict config gap: The public user model compat type and strict Zod schema omit supportsTemperature, so custom provider/model configuration cannot use the new override without validation failure. (src/config/zod-schema.core.ts:221, 52498f7aef82)
  • Feature provenance: The narration behavior entered current main through the merged related PR, while blame and file history connect the affected narration, schema, and transport surfaces to recent work by the likely owners below. (src/auto-reply/reply/progress-narrator.ts:353, 26d200c6a303)
  • Codex contract check: Sibling Codex main does not send or define a temperature option in its Responses request path, so it neither covers OpenClaw's independent managed transport nor proves the proposed GPT-5.6 family rule. (../codex/codex-rs, 601b61a83ffe)

Likely related people:

  • steipete: Merged history and blame connect this person to the narration feature, strict model config schema, and recent OpenAI transport work. (role: feature owner and recent area contributor; confidence: high; commits: 26d200c6a303, dff4c634f1a5; files: src/auto-reply/reply/progress-narrator.ts, src/config/zod-schema.core.ts, src/agents/openai-transport-stream.ts)
  • vincentkoc: File history shows repeated transport and provider compatibility work in the central OpenAI transport module. (role: recent area contributor; confidence: medium; commits: cc1881a83846, d8458a1481e9, 5ddca5dd56e9; files: src/agents/openai-transport-stream.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete
steipete merged commit 97bbbc2 into main Jul 10, 2026
97 checks passed
@steipete
steipete deleted the claude/utility-model-defaults branch July 10, 2026 16:40
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 11, 2026
…nset (openclaw#103769)

* feat(agents): derive a provider-declared default utility model when unset

When agents.defaults.utilityModel is not set, utility tasks (titles, progress
narration) now use the primary provider's declared small model
(modelCatalog.providers.<id>.defaultUtilityModel: OpenAI -> gpt-5.6-luna,
Anthropic -> claude-haiku-4-5). Auth is inherent because derivation follows
the agent's primary provider. Setting utilityModel to an empty string
disables utility routing entirely; narration turns on automatically when a
default resolves and stays off otherwise.

Formatting/lint/tests verified on Testbox (oxfmt, oxlint, plugins:inventory,
docs:map, import-cycles, check:test-types, 4 test files).

* fix(ai): drop the temperature parameter for models that reject it

The GPT-5.6 family 400s on temperature via the Responses API (live-verified:
gpt-5.6-luna/-terra reject it; gpt-5.5 and gpt-5.4-mini/nano accept it).
supportsOpenAITemperature gates all three OpenAI payload builders, with a
catalog compat override (compat.supportsTemperature) declared for the 5.6
family in the openai manifest. Without this, utility tasks (titles,
narration) would fail once gpt-5.6-luna becomes the derived default.

Gates on Testbox: oxfmt, oxlint, plugins:inventory, import-cycles,
check:test-types, 6 test files. Live proof on Testbox: gpt-5.6-luna and
claude-haiku-4-5 one-shot completions succeed with temperature requested.

* fix(agents): carry the primary model's auth profile onto the derived utility default

A primary like openai/gpt-5.5@work previously reached utility tasks via the
profiled fallback; the derived default shares the provider, so its trailing
auth profile carries over instead of silently switching to default
credentials (Codex review). Gates on Testbox: oxfmt, check:test-types, tests.

* docs: realign the manifest provider-fields table after adding defaultUtilityModel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation extensions: anthropic extensions: openai gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Smart default utility model derived from the primary provider

1 participant