feat: A2A (Agent-to-Agent) protocol support#3562
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4cf9d4c728
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| _LAZY_EXPORTS: dict[str, tuple[str, tuple[str, str] | None]] = { | ||
| "A2AClientTool": ("._client_tool", ("a2a-sdk", "a2a")), | ||
| "generate_agent_card": ("._agent_card", ("a2a-sdk", "a2a")), |
There was a problem hiding this comment.
Export the server executor from the extension
The package-level docs advertise from agents.extensions.a2a import A2AServerAgent, but _LAZY_EXPORTS only recognizes the client/converter/card helpers. In any server setup using that documented import path, __getattr__ rejects A2AServerAgent and the server-side half of this extension is not accessible from the public module even though _server_executor.py defines it.
Useful? React with 👍 / 👎.
| if supported_interfaces is None: | ||
| supported_interfaces = [ | ||
| AgentInterface(url=url), | ||
| ] |
There was a problem hiding this comment.
Include the default protocol binding in generated cards
When callers rely on the default supported_interfaces, the generated AgentInterface only contains the URL even though the function documents this as a JSON-RPC interface. A2A clients use the interface's protocol binding/version for transport negotiation, so cards produced by generate_agent_card() can be rejected or fail negotiation unless the caller manually supplies interfaces.
Useful? React with 👍 / 👎.
| task: Task | None = None | ||
| task_id: str | None = None | ||
|
|
||
| timeout = timeout_seconds if timeout_seconds is not None else 300.0 |
There was a problem hiding this comment.
Honor
timeout_seconds=None instead of timing out
The dataclass docs say timeout_seconds=None disables the timeout, but _send_and_wait() replaces None with 300.0 before calling asyncio.wait_for. Callers who opt out for long-running A2A tasks will still have the remote task canceled after five minutes, so this path should pass None through or skip wait_for when no timeout is requested.
Useful? React with 👍 / 👎.
| if task.status.state == TaskState.TASK_STATE_CANCELED: | ||
| raise ModelBehaviorError( | ||
| f"A2A agent '{self.tool_name}' task was canceled." | ||
| ) | ||
|
|
||
| return task |
There was a problem hiding this comment.
Treat rejected A2A tasks as tool errors
TASK_STATE_REJECTED is included as a terminal state, so _send_and_wait() stops as soon as the remote agent rejects the request, but the post-processing only raises for failed or canceled tasks. In that rejected-task scenario the code returns the task and _extract_task_result() turns the rejection/status text into a successful tool output, which masks a remote refusal as a normal result.
Useful? React with 👍 / 👎.
|
Hi @seratch @rm-openai, friendly ping for review when you get a chance. This PR adds bidirectional A2A protocol support as discussed in #472. A few notes:
Happy to iterate on the design based on your feedback. Thanks! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5c4b6e138
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| status=status, | ||
| final=False, # type: ignore[call-arg] | ||
| ) | ||
| await event_queue.enqueue(event) |
There was a problem hiding this comment.
Publish A2A events through the queue API
When A2AServerAgent runs under the current a2a-sdk request handlers, the producer queue passed to executors exposes enqueue_event(...) (the a2a.server.events.event_queue docs describe only that method), not enqueue(...). The first working-status publish therefore raises AttributeError, the error path then tries the same invalid call for the failed task, and the server executor cannot return any A2A response. Use the SDK queue method consistently here and in _publish_task.
Useful? React with 👍 / 👎.
a5c4b6e to
78fce13
Compare
|
This PR was closed due to a force push that updated the branch. The changes are now available in #3567 with the same branch and Codex review fixes applied. |
Summary
Add bidirectional A2A (Agent-to-Agent) protocol integration for the OpenAI Agents SDK, enabling interoperability between OpenAI agents and any A2A-compatible agent built with any framework/language.
Closes #472.
What is included
🔄 Bidirectional Message Converter (
_converter.py)Message/Part/Task/Artifact↔ OpenAITResponseInputItem/RunResult/StreamEvent📞 A2A Client Tool (
_client_tool.py).well-known/agent-card.json🖥️ A2A Server Agent (
_server_executor.py)AgentExecutorinterfacecontext_id📋 AgentCard Generator (
_agent_card.py)Design decisions
a2a-sdkas optional dependency ([a2a]extra)__getattr__frozensetfor terminal statesTests
Breaking changes
None. This is a purely additive extension under
agents.extensions.a2a.