fix: emit reasoning_effort for custom OpenAI-compatible providers - #90057
LeonardoLGDS wants to merge 1 commit into
Conversation
Otherwise clean: identity pass-through without inventing values, deliberate omission over defaults, no — reviewer-a · automated agent review (Hermes week-review) |
|
I posted a repro on #90031 that I think affects the premise here, so flagging it rather than letting this merge quietly. Two things: 1. Plain 2. Restricting to Also worth weighing #72649 alongside this: it argues an unconditional top-level The gap that does still look real is #72202 — the named None of this is a knock on the implementation; the red-run-plus-sabotage-check discipline is better than most of what I read. I just don't think the root cause it's built on holds on current builds. |
|
Thanks @cvaisnor for the repro and detailed check. You are correct that in Since the main pain point was the named profile drop (#72202) and that is better addressed in the provider resolution chain, I'll close this in favor of keeping the existing profile emit intact. |
Why
Issue #90031: for custom OpenAI-compatible providers (
provider: custom, e.g. a local llama.cpp server viabase_url),reasoning_effortwas silently dropped. Custom providers do not advertisesupports_reasoning, so the genericextra_body.reasoningarm never fires, and the server falls back to its model default (usually no reasoning or default effort), ignoring what the user configured.What
Added a custom-scoped arm in
ChatCompletionsTransport.build_kwargs()(agent/transports/chat_completions.py), after the LM Studio arm, that emits top-levelreasoning_effortfor custom providers only when the user explicitly set an effort (config.yamlagent.reasoning_effortor a session/reasoningoverride; both arrive resolved in the samereasoning_configslot). Levels are passed through identically forlow/medium/high; unset, disabled (reasoning_effort: false), or levels outside the OpenAI wire set (xhigh,ultra, etc.) omit the field instead of inventing a value. No new config keys, no env vars, nosupports_reasoningside effect, noextra_bodyusage. The existing Kimi / TokenHub / LM Studio arms and the genericextra_body.reasoningarm are untouched.xhighand friends remain reachable for users who need them via the existingextra_bodyroute; the new arm deliberately stays within the OpenAIlow|medium|highwire set.Verification
Tests-first in
tests/agent/transports/test_chat_completions.py(new classTestChatCompletionsCustomProviderReasoning):1 failed, 51 deselectedwithKeyError: 'reasoning_effort'on the first emission test.8 passed(targeted),59 passed(full transport file).1 failed, same missing-key error), restored byte-identically, both runs green again (8 passed,59 passed).59 passed in 2.57s.Covered by the tests: custom+medium emits top-level; low/high identity pass-through (covers the session override slot, runtime beats config); unset omits (no
reasoning_effort, noextra_body.reasoning); disabled omits; unsupported level omits; messages list byte-identical with the branch firing and not firing; negative: Kimi takes its own arm with no double emission.Scope notes
This is complementary to #83566 (Ollama gate fix in
run_agent.py, which does not coverprovider: customendpoints) and rebases cleanly either way. #87413 (reasoning_format surface) is still unmerged; if that design lands, the per-vendor arms are the natural seam to fold into it.One known residual: the summary/compaction path in agent/chat_completion_helpers.py calls
chat.completions.create()directly and mirrors only the LM Studio top-level emission and thesupports_reasoningextra_body shape. For custom providers it never emittedreasoning_effortbefore this change and still does not; extending it is a separate, larger change and is deliberately out of scope here. Unknown-field residual risk on strict OpenAI-compatible gateways is unchanged from the existing Kimi/TokenHub top-level arms (the field is part of the OpenAI Chat Completions schema).Fixes #90031
Refs #83566
Refs #87413