-
Notifications
You must be signed in to change notification settings - Fork 826
Python: feat: Add Agent Framework to A2A bridge support #2403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Python: feat: Add Agent Framework to A2A bridge support #2403
Conversation
- Implement A2A event adapter for converting agent messages to A2A protocol - Add A2A execution context for managing agent execution state - Implement A2A executor for running agents in A2A environment - Add comprehensive unit tests for event adapter, execution context, and executor - Update agent framework core A2A module exports and type stubs - Integrate thread management utilities for async execution - Add getting started sample for A2A agent framework integration - Update dependencies in uv.lock This integration enables agent framework agents to communicate and execute within the A2A (Agent to Agent) infrastructure.
…rage in A2A executor tests
- Reordered imports in various files for consistency and clarity. - Updated `__all__` definitions to maintain a consistent order across modules. - Simplified method signatures by removing unnecessary line breaks. - Enhanced readability by adjusting formatting in several sections. - Removed redundant comments and example scenarios in the execution context. - Improved handling of agent messages in the event adapter. - Added type hints for better clarity and type checking. - Cleaned up test cases for better organization and readability.
- Deleted the test file for A2aExecutionContext as it is no longer needed. - Updated A2aExecutor tests to remove dependencies on A2aExecutionContext and adjusted method calls accordingly. - Modified event adapter tests to use ChatMessage instead of AgentRunResponseUpdate. - Removed A2aExecutionContext from imports in agent_framework.a2a module and updated type hints accordingly.
eavanvalkenburg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea behind this, but there are some major issues with the implementation, especially the addition of the AgentThreadStore, please discuss that further with us.
| from agent_framework import ChatMessage, DataContent, Role, TextContent, UriContent | ||
|
|
||
|
|
||
| class A2aEventAdapter(ABC): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use python default styles for things like abbreviations, so:
| class A2aEventAdapter(ABC): | |
| class A2AEventAdapter(ABC): |
| pass | ||
|
|
||
|
|
||
| class BaseA2aEventAdapter(A2aEventAdapter): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| class BaseA2aEventAdapter(A2aEventAdapter): | |
| class BaseA2AEventAdapter(A2aEventAdapter): |
| if isinstance(content, TextContent): | ||
| parts.append(Part(root=TextPart(text=content.text))) | ||
| if isinstance(content, DataContent): | ||
| parts.append(Part(root=FilePart(file=FileWithBytes(bytes=_extract_base64_from_uri(content.uri))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a method on DataContent for this:
| parts.append(Part(root=FilePart(file=FileWithBytes(bytes=_extract_base64_from_uri(content.uri))))) | |
| parts.append(Part(root=FilePart(file=FileWithBytes(bytes=content.get_data_bytes()))) |
(there is also a get_data_bytes_as_str in case the data type needs to be the string representation
| # Handle URI content | ||
| parts.append( | ||
| Part( | ||
| root=FilePart(file=FileWithUri(uri=content.uri, mime_type=getattr(content, "media_type", None))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for getattr, UriContent always has media_type as a attribute.
| root=FilePart(file=FileWithUri(uri=content.uri, mime_type=getattr(content, "media_type", None))) | |
| root=FilePart(file=FileWithUri(uri=content.uri, mime_type=content.media_type)) |
| ) | ||
|
|
||
|
|
||
| def _extract_base64_from_uri(uri: str) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove this
| from ._a2a_event_adapter import A2aEventAdapter, BaseA2aEventAdapter | ||
|
|
||
|
|
||
| class A2aExecutor(AgentExecutor): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as before:
| class A2aExecutor(AgentExecutor): | |
| class A2AExecutor(AgentExecutor): |
| Example: | ||
| Demonstrating the internal processing: | ||
| ```python |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure code comments are marked appropriately, with .. code-block:: python. This applies in a number of places.
| from agent_framework import ChatMessage, DataContent, Role, TextContent, UriContent | ||
|
|
||
|
|
||
| class A2aEventAdapter(ABC): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we even need this base class? Since the base class also has this single method, it doesn't add anything.
| self.message_store = ChatMessageStore(messages=state.chat_message_store_state.messages, **kwargs) | ||
|
|
||
|
|
||
| class AgentThreadStorage(ABC): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are you adding this, we already have multiple ways of dealing with threads. Including serializing and deserializing them. So please don't add completely new concepts in the core package without first creating an issue and discussing the needs.
| self._agent: ChatAgent | WorkflowAgent = agent | ||
| self._event_adapter: A2aEventAdapter = event_adapter if event_adapter else BaseA2aEventAdapter() | ||
|
|
||
| async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add the @overload decorator so that it's clear which method come from the AgentExecutor.
- Updated test cases to use A2AExecutor instead of A2aExecutor for consistency. - Removed mock_event_adapter fixture and related tests as A2aEventAdapter is deprecated. - Consolidated event handling tests into TestA2AExecutorEventAdapter. - Adjusted imports in various files to reflect the removal of deprecated components. - Ensured all references to A2aExecutor are updated to A2AExecutor across the codebase.
|
Hi @eavanvalkenburg thanks for the review. I have further simplified the code for this feature and have addressed most of you review comments. Regarding your major concern around Do let me know if any other changes are required. |
Motivation and Context
This integration enables agent framework agents to communicate and execute as a A2A hosted agent server.
Description
Contribution Checklist