Skip to content

refactor(anthropic): extract 7 helpers from convert_messages_to_anthropic (rebased #23968)#27784

Closed
kshitijk4poor wants to merge 1 commit into
NousResearch:mainfrom
kshitijk4poor:refactor/anthropic-convert-messages
Closed

refactor(anthropic): extract 7 helpers from convert_messages_to_anthropic (rebased #23968)#27784
kshitijk4poor wants to merge 1 commit into
NousResearch:mainfrom
kshitijk4poor:refactor/anthropic-convert-messages

Conversation

@kshitijk4poor

@kshitijk4poor kshitijk4poor commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Refactor of convert_messages_to_anthropic in agent/anthropic_adapter.py — the largest cyclomatic-complexity hotspot in the file (originally 185 statements, 90 branches, C=79). Extracts 7 single-responsibility helpers, trims net −4 LOC.

Cherry-pick of #23968 rebased onto current origin/main. Cherry-pick was clean — agent/anthropic_adapter.py had 4 commits land since the original branch point but none touched convert_messages_to_anthropic or its sibling helpers, so no conflict resolution was needed.

Closes part of #23972 (Phase 2 tracker). The 4 codex_responses_adapter refactor PRs from that tracker (#23951/#23952/#23954/#23957) were closed today because their LOC accounting was net positive — this PR is the lone Phase 2 win that drops both LOC and max complexity.

Honest accounting

Metric Before After Δ
LOC in anthropic_adapter.py n/a n/a −4
convert_messages_to_anthropic cyclomatic 79 <10 (passes) −69
File-wide max cyclomatic 79 18 −61 (77% drop)
Max statements per function 185 ~70 −115
ruff C901/PLR0912/PLR0915 violations in file 3 8 +5 (regression by raw count)

The violation-count regression is real and worth calling out: splitting one C=79 function into 1 helper-at-<10 + 4 helpers at C=12/13/15/18 makes ruff flag MORE individual functions even though the max complexity drops 77%. By rule-count alone, this is a regression. By the actual goal of the C901 rule (find untestable monolithic functions), it's a clear win — each of the 4 new helpers is short enough to read in one screen and individually testable.

The original PR description in #23968 overclaimed "79→10". The honest number is "79→18 max, spread across 4 still-complex helpers".

What changed structurally

convert_messages_to_anthropic previously did all of the following in one function:

  1. Walk messages, dispatch by role
  2. Convert tool-result messages into Anthropic tool_result blocks
  3. Strip orphaned tool_use blocks (when the agent loop aborted mid-tool-call)
  4. Merge consecutive same-role messages
  5. Validate and re-issue Anthropic thinking signatures
  6. Evict old computer-use screenshots (keep most recent 3)
  7. Build the final assembled message list

After: top-level walks/dispatches, 7 extracted helpers handle (2)-(6). Helper signatures:

_convert_assistant_message(m) → dict                   # <C=10
_convert_tool_message_to_result(result, m) → None      # C=12, mutates result
_convert_user_message(content) → dict                  # <C=10
_strip_orphaned_tool_blocks(result) → None             # C=15, mutates result
_merge_consecutive_roles(result) → list                # C=13, returns new list
_manage_thinking_signatures(result, base_url, model)   # C=18, mutates result
_evict_old_screenshots(result) → None                  # <C=10, mutates result

Each helper takes its inputs explicitly. The four mutating helpers (_convert_tool_message_to_result, _strip_orphaned_tool_blocks, _manage_thinking_signatures, _evict_old_screenshots) update result in place — the original code did the same inline, so semantics are preserved. _merge_consecutive_roles returns a new list; the caller rebinds result = _merge_consecutive_roles(result).

Test plan

  • ✅ Cherry-pick clean (git cherry-pick b8e47ab16 no conflicts)
  • tests/agent/test_anthropic_adapter.py: 136 pass, 14 fail
  • ✅ Of the 14 failing tests, all 14 are pre-existing TestResolveAnthropicToken* / TestResolveWithRefresh / TestRunOauthSetupToken OAuth-credential tests that fail identically on origin/main (verified against current HEAD). None touch convert_messages_to_anthropic.
  • ✅ The 53 conversion-specific tests filtered by -k "convert or message or strip or merge or thinking or tool_result or orphan or signature or screenshot or evict": 53/53 pass.
  • ✅ Broader tests/agent/ -k anthropic: 296 passed, 14 failed (same 14 OAuth tests, no other regressions).
  • py_compile.compile clean on anthropic_adapter.py
  • ruff check clean on anthropic_adapter.py (only the pre-existing complexity warnings remain, which are exactly what this PR addresses)

Worth shipping

Yes — net-negative LOC + 77% reduction in max-function complexity + each helper individually testable. The raw violation-count regression is a quirk of how ruff counts (one function above threshold becomes four functions above threshold even though they're each meaningfully smaller). The original 185-statement function was the kind of thing the C901 rule exists to catch; now it isn't.

@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change comp/agent Core agent loop, run_agent.py, prompt builder provider/anthropic Anthropic native Messages API P3 Low — cosmetic, nice to have labels May 18, 2026
Split convert_messages_to_anthropic (complexity 79) into 7 focused helpers:

- _convert_assistant_message    — assistant msg to content blocks
- _convert_tool_message_to_result — tool msg to tool_result + merge
- _convert_user_message         — user msg validation + conversion
- _strip_orphaned_tool_blocks   — orphan tool_use + tool_result removal
- _merge_consecutive_roles      — role alternation enforcement
- _manage_thinking_signatures   — strip/preserve/downgrade by endpoint
- _evict_old_screenshots        — keep only 3 most recent images

Main function complexity: 79 → 10 (below C901 threshold).
Zero logic changes — pure extraction. Net -4 lines (refactor itself);
+45/-17 follow-up polish for annotation tightening (List[Dict] →
List[Dict[str, Any]]), restored rationale comments in
_manage_thinking_signatures (third-party endpoint examples, NousResearch#13848/NousResearch#16748
issue refs, redacted_thinking 'data'-as-signature note), and "Mutates
``result`` in place." docstring lines on the four mutating helpers.
@kshitijk4poor kshitijk4poor force-pushed the refactor/anthropic-convert-messages branch from 65c4e3d to 9b322b5 Compare May 18, 2026 06:23
@kshitijk4poor kshitijk4poor changed the title refactor(anthropic): extract 6 helpers from convert_messages_to_anthropic (rebased #23968) refactor(anthropic): extract 7 helpers from convert_messages_to_anthropic (rebased #23968) May 18, 2026
@austinpickett austinpickett requested a review from Copilot May 18, 2026 15:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Pure-extraction refactor of convert_messages_to_anthropic in agent/anthropic_adapter.py, splitting a single ~185-statement / C=79 function into seven single-responsibility helpers. Logic is preserved; only structure changes.

Changes:

  • Extract _convert_assistant_message, _convert_tool_message_to_result, _convert_user_message, _strip_orphaned_tool_blocks, _merge_consecutive_roles, _manage_thinking_signatures, _evict_old_screenshots.
  • Reduce convert_messages_to_anthropic to a top-level dispatch loop followed by sequential helper calls.
  • Net −4 LOC; max cyclomatic complexity in the file drops from 79 to 18.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@teknium1

Copy link
Copy Markdown
Contributor

Merged via #30386 (9d61408). Your commit was cherry-picked onto current main with authorship preserved (kshitijk4poor as Author, Teknium as Committer per project convention). One conflict in _manage_thinking_signatures resolved in favor of your version — the docstring wording already covered the recent Azure Foundry / Bedrock cases better than the inline comments on main. The 3 recent commits that landed on anthropic_adapter.py since your branch point (Azure Entra, MiniMax beta scoping, Azure Bearer auth) all live outside convert_messages_to_anthropic and merged cleanly. Targeted tests 365/365 pass. Thanks for the refactor — file-wide max complexity 79 → 18 is a solid win.

Gpapas pushed a commit to Gpapas/hermes-agent that referenced this pull request May 23, 2026
Mucky010 pushed a commit to Mucky010/hermes-agent that referenced this pull request May 24, 2026
exosyphon pushed a commit to exosyphon/hermes-agent that referenced this pull request May 24, 2026
mathias3 pushed a commit to mathias3/hermes-agent that referenced this pull request May 28, 2026
Bryce-huang pushed a commit to wbkunlun/hermes-agent that referenced this pull request May 29, 2026
mosaiq-systems pushed a commit to mosaiq-systems/hermes-agent that referenced this pull request May 29, 2026
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P3 Low — cosmetic, nice to have provider/anthropic Anthropic native Messages API type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants