Skip to content

fix(agent): steer on non-string tool content only as anthropic blocks, coerce for chat-completions (#78598) - #78706

Open
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix-78598-work
Open

fix(agent): steer on non-string tool content only as anthropic blocks, coerce for chat-completions (#78598)#78706
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix-78598-work

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Summary

Fixes /steer on tool results with non-string content (e.g. multimodal block lists). apply_pending_steer_to_tool_results() in agent/agent_runtime_helpers.py unconditionally converted non-string tool content into an Anthropic Messages API block list. For chat-completions providers (OpenRouter, Xiaomi, DeepSeek, DeepInfra — routed via /chat/completions), ChatCompletionToolMessage.content must be a plain string; the block list is rejected with HTTP 400 and the poisoned message stays in the history, causing a 400 loop on every subsequent call including fallbacks.

Root Cause

if not isinstance(existing_content, str):
    blocks = list(existing_content) if existing_content else []
    blocks.append({"type": "text", "text": marker.lstrip()})
    messages[target_idx]["content"] = blocks

The block-list shape is only valid for the Anthropic Messages API wire format. The function never consulted the active wire format (agent.api_mode) before choosing the content shape.

Change

  • agent/agent_runtime_helpers.py:
    • apply_pending_steer_to_tool_results() now only builds the block-list shape when agent.api_mode == "anthropic_messages" (previous behavior preserved for Anthropic).
    • For any other wire format, non-string tool content is coerced to a plain string BEFORE appending the steer marker, via a new module-level helper _coerce_tool_content_to_string() (JSON serialization with str() fallback, matching the pattern already used elsewhere in the codebase).
  • tests/run_agent/test_steer.py:
    • test_multimodal_content_list_preserved now pins api_mode="anthropic_messages" (block-list behavior is Anthropic-only).
    • New test_chat_completions_coerces_non_string_content_to_plain_string — chat-completions mode + block-list content produces a plain string with the marker appended after the serialized content.
    • New test_string_content_shape_unchanged_in_anthropic_mode — string content stays a string with the marker in both wire formats.

Verification

  • python3 -m pytest tests/run_agent/test_steer.py -q — 29 passed.
  • python3 -m pytest tests/ -k "steer or tool_result" -q — 154 passed, 3 skipped, 0 failures.

Closes

Closes #78598

@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 sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 4, 2026
This was referenced Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /steer on a tool result with non-string content produces invalid content for chat-completions providers (400 loop)

2 participants