Skip to content

fix(agent): use canonical assistant history format in subagent_runner#606

Merged
M3gA-Mind merged 1 commit intotinyhumansai:mainfrom
sanil-23:fix/subagent-runner-native-assistant-format
Apr 16, 2026
Merged

fix(agent): use canonical assistant history format in subagent_runner#606
M3gA-Mind merged 1 commit intotinyhumansai:mainfrom
sanil-23:fix/subagent-runner-native-assistant-format

Conversation

@sanil-23
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 commented Apr 16, 2026

Summary

  • Fixes subagent_runner's inner tool-call loop producing 400 errors from the backend on iteration 1+: "jinja template rendering failed. Message has tool role, but there was no previous assistant message with a tool call!"
  • Root cause: build_native_assistant_payload used "text" instead of "content" and nested {"type":"function","function":{...}} wrappers instead of flat {id, name, arguments} — two mismatches vs the format parse::build_native_assistant_history produces and the backend expects.
  • Fix: replace the broken inline copy with a direct call to parse::build_native_assistant_history (the same serialiser tool_loop.rs uses successfully for the orchestrator's tool calls). Dead function removed.

Problem

Every sub-agent that made a tool call and entered iteration 1+ hit a 400 from the backend. The backend's jinja template saw a role=tool message without a recognised assistant-with-tool-calls predecessor because the assistant message was formatted with the wrong keys and structure.

Introduced in PR #474 (6465f3d3, 2026-04-09). Latent since then because the orchestrator self-heals by retrying via other agents.

Solution

One-line fix in subagent_runner.rs: replace build_native_assistant_payload(&response_text, &native_calls) with super::parse::build_native_assistant_history(&response_text, &native_calls). Delete the dead function.

E2E verification

skills_agent with gmail toolkit progressed through iterations 0->1->2 successfully (previously died at iteration 1 with the 400 error every time). code_executor also confirmed working through iteration 0->1 on staging API.

Submission Checklist

  • Single file change, minimal diff
  • E2E verified on staging-api.tinyhumans.ai
  • No new dependencies

Impact

  • Unblocks ALL sub-agent multi-iteration tool calls (skills_agent, code_executor, researcher, etc.)
  • Zero risk to orchestrator path (it already uses parse::build_native_assistant_history via tool_loop.rs)

Related

Summary by CodeRabbit

  • Bug Fixes
    • Fixed tool call ID references in agent message serialization, ensuring proper communication between sequential tool interactions.

build_native_assistant_payload in subagent_runner.rs serialised the
assistant tool-call message using {"text": …, "tool_calls": [{
"type":"function","function":{…}}]} — two mismatches vs the format
parse::build_native_assistant_history produces and the backend
jinja template expects:

  1. "text" instead of "content" — the downstream convert_messages
     parser looks for "content" to detect assistant-with-tool-calls
     messages; "text" is not recognised, so the message is treated
     as a plain assistant message.
  2. Nested {"type":"function","function":{"name":…,"arguments":…}}
     wrappers instead of flat {"id":…,"name":…,"arguments":…}.

Result: every sub-agent that made a tool call and entered iteration
1+ hit a 400 from the backend: "jinja template rendering failed.
Message has tool role, but there was no previous assistant message
with a tool call!" — the backend saw a role=tool message without a
recognised assistant-with-tool-calls predecessor.

The fix replaces the broken inline copy with a direct call to
parse::build_native_assistant_history (the same serialiser
tool_loop.rs uses successfully for the orchestrator's tool calls).
The dead build_native_assistant_payload function is removed.

Introduced in PR tinyhumansai#474 (6465f3d, 2026-04-09). Latent since then
because the orchestrator self-heals by retrying via other agents.

E2E verified: skills_agent with gmail toolkit now progresses through
iterations 0→1→2 successfully (previously died at iteration 1 with
the 400 error every time).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

The assistant message serialization in the run_inner_loop function was updated to use the shared parse::build_native_assistant_history function instead of a duplicate local build_native_assistant_payload function. This corrects the serialized message structure for native tool calls and eliminates redundant code.

Changes

Cohort / File(s) Summary
Assistant Message Serialization
src/openhuman/agent/harness/subagent_runner.rs
Replaced local build_native_assistant_payload call with super::parse::build_native_assistant_history to fix the serialized structure of assistant messages carrying native calls, enabling tool messages to correctly reference tool call IDs. Removes 18 lines of duplicate serialization logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • senamakel

Poem

🐰 A payload once broken, now fixed with care,
Where native calls dance through the serialized air,
One function, not two, keeps the logic so clean,
The tool references flow—oh, what a scene!
Simplicity wins when duplication takes flight! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(agent): use canonical assistant history format in subagent_runner' is specific and directly reflects the main change—replacing an inline serializer with the canonical format function.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/openhuman/agent/harness/subagent_runner.rs (1)

739-740: Add a regression assertion for assistant payload shape in multi-iteration tool-call tests.

Current tests confirm role=tool presence, but not that the preceding assistant message is serialized with content + flat tool_calls[{id,name,arguments}] (the actual bug vector).

Proposed test hardening
@@
     let second_call_messages = &captured[1].messages;
     let has_tool_msg = second_call_messages.iter().any(|m| m.role == "tool");
     assert!(
         has_tool_msg,
         "second provider call should include role=tool message"
     );
+
+    let assistant_msg = second_call_messages
+        .iter()
+        .find(|m| m.role == "assistant")
+        .expect("assistant tool-call history message should be present");
+    let payload: serde_json::Value = serde_json::from_str(&assistant_msg.content)
+        .expect("assistant tool-call history should be valid JSON");
+    assert!(payload.get("content").is_some(), "missing `content` field");
+    assert!(payload.get("tool_calls").is_some(), "missing `tool_calls` field");
+    assert_eq!(payload["tool_calls"][0]["id"], "call-1");
+    assert_eq!(payload["tool_calls"][0]["name"], "file_read");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/agent/harness/subagent_runner.rs` around lines 739 - 740, Add a
regression assertion in the multi-iteration tool-call tests to validate the
assistant message payload shape produced by
super::parse::build_native_assistant_history: after building history (history
variable) and pushing ChatMessage::assistant(assistant_history_content), assert
that the assistant message serializes to an object with a top-level "content"
field plus a flat "tool_calls" array where each entry has {id, name, arguments}
(matching native_calls and response_text). Update the test that exercises
build_native_assistant_history / ChatMessage::assistant to deserialize/inspect
the assistant_history_content and explicitly assert presence and shape of
"content" and that tool_calls is a flat list with each element containing id,
name, and arguments to prevent regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/openhuman/agent/harness/subagent_runner.rs`:
- Around line 739-740: Add a regression assertion in the multi-iteration
tool-call tests to validate the assistant message payload shape produced by
super::parse::build_native_assistant_history: after building history (history
variable) and pushing ChatMessage::assistant(assistant_history_content), assert
that the assistant message serializes to an object with a top-level "content"
field plus a flat "tool_calls" array where each entry has {id, name, arguments}
(matching native_calls and response_text). Update the test that exercises
build_native_assistant_history / ChatMessage::assistant to deserialize/inspect
the assistant_history_content and explicitly assert presence and shape of
"content" and that tool_calls is a flat list with each element containing id,
name, and arguments to prevent regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ed9eb8ae-2354-42bb-bb8a-b55f7ce81833

📥 Commits

Reviewing files that changed from the base of the PR and between fd213d4 and c563bd4.

📒 Files selected for processing (1)
  • src/openhuman/agent/harness/subagent_runner.rs

@M3gA-Mind M3gA-Mind merged commit a78506f into tinyhumansai:main Apr 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants