Skip to content

fix(agent): honour disabled reasoning on the profile-less chat_completions path - #66114

Closed
laurinaitis wants to merge 1 commit into
NousResearch:mainfrom
laurinaitis:fix/reasoning-disabled-legacy-path
Closed

laurinaitis wants to merge 1 commit into
NousResearch:mainfrom
laurinaitis:fix/reasoning-disabled-legacy-path

Conversation

@laurinaitis

Copy link
Copy Markdown
Contributor

What

ChatCompletionsTransport.build_kwargs hardcodes reasoning: {"enabled": True, "effort": ...} for
reasoning-capable routes that resolve no provider profile, so a caller passing {"enabled": False} gets
reasoning back on at medium effort.

Why

5a6720b88 fixed thinking-off at the reporting site and in the zai profile. The transport branch at
agent/transports/chat_completions.py:481 was not part of that change and still overrides an explicit
disable.

The branch is reached when a reasoning-capable base_url resolves no profile. _supports_reasoning_extra_body
(run_agent.py:5425) admits Nous Portal, GitHub Models, LM Studio, Ollama and OpenRouter; profile lookup is
keyed on the provider string, which is optional (agent.provider = provider_name or "",
agent/agent_init.py:436), and get_provider_profile("") returns None. So an OpenRouter or Nous Portal
base_url configured without a provider name lands here. AIAgent's own docstring (agent/agent_init.py:382)
documents reasoning_config as an OpenRouter override that can be set to disable, which is the case that
breaks.

Reproduced on 7cb2d2cd4:

t.build_kwargs(model="gpt-4o", messages=[...], supports_reasoning=True,
               reasoning_config={"enabled": False})
# extra_body["reasoning"] -> {'enabled': True, 'effort': 'medium'}

Every profiled sibling already honours the disable: nous omits the field
(plugins/model-providers/nous/__init__.py:35), openrouter forwards the config
(plugins/model-providers/openrouter/__init__.py:158), and the Kimi branch twenty lines up maps disabled to
thinking: {"type": "disabled"} (chat_completions.py:462). Each defaults to medium only in the absence of
an explicit config. The legacy branch applies that default to explicit configs too, including an explicit off.

For the wire value I followed agent/auxiliary_client.py:6632, which emits {"enabled": False} for the same
disabled-and-no-profile-reasoning case. Nous is the exception: it rejects enabled: false, so the field is
omitted there, which is what the nous profile does.

The origin looks like an oversight rather than intent. f69a33794 ("fix: forward reasoning_effort for custom
providers (GLM-5.2 on ARK)") rewrote this branch to stop hardcoding medium effort and honour the caller's
effort; it did not carry the enabled flag through.

I did not find an open issue for this, so the reproduction above stands in for one.

Testing

Three contracts in tests/agent/transports/test_chat_completions.py, alongside the existing
test_nous_omits_disabled_reasoning:

  • test_disabled_reasoning_honored_without_profile: disabled stays disabled.
  • test_disabled_reasoning_omitted_for_nous_without_profile: Nous omits the field.
  • test_requested_effort_preserved_without_profile: an explicit effort survives the new branch.

The first two fail on stock main and pass with the fix. The third passes either way and pins the
else-branch this change restructures.

tests/agent/transports/test_chat_completions.py: 93 passed. The wider tests/agent/transports run has the
same 52 pre-existing failures before and after (unrelated codex and bedrock suites, missing deps in my env),
plus the 3 new tests.

Assertions are on the request kwargs. I have not driven a live profile-less endpoint.

…tions path

build_kwargs hardcoded reasoning {"enabled": True} for OpenAI-compatible
routes without a provider profile, so a caller that disabled reasoning got it
back on at medium effort. 5a6720b fixed the same class at the reporting site
and in the zai profile; this transport branch was missed.

Nous rejects enabled=false, so omit the field there rather than send it,
matching the nous profile's own handling.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openrouter OpenRouter aggregator provider/nous Nous Research API (OAuth) area/config Config system, migrations, profiles needs-decision Awaiting maintainer decision before any implementation sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 17, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Related to #51940. Both repair profile-less reasoning disable handling, but #51940 omits the reasoning field whereas this PR forwards enabled:false except for Nous; maintainer direction is needed on the provider wire contract.

@laurinaitis

Copy link
Copy Markdown
Contributor Author

#51940 should carry this. It's three weeks older, it's tied to #51903, and the sweeper has already reviewed it. Closing this one. My body said I found no open issue: true only because #51903 is closed, and I never searched PRs at all, which is how I missed the cluster.

On the wire contract I don't think the in-tree evidence lands cleanly either way, so alt-glitch's ask for direction seems right to me. The hosts that reach this branch disagree. Nous rejects enabled: false, so the nous profile omits (plugins/model-providers/nous/__init__.py:35). OpenRouter is the opposite: its profile forwards the config (:158), and the comment above it says why. {enabled: false} is what makes OpenRouter emit thinking: {type: "disabled"}, while omitting leaves the model on its adaptive default (#42991, #43432). Upstream's own profile-less path sends {"enabled": False} at agent/auxiliary_client.py:6632. I've put the detail on #51940 rather than here.

My version also leaves the GitHub Models sub-branch unguarded at the transport level. run_agent.py:5563 already returns None there when reasoning is disabled, so it isn't reachable in production, but #51940's guard covers it at the transport too and that's the better shape.

Rebase notes are on #51940.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Comment

Honours disabled reasoning on profile-less chat_completions path (39 additions / 0 deletions). Very small, targeted fix.

No security issues, no debug artifacts, no hardcoded secrets. Looks clean.

Note: 0 deletions may indicate this is an addition-only path extension rather than a fix of existing code.


Reviewed by Hermes Agent (cron batch 2026-07-17)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists provider/nous Nous Research API (OAuth) provider/openrouter OpenRouter aggregator sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants