mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
fix: Fix logging issues with RemoteA2aAgent
Recent change to the updated A2A Client SDK broke the logging utilities. This updates those logging utilities to work with the new A2A SDK structure. PiperOrigin-RevId: 806482017
This commit is contained in:
committed by
Copybara-Service
parent
bb148002f8
commit
0c1f1fadeb
@@ -20,11 +20,10 @@ import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
from a2a.client import ClientEvent as A2AClientEvent
|
||||
from a2a.types import DataPart as A2ADataPart
|
||||
from a2a.types import Message as A2AMessage
|
||||
from a2a.types import Part as A2APart
|
||||
from a2a.types import SendMessageRequest
|
||||
from a2a.types import SendMessageResponse
|
||||
from a2a.types import Task as A2ATask
|
||||
from a2a.types import TextPart as A2ATextPart
|
||||
except ImportError as e:
|
||||
@@ -49,6 +48,16 @@ def _is_a2a_task(obj) -> bool:
|
||||
return type(obj).__name__ == "Task" and hasattr(obj, "status")
|
||||
|
||||
|
||||
def _is_a2a_client_event(obj) -> bool:
|
||||
"""Check if an object is an A2A Client Event (Task, UpdateEvent) tuple."""
|
||||
try:
|
||||
return isinstance(obj, tuple) and _is_a2a_task(obj[0])
|
||||
except (TypeError, AttributeError):
|
||||
return (
|
||||
hasattr(obj, "__getitem__") and len(obj) == 2 and _is_a2a_task(obj[0])
|
||||
)
|
||||
|
||||
|
||||
def _is_a2a_message(obj) -> bool:
|
||||
"""Check if an object is an A2A Message, with fallback for isinstance issues."""
|
||||
try:
|
||||
@@ -114,7 +123,7 @@ def build_message_part_log(part: A2APart) -> str:
|
||||
return part_content
|
||||
|
||||
|
||||
def build_a2a_request_log(req: SendMessageRequest) -> str:
|
||||
def build_a2a_request_log(req: A2AMessage) -> str:
|
||||
"""Builds a structured log representation of an A2A request.
|
||||
|
||||
Args:
|
||||
@@ -125,100 +134,70 @@ def build_a2a_request_log(req: SendMessageRequest) -> str:
|
||||
"""
|
||||
# Message parts logs
|
||||
message_parts_logs = []
|
||||
if req.params.message.parts:
|
||||
for i, part in enumerate(req.params.message.parts):
|
||||
if req.parts:
|
||||
for i, part in enumerate(req.parts):
|
||||
part_log = build_message_part_log(part)
|
||||
# Replace any internal newlines with indented newlines to maintain formatting
|
||||
part_log_formatted = part_log.replace("\n", "\n ")
|
||||
message_parts_logs.append(f"Part {i}: {part_log_formatted}")
|
||||
|
||||
# Configuration logs
|
||||
config_log = "None"
|
||||
if req.params.configuration:
|
||||
config_data = {
|
||||
"accepted_output_modes": req.params.configuration.accepted_output_modes,
|
||||
"blocking": req.params.configuration.blocking,
|
||||
"history_length": req.params.configuration.history_length,
|
||||
"push_notification_config": bool(
|
||||
req.params.configuration.push_notification_config
|
||||
),
|
||||
}
|
||||
config_log = json.dumps(config_data, indent=2)
|
||||
|
||||
# Build message metadata section
|
||||
message_metadata_section = ""
|
||||
if req.params.message.metadata:
|
||||
if req.metadata:
|
||||
message_metadata_section = f"""
|
||||
Metadata:
|
||||
{json.dumps(req.params.message.metadata, indent=2).replace(chr(10), chr(10) + ' ')}"""
|
||||
{json.dumps(req.metadata, indent=2).replace(chr(10), chr(10) + ' ')}"""
|
||||
|
||||
# Build optional sections
|
||||
optional_sections = []
|
||||
|
||||
if req.params.metadata:
|
||||
if req.metadata:
|
||||
optional_sections.append(
|
||||
f"""-----------------------------------------------------------
|
||||
Metadata:
|
||||
{json.dumps(req.params.metadata, indent=2)}"""
|
||||
{json.dumps(req.metadata, indent=2)}"""
|
||||
)
|
||||
|
||||
optional_sections_str = _NEW_LINE.join(optional_sections)
|
||||
|
||||
return f"""
|
||||
A2A Request:
|
||||
-----------------------------------------------------------
|
||||
Request ID: {req.id}
|
||||
Method: {req.method}
|
||||
JSON-RPC: {req.jsonrpc}
|
||||
A2A Send Message Request:
|
||||
-----------------------------------------------------------
|
||||
Message:
|
||||
ID: {req.params.message.message_id}
|
||||
Role: {req.params.message.role}
|
||||
Task ID: {req.params.message.task_id}
|
||||
Context ID: {req.params.message.context_id}{message_metadata_section}
|
||||
ID: {req.message_id}
|
||||
Role: {req.role}
|
||||
Task ID: {req.task_id}
|
||||
Context ID: {req.context_id}{message_metadata_section}
|
||||
-----------------------------------------------------------
|
||||
Message Parts:
|
||||
{_NEW_LINE.join(message_parts_logs) if message_parts_logs else "No parts"}
|
||||
-----------------------------------------------------------
|
||||
Configuration:
|
||||
{config_log}
|
||||
{optional_sections_str}
|
||||
-----------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
def build_a2a_response_log(resp: SendMessageResponse) -> str:
|
||||
def build_a2a_response_log(resp: A2AClientEvent | A2AMessage) -> str:
|
||||
"""Builds a structured log representation of an A2A response.
|
||||
|
||||
Args:
|
||||
resp: The A2A SendMessageResponse to log.
|
||||
resp: The A2A SendMessage Response to log.
|
||||
|
||||
Returns:
|
||||
A formatted string representation of the response.
|
||||
"""
|
||||
# Handle error responses
|
||||
if hasattr(resp.root, "error"):
|
||||
return f"""
|
||||
A2A Response:
|
||||
-----------------------------------------------------------
|
||||
Type: ERROR
|
||||
Error Code: {resp.root.error.code}
|
||||
Error Message: {resp.root.error.message}
|
||||
Error Data: {json.dumps(resp.root.error.data, indent=2) if resp.root.error.data else "None"}
|
||||
-----------------------------------------------------------
|
||||
Response ID: {resp.root.id}
|
||||
JSON-RPC: {resp.root.jsonrpc}
|
||||
-----------------------------------------------------------
|
||||
"""
|
||||
|
||||
# Handle success responses
|
||||
result = resp.root.result
|
||||
result = resp
|
||||
result_type = type(result).__name__
|
||||
if result_type == "tuple":
|
||||
result_type = "ClientEvent"
|
||||
|
||||
# Build result details based on type
|
||||
result_details = []
|
||||
|
||||
if _is_a2a_task(result):
|
||||
if _is_a2a_client_event(result):
|
||||
result = result[0]
|
||||
result_details.extend([
|
||||
f"Task ID: {result.id}",
|
||||
f"Context ID: {result.context_id}",
|
||||
@@ -342,7 +321,4 @@ Status Message:
|
||||
History:
|
||||
{history_section}
|
||||
-----------------------------------------------------------
|
||||
Response ID: {resp.root.id}
|
||||
JSON-RPC: {resp.root.jsonrpc}
|
||||
-----------------------------------------------------------
|
||||
"""
|
||||
|
||||
@@ -492,9 +492,16 @@ class RemoteA2aAgent(BaseAgent):
|
||||
event.custom_metadata[A2A_METADATA_PREFIX + "request"] = (
|
||||
a2a_request.model_dump(exclude_none=True, by_alias=True)
|
||||
)
|
||||
event.custom_metadata[A2A_METADATA_PREFIX + "response"] = (
|
||||
a2a_response.model_dump(exclude_none=True, by_alias=True)
|
||||
)
|
||||
# If the response is a ClientEvent, record the task state, otherwise
|
||||
# record the message object.
|
||||
if isinstance(a2a_response, tuple):
|
||||
event.custom_metadata[A2A_METADATA_PREFIX + "response"] = (
|
||||
a2a_response[0].model_dump(exclude_none=True, by_alias=True)
|
||||
)
|
||||
else:
|
||||
event.custom_metadata[A2A_METADATA_PREFIX + "response"] = (
|
||||
a2a_response.model_dump(exclude_none=True, by_alias=True)
|
||||
)
|
||||
|
||||
yield event
|
||||
|
||||
|
||||
Reference in New Issue
Block a user