Route reasoning models to reasoning-aware provider clients so reasoning_content round-trips #101
Labels
No labels
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
#106 feat(llm): route reasoning models to reasoning-aware provider clients
cleveragents/cleveractors-core
Reference
cleveragents/cleveractors-core#101
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Metadata
feat(llm): route reasoning models to reasoning-aware provider clientsfeature/m2-reasoning-model-provider-supportBackground and context
The Actor Configuration Standard (§1.2) deliberately excludes "the internal
mechanics of LLM providers" from its scope, and ADR-2028 (Extended Provider
Routing) routes every non-native provider — including
openai_compatibleandevery named additional provider — through a bare
langchain_openai.ChatOpenAI(base_url=..., api_key=...)client(
cleveractors.agents.llm_client._build_from_credentials, at commit882336c).langchain-openai(currently 1.4.1) explicitly targets the official OpenAIAPI specification only. Its own module docstring states that non-standard
response fields added by third-party providers — specifically
reasoning_content— are not extracted or preserved, and recommends aprovider-specific subclass (e.g.
openai_compatible_reasoning) when pointingbase_urlat areasoning-capable provider.
As a result, when an
llmagent is pointed at a reasoning / "thinking"model through an OpenAI-compatible endpoint, the model's
reasoning_contentis silently dropped on both the response leg (
_convert_dict_to_message) andthe request leg (
_convert_message_to_dict, which only ever emitsrole/content/tool_calls). The multi-turn tool-call loop(
cleveractors.agents.llm.LLMAgent._execute_tool_loop) then replays thereturned
AIMessage— now stripped of its reasoning — back to the provider onthe follow-up call after a tool result. Providers that require the reasoning
block to be echoed back reject the request.
This issue tracks Option A of the diagnosis: route reasoning models to a
reasoning-aware provider client so
reasoning_contentround-trips correctly.Current behavior
Running an actor whose
llmagent usesprovider: openai_compatiblewith areasoning model (e.g.
deepseek-v4-flash-free) behind a reasoning-capableproxy fails on the first post-tool-call round with:
Confirmed diagnostics:
reasoning_contentappears in no logged LLM request or response — only inthe error — proving it is dropped end-to-end.
400, so per ADR-2032 D-8 it is notretried; it aborts the actor-graph execution and recurs on every attempt.
Expected behavior
llmagent configured with a reasoning / thinking model behind anOpenAI-compatible (or provider-specific) endpoint completes a multi-turn
tool-call loop without the
reasoning_content ... must be passed back400 error.
reasoning_content, it is preserved on the resultingAIMessageand re-sent on the assistant turn that precedes the subsequenttool result.
openai,anthropic,google) and fornon-reasoning models is unchanged.
Acceptance criteria
llmagent using a reasoning model through the reasoning-awarerouting path completes at least one tool-call → tool-result → follow-up
round with no
invalid_request_erroraboutreasoning_content.LLMAgent._execute_tool_loopafter a tool call includes thereasoning_contentreturned by the model (verified via a fake reasoningmodel in a Behave scenario).
request payloads to those produced before this change (no regression).
routing decision, and
docs/specification.mdis updated accordinglybefore implementation lands.
nox(all default sessions) is green andnox -s coverage_reportreports ≥ 97%.
Supporting information
882336c):cleveractors.agents.llm_client._build_from_credentials— routes everynon-native provider to
ChatOpenAI(base_url=...).cleveractors.agents.llm.LLMAgent._execute_tool_loop— appends thereturned
AIMessage(reasoning already stripped) before re-invoking.langchain-openai1.4.1BaseChatOpenAIdocstring — documents thatreasoning_contentis not extracted; recommendsChatDeepSeek/provider-specific subclasses.
work extends), ADR-2032 (retry scope — explains why the 400 is not
retried).
written and approved before implementation (ADR process). If the maintainer
prefers strict one-commit atomicity, this issue may be promoted to an Epic
with the ADR split into its own child issue. It also still needs to be linked
to an appropriate LLM/provider parent Epic during triage (mandatory-parent
rule).
Subtasks
docs/adr/ADR-NNNN-reasoning-aware-provider-routing.md)extending ADR-2028; submit for review; on approval update
docs/specification.md.(
langchain-deepseek/ChatDeepSeek) topyproject.toml.cleveractors.agents.llm_clientto select thereasoning-aware client for reasoning models / configured providers,
preserving the
reasoning_contentround-trip while leaving native andnon-reasoning paths unchanged.
LLMAgent._execute_tool_loopre-sendsreasoning_content(ensure
additional_kwargscarrying reasoning survive the replay).reasoning_contentis preserved acrossa tool-call turn using a fake reasoning model in
features/mocks/.through the new routing path.
nox -s coverage_report.nox(all default sessions) and fix any errors.Definition of Done
This issue is complete when:
Metadata exactly.
master, reviewed, and merged.