Merge pull request #2184 from Molecule-AI/fix/jsonrpc-routes-rpc-url
fix: pass rpc_url='/' to create_jsonrpc_routes (a2a-sdk 1.x)
This commit is contained in:
commit
9532890f04
@ -71,7 +71,7 @@ async def delegate(target_id: str, task: str, async_mode: bool = False):
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"messageId": str(uuid.uuid4()),
|
||||
"parts": [{"kind": "text", "text": task}],
|
||||
}
|
||||
@ -108,7 +108,7 @@ async def delegate(target_id: str, task: str, async_mode: bool = False):
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"messageId": str(uuid.uuid4()),
|
||||
"parts": [{"kind": "text", "text": task}],
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ async def send_a2a_message(target_url: str, message: str) -> str:
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"messageId": str(uuid.uuid4()),
|
||||
"parts": [{"kind": "text", "text": message}],
|
||||
}
|
||||
|
||||
@ -446,20 +446,32 @@ class LangGraphA2AExecutor(AgentExecutor):
|
||||
# making the earlier `Part(text=text)` call (line ~358, inside
|
||||
# the astream_events loop) raise UnboundLocalError because
|
||||
# the local binding is not yet in scope at that point.
|
||||
from a2a.types import FilePart, FileWithUri, Message, Role, TextPart
|
||||
_parts: list[Part] = [Part(root=TextPart(text=final_text))] if final_text else []
|
||||
#
|
||||
# a2a-sdk 1.x flattened the Part shape: 0.x used
|
||||
# `Part(root=TextPart(text=...))` / `Part(root=FilePart(file=
|
||||
# FileWithUri(uri=..., name=..., mimeType=...)))` (Pydantic
|
||||
# discriminated-union style). 1.x's Part is a single proto
|
||||
# message with flat fields: text, url, filename, media_type,
|
||||
# raw, data, metadata. TextPart/FilePart/FileWithUri were
|
||||
# removed. Same for Message: messageId/taskId/contextId
|
||||
# camelCase became message_id/task_id/context_id.
|
||||
from a2a.types import Message, Role
|
||||
_parts: list[Part] = [Part(text=final_text)] if final_text else []
|
||||
for f in _outbound:
|
||||
_parts.append(Part(root=FilePart(file=FileWithUri(
|
||||
uri="workspace:" + f["path"],
|
||||
name=f["name"],
|
||||
mimeType=f["mime_type"],
|
||||
))))
|
||||
_parts.append(Part(
|
||||
url="workspace:" + f["path"],
|
||||
filename=f["name"],
|
||||
media_type=f["mime_type"],
|
||||
))
|
||||
msg = Message(
|
||||
messageId=uuid.uuid4().hex,
|
||||
role=Role.agent,
|
||||
message_id=uuid.uuid4().hex,
|
||||
# 1.x Role is a protobuf enum: ROLE_UNSPECIFIED,
|
||||
# ROLE_USER, ROLE_AGENT. Old `Role.agent` (Pydantic
|
||||
# lowercase enum) doesn't exist anymore.
|
||||
role=Role.ROLE_AGENT,
|
||||
parts=_parts,
|
||||
taskId=task_id,
|
||||
contextId=context_id,
|
||||
task_id=task_id,
|
||||
context_id=context_id,
|
||||
)
|
||||
else:
|
||||
msg = new_text_message(final_text, task_id=task_id, context_id=context_id)
|
||||
|
||||
@ -36,7 +36,7 @@ from typing import TYPE_CHECKING, Any
|
||||
|
||||
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
||||
from a2a.server.events import EventQueue
|
||||
from a2a.helpers import new_agent_text_message
|
||||
from a2a.helpers import new_text_message
|
||||
|
||||
from adapter_base import AdapterConfig, BaseAdapter
|
||||
|
||||
@ -191,7 +191,7 @@ class GoogleADKA2AExecutor(AgentExecutor):
|
||||
if not user_text:
|
||||
parts = getattr(getattr(context, "message", None), "parts", None)
|
||||
logger.warning("GoogleADKA2AExecutor: no text in message parts: %s", parts)
|
||||
await event_queue.enqueue_event(new_agent_text_message(_NO_TEXT_MSG))
|
||||
await event_queue.enqueue_event(new_text_message(_NO_TEXT_MSG))
|
||||
return
|
||||
|
||||
session_id = getattr(context, "context_id", None) or "default-session"
|
||||
@ -223,7 +223,7 @@ class GoogleADKA2AExecutor(AgentExecutor):
|
||||
response_parts.append(text)
|
||||
|
||||
final_text = "".join(response_parts).strip() or _NO_RESPONSE_MSG
|
||||
await event_queue.enqueue_event(new_agent_text_message(final_text))
|
||||
await event_queue.enqueue_event(new_text_message(final_text))
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
@ -234,7 +234,7 @@ class GoogleADKA2AExecutor(AgentExecutor):
|
||||
)
|
||||
# Mirror sanitize_agent_error() convention: expose class name only.
|
||||
await event_queue.enqueue_event(
|
||||
new_agent_text_message(f"Agent error: {type(exc).__name__}")
|
||||
new_text_message(f"Agent error: {type(exc).__name__}")
|
||||
)
|
||||
|
||||
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
||||
|
||||
@ -57,7 +57,7 @@ async def delegate_task(workspace_id: str, task: str) -> str:
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"messageId": str(uuid.uuid4()),
|
||||
"parts": [{"kind": "text", "text": task}],
|
||||
},
|
||||
|
||||
@ -269,7 +269,7 @@ async def _execute_delegation(task_id: str, workspace_id: str, task: str):
|
||||
"id": f"delegation-{task_id}-{attempt}",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"parts": [{"kind": "text", "text": task}],
|
||||
"messageId": f"msg-{task_id}-{attempt}",
|
||||
},
|
||||
|
||||
@ -375,7 +375,7 @@ class HeartbeatLoop:
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"parts": [{"type": "text", "text": trigger_msg}],
|
||||
},
|
||||
},
|
||||
|
||||
@ -242,10 +242,20 @@ async def main(): # pragma: no cover
|
||||
agent_card=agent_card,
|
||||
)
|
||||
|
||||
# v1: replace A2AStarletteApplication with Starlette route factory
|
||||
# v1: replace A2AStarletteApplication with Starlette route factory.
|
||||
# rpc_url is required in a2a-sdk 1.x (was implicit at root in 0.x).
|
||||
# Use '/' to match a2a.utils.constants.DEFAULT_RPC_URL — that's also
|
||||
# what the platform's a2a_proxy.go POSTs to (it forwards to the
|
||||
# workspace's URL without appending a path). Card endpoint stays at
|
||||
# the well-known path /.well-known/agent-card.json (handled by
|
||||
# create_agent_card_routes default).
|
||||
routes = []
|
||||
routes.extend(create_agent_card_routes(agent_card))
|
||||
routes.extend(create_jsonrpc_routes(request_handler=handler))
|
||||
# enable_v0_3_compat=True so any external 0.3.x A2A client (still using
|
||||
# `"role": "user"` lowercase + camelCase Pydantic field names) can talk
|
||||
# to us without re-deploying. Internally our outbound payloads are now
|
||||
# 1.x-shaped (ROLE_USER), but inbound is opt-in compatible.
|
||||
routes.extend(create_jsonrpc_routes(request_handler=handler, rpc_url="/", enable_v0_3_compat=True))
|
||||
app = Starlette(routes=routes)
|
||||
|
||||
# 8. Register with platform
|
||||
@ -451,7 +461,7 @@ async def main(): # pragma: no cover
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"messageId": f"initial-{_uuid.uuid4().hex[:8]}",
|
||||
"parts": [{"kind": "text", "text": config.initial_prompt}],
|
||||
},
|
||||
@ -550,7 +560,7 @@ async def main(): # pragma: no cover
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"role": "ROLE_USER",
|
||||
"messageId": f"idle-{_uuid.uuid4().hex[:8]}",
|
||||
"parts": [{"kind": "text", "text": config.idle_prompt}],
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user