Skip to content

fix: convert OpenAI-format tool messages in OAuth path#354

Merged
bicced merged 2 commits intomainfrom
worktree-fix-failover-logging
Mar 6, 2026
Merged

fix: convert OpenAI-format tool messages in OAuth path#354
bicced merged 2 commits intomainfrom
worktree-fix-failover-logging

Conversation

@bicced
Copy link
Contributor

@bicced bicced commented Mar 6, 2026

Summary

  • Root cause: The OAuth path (_oauth_chat / _oauth_chat_stream) bypasses LiteLLM and calls the Anthropic Messages API directly. Unlike LiteLLM, _build_anthropic_body was not converting OpenAI-format tool messages (role: "tool", assistant tool_calls) to Anthropic format (role: "user" with tool_result blocks, assistant with tool_use content blocks). When an agent's chat history contained tool results from previous turns, Anthropic rejected them with HTTP 400: Unexpected role "tool".
  • Failover improvements: Record failures for previously-silent OAuth error paths (connection/timeout, 401), add 529 to transient codes, include error details in failover warning logs.

Test plan

  • New tests for tool message conversion (single + merged consecutive results)
  • All 1690 existing tests pass
  • Deploy and verify heartbeat no longer triggers 400 errors

bicced added 2 commits March 6, 2026 20:46
… path

The OAuth path bypasses LiteLLM and calls the Anthropic Messages API
directly via httpx. Unlike LiteLLM, it was not converting OpenAI-format
tool messages (role:"tool", assistant tool_calls) to Anthropic format
(role:"user" with tool_result blocks, assistant with tool_use blocks).

When an agent's chat history contained tool results from previous turns,
the heartbeat would trigger a new chat turn, and the OAuth path would
send the raw OpenAI-format messages to Anthropic — which rejects them
with HTTP 400: "Unexpected role 'tool'".

Changes:
- _build_anthropic_body now converts assistant tool_calls to tool_use
  content blocks and role:"tool" messages to role:"user" with
  tool_result blocks, merging consecutive tool results
- _oauth_chat records failures for connection/timeout and 401 errors
  (previously silent — health tracker was blind to these)
- _oauth_chat_stream records failures for 401 and non-success HTTP
  responses (previously only caught streaming parse exceptions)
- Failover warning log now includes error_type and status_code in the
  message text (previously only in extra dict, invisible in log output)
- Added 529 (Anthropic overloaded) to transient status codes for proper
  exponential backoff instead of flat 60s default cooldown
…utive user roles

_hard_prune drops middle message groups, which can leave the first group
(user) adjacent to a kept group that also starts with user — creating
consecutive same-role messages. The Anthropic API rejects these with 400.

_summarize_compact already handled this with a bridge message; now
_hard_prune does the same.
@bicced bicced merged commit c8eb0f4 into main Mar 6, 2026
3 checks passed
bicced added a commit that referenced this pull request Mar 6, 2026
PR #354 landed the same OAuth tool message conversion inline.
Remove our extracted _convert_messages_to_anthropic() (now dead code)
and keep origin/main's tests.
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.

1 participant