Skip to content

fix(agentcore): parse A2A JSON-RPC responses in AgentCore provider - #24995

Merged
yuneng-berri merged 1 commit into
BerriAI:litellm_internal_dev_04_02_2026from
michelligabriele:fix/agentcore-a2a-response-parsing
Apr 2, 2026
Merged

fix(agentcore): parse A2A JSON-RPC responses in AgentCore provider#24995
yuneng-berri merged 1 commit into
BerriAI:litellm_internal_dev_04_02_2026from
michelligabriele:fix/agentcore-a2a-response-parsing

Conversation

@michelligabriele

Copy link
Copy Markdown
Contributor

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🐛 Bug Fix

Changes

Problem: _parse_json_response() in the AgentCore provider returns empty content for A2A agents. Strategy 1 matches A2A responses (because result is a dict), but _extract_content_from_message() looks for result["content"] which doesn't exist in A2A format — A2A agents return result["message"]["parts"] instead. This means message.get("content", []) returns [], producing an empty string.

Fix: Added Strategy 0 before the existing strategies that detects A2A JSON-RPC responses by checking for the "jsonrpc" key, then delegates to the existing extract_text_from_a2a_response() utility from litellm/llms/a2a/common_utils.py. This utility already handles 5 A2A response shapes (direct message, nested message, task with artifacts, task status, streaming artifact-update). If A2A extraction returns empty, it falls through to the existing strategies — zero regression risk.

Files modified:

  • litellm/llms/bedrock/chat/agentcore/transformation.py — added import + Strategy 0 in _parse_json_response()
  • tests/test_litellm/llms/bedrock/chat/agentcore/test_agentcore_transformation.py — 4 new tests

Tests added (all in TestAgentCoreJsonResponseParsing):

  • test_parse_json_a2a_jsonrpc_nested_messageresult.message.parts[] format (customer's exact case)
  • test_parse_json_a2a_jsonrpc_direct_partsresult.parts[] format
  • test_parse_json_a2a_jsonrpc_multi_parts — multiple text parts concatenated
  • test_parse_json_a2a_jsonrpc_empty_falls_through — empty A2A result falls through to Strategy 3

@vercel

vercel Bot commented Apr 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 2, 2026 1:25pm

Request Review

@codspeed-hq

codspeed-hq Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing michelligabriele:fix/agentcore-a2a-response-parsing (ca19d7b) with main (d1df4e8)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes empty responses from AgentCore when the underlying agent is an A2A (Agent-to-Agent Protocol) agent. The root cause was that A2A agents return a {"jsonrpc": "2.0", "result": {"message": {"parts": [...]}}} structure, which matched Strategy 1's result-is-dict check, but then _extract_content_from_message() looked for result["content"] — a key that doesn't exist in A2A format — yielding an empty string.

Fix summary:

  • Adds "Strategy 0" in _parse_json_response(): detects a "jsonrpc" key and delegates immediately to the already-existing extract_text_from_a2a_response() utility (which handles five A2A response shapes).
  • If extraction returns empty, execution falls through to the existing strategies, preserving all pre-existing behavior.
  • Adds the import of extract_text_from_a2a_response from litellm/llms/a2a/common_utils.py (no new utility code added).

Test coverage:

  • 4 new mock-only unit tests covering nested-message format, direct-parts format, multi-part concatenation, and the empty-result fallthrough path.

Confidence Score: 5/5

  • Safe to merge — the fix is additive and isolated, with correct fallthrough semantics preserving all pre-existing behavior.
  • The only finding is a minor import ordering style issue (P2). The logic itself is correct: Strategy 0 fires only on jsonrpc-keyed responses and cleanly falls through when extraction yields empty, leaving Strategies 1-4 fully intact. Tests are pure mocks and cover the four distinct structural variants plus the fallthrough edge case.
  • No files require special attention.

Important Files Changed

Filename Overview
litellm/llms/bedrock/chat/agentcore/transformation.py Adds Strategy 0 for A2A JSON-RPC detection in _parse_json_response(); delegates to existing extract_text_from_a2a_response() utility; falls through cleanly to existing strategies if extraction yields empty. Minor import ordering issue (new import placed mid-block between two bedrock imports rather than alphabetically before them).
tests/test_litellm/llms/bedrock/chat/agentcore/test_agentcore_transformation.py Adds 4 pure-mock unit tests covering the new A2A Strategy 0: nested message, direct parts, multi-part concatenation, and empty-result fallthrough. No network calls, all assertions are correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[_parse_json_response called] --> B{Is dict?}
    B -- No --> Z[Strategy 4: return raw JSON string]
    B -- Yes --> C{jsonrpc key present? Strategy 0 NEW}
    C -- Yes --> D[extract_text_from_a2a_response]
    D --> E{content non-empty?}
    E -- Yes --> F[Return A2A content]
    E -- No --> G{result is dict? Strategy 1}
    C -- No --> G
    G -- Yes --> H[_extract_content_from_message - return with final_message]
    G -- No --> I{response is list? Strategy 2}
    I -- Yes --> J[_extract_content_from_message - return content]
    I -- No --> K{result or response is string? Strategy 3}
    K -- Yes --> L[Return string value]
    K -- No --> Z
Loading

Reviews (1): Last reviewed commit: "fix(agentcore): parse A2A JSON-RPC respo..." | Re-trigger Greptile

from litellm.litellm_core_utils.streaming_handler import CustomStreamWrapper
from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException
from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
from litellm.llms.a2a.common_utils import extract_text_from_a2a_response

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.

P2 Import placed out of alphabetical order

The new a2a import is inserted between two litellm.llms.bedrock.* imports. Following the existing file's alphabetical grouping (a2a < base_llm < bedrock), it should appear before the base_llm import.

Suggested change
from litellm.llms.a2a.common_utils import extract_text_from_a2a_response
from litellm.llms.a2a.common_utils import extract_text_from_a2a_response
from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException
from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codecov

codecov Bot commented Apr 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@yuneng-berri
yuneng-berri changed the base branch from main to litellm_internal_dev_04_02_2026 April 2, 2026 22:14
@yuneng-berri
yuneng-berri merged commit f109dff into BerriAI:litellm_internal_dev_04_02_2026 Apr 2, 2026
59 of 62 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