-
Notifications
You must be signed in to change notification settings - Fork 333
Open
Description
What happened?
Steps to reproduce
- Install
a2a-sdk[http-server](tested with v0.3.22) anduvicorn(tested with v0.40.0) using Python 3.12:
uv init -p 3.12
uv add "a2a-sdk[http-server]" uvicorn- Paste the following code in
main.py
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.apps import A2ARESTFastAPIApplication, A2AStarletteApplication
from a2a.server.events import EventQueue
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
from a2a.types import (
AgentCapabilities,
AgentCard,
AgentInterface,
AgentSkill,
TransportProtocol,
UnsupportedOperationError,
)
from a2a.utils import new_agent_text_message, new_task
from a2a.utils.errors import ServerError
import uvicorn
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
class TheAgentExecutor(AgentExecutor):
async def execute(
self,
context: RequestContext,
event_queue: EventQueue,
) -> None:
task = context.current_task
if not task:
task = new_task(context.message) # type: ignore
await event_queue.enqueue_event(task)
context_id = context.context_id or task.context_id
updater = TaskUpdater(event_queue, task.id, context_id)
await updater.reject(message=new_agent_text_message("I don't want to work"))
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
raise ServerError(error=UnsupportedOperationError())
def create_server(transport_protocol: TransportProtocol):
capabilities = AgentCapabilities(streaming=True, push_notifications=False)
skill = AgentSkill(
id="convert_currency",
name="Currency Exchange Rates Tool",
description="Helps with exchange values between various currencies",
tags=["currency conversion", "currency exchange"],
examples=["What is exchange rate between USD and GBP?"],
)
url = "http://localhost:10000"
agent_card = AgentCard(
name="Currency Agent",
description="Helps with exchange rates for currencies",
url=url,
version="1.0.0",
default_input_modes=SUPPORTED_CONTENT_TYPES,
default_output_modes=SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
preferred_transport=transport_protocol,
additional_interfaces=[AgentInterface(transport=transport_protocol, url=url)],
)
request_handler = DefaultRequestHandler(
agent_executor=TheAgentExecutor(),
task_store=InMemoryTaskStore(),
)
match transport_protocol:
case TransportProtocol.http_json:
return A2ARESTFastAPIApplication(
agent_card=agent_card, http_handler=request_handler
)
case TransportProtocol.jsonrpc:
return A2AStarletteApplication(
agent_card=agent_card, http_handler=request_handler
)
case _:
raise NotImplementedError(
f"Unsupported server transport protocol: {transport_protocol}"
)
server = create_server(transport_protocol=TransportProtocol.http_json)
if __name__ == "__main__":
uvicorn.run(server.build(), host="0.0.0.0", port=10000)- Run it:
uv run main.py - Open a terminal and send a message to the server:
curl --request POST \
--url http://127.0.0.1:10000/v1/message:send \
--header 'content-type: application/json' \
--data '{
"message": {
"messageId": "c52d542b-06cb-4e9a-b36e-15ee6cb7eee4",
"role": "ROLE_USER",
"content": [
{
"text": "Exchange USD"
}
]
}
}'- Wait a few seconds then get the task, replacing
<TASK_ID>with the task ID you got inresponse to the send message request:
curl --request GET \
--url http://127.0.0.1:10000/v1/tasks/<TASK_ID>Actual Output
The Task response is not valid. It contains a status field with no state sub-field.
Example (IDs might be different for you, but the shape should be the same):
{
"id": "dd8b8e5b-1831-422c-a8a0-46560ef59e59",
"contextId": "d076a8f4-e1a2-4a25-8516-3e853433e8bb",
"status": {
"message": {
"messageId": "c7e0d4aa-8a8d-4e31-bafe-e57e19585803",
"role": "ROLE_AGENT",
"content": [
{
"text": "I don't want to work"
}
]
}
},
"history": [
{
"messageId": "c52d542b-06cb-4e9a-b36e-15ee6cb7eee4",
"contextId": "d076a8f4-e1a2-4a25-8516-3e853433e8bb",
"taskId": "dd8b8e5b-1831-422c-a8a0-46560ef59e59",
"role": "ROLE_USER",
"content": [
{
"text": "Exchange USD"
}
],
"metadata": {}
}
]
}Expected Output
The response should be a task object that contains a status field. Within status, there should be a state field with a valid TaskState value. According to the A2A specs, state is a required field: https://a2a-protocol.org/dev/specification/#412-taskstatus
Other notes
- If you change the server code to use
TransportProtocol.jsonrpc, thestatefield appears as expected. - If you change
TheAgentExecutorto complete the task instead of rejecting it, thestatefield appears as expected: i.e. replace
await updater.reject(message=new_agent_text_message("I don't want to work"))with
await updater.complete(message=new_agent_text_message("I don't want to work"))Relevant log output
Code of Conduct
- I agree to follow this project's Code of Conduct
Metadata
Metadata
Assignees
Labels
No labels