Python: feat: Add Agent Framework to A2A bridge support#2403
Python: feat: Add Agent Framework to A2A bridge support#2403Shubham-Kumar-2000 wants to merge 46 commits intomicrosoft:mainfrom
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.
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.
- 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. |
|
I have successfully addressed all mentioned review comments. |
|
@Shubham-Kumar-2000 we have redone the threads, now called sessions, and the context providers,please update if you can! |
Sure @eavanvalkenburg give me sometime let me go through the changes. |
…nto agent_framework_to_a2a
- Consolidated mock agent fixtures in test_a2a_executor.py to simplify agent mocking. - Removed redundant tests related to thread storage and agent types, focusing on A2AExecutor's core functionality. - Updated test assertions to reflect changes in message handling with new Message and Content classes. - Enhanced integration tests to ensure compatibility with the new agent framework structure. - Added A2AExecutor to the module exports in __init__.py and __init__.pyi for better accessibility.
…d_dotenv call in agent_framework_to_a2a.py
…and fix agent creation method
|
@Shubham-Kumar-2000, friendly reminder — this issue is waiting on your response. Please share any updates when you get a chance. (This is an automated message.) |
|
Hi I have updated the PR with the new sessions implementation. Please let me know what more info is required by my side on this? |
|
@Shubham-Kumar-2000 sorry about the delays we've been busy with getting things to v1. Could you do another rebase to main and then I'll keep track and help you get this over the line! |
|
Hi @eavanvalkenburg I have merged the master branch. |
eavanvalkenburg
left a comment
There was a problem hiding this comment.
Looks quite good overall, few small comments.
| user_message = Message(role="user", contents=[Content.from_text(text=query)]) | ||
|
|
||
| # Run the agent with the message list | ||
| response = await self._agent.run(user_message, session=session) |
There was a problem hiding this comment.
when i look at our A2A connector, it seems it really only support streaming, should we use:
| response = await self._agent.run(user_message, session=session) | |
| response_stream = self._agent.run(user_message, stream=True, session=session) |
and then use something like (with a updated handle_events function and a new complete closure that calls updater.complete)):
stream = response_stream.with_transform_hook(self.handle_events).with_finalizer(complete)
await stream.get_final_response() # this ensures the whole stream is exhaustedThere was a problem hiding this comment.
I have added support for both streaming and non-streaming modes
| parts.append(Part(root=FilePart(file=FileWithBytes(bytes=base64_str, mime_type=content.media_type)))) | ||
| elif content.type == "uri" and content.uri: | ||
| parts.append(Part(root=FilePart(file=FileWithUri(uri=content.uri, mime_type=content.media_type)))) | ||
| # Silently skip unsupported content types |
There was a problem hiding this comment.
can we at least log those?
| @@ -0,0 +1,62 @@ | |||
| import uvicorn | |||
There was a problem hiding this comment.
missing copyright header
There was a problem hiding this comment.
Added copyright header
|
|
||
| # --8<-- [start:AgentCard] | ||
| # This will be the public-facing agent card | ||
| public_agent_card = AgentCard( |
There was a problem hiding this comment.
could we add a helper function to _a2a_executor that generates this from a Agent object?
There was a problem hiding this comment.
We can but only fields available to us as part of agent being common to the AgentCard are name and description . For all other fields, they will just become a parameter to the helper function as well.
We can still go ahead and add a simple helper function but not sure how much helpful it will be.
| user_message = Message(role="user", contents=[Content.from_text(text=query)]) | ||
|
|
||
| # Run the agent with the message list | ||
| response = await self._agent.run(user_message, session=session) |
There was a problem hiding this comment.
is there no way to get additional options passed from the caller?
There was a problem hiding this comment.
Added support to pass additional run_kwargs
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
…ate agent framework sample for flight and hotel booking capabilities
| from typing import Annotated | ||
|
|
||
| import uvicorn | ||
| from pydantic import Field |
There was a problem hiding this comment.
doesn't look like this is being used...
…update tests for initialization and execution scenarios
…umar-2000/agent-framework into agent_framework_to_a2a
…pdate tests for new behavior
|
Hi @eavanvalkenburg I have addressed all the review comments. Please check once and let me know if any further changes are required from my end. |
…umar-2000/agent-framework into agent_framework_to_a2a
|
I have fixed the |
|
|
||
| async def _run_stream(self, query: Any, session: AgentSession, updater: TaskUpdater) -> None: | ||
| """Run the agent in streaming mode and publish updates to the task updater.""" | ||
| response_stream = await self._agent.run(query, session=session, stream=True, **self._run_kwargs) |
There was a problem hiding this comment.
| response_stream = await self._agent.run(query, session=session, stream=True, **self._run_kwargs) | |
| response_stream = self._agent.run(query, session=session, stream=True, **self._run_kwargs) |
|
|
||
| async def _run(self, query: Any, session: AgentSession, updater: TaskUpdater) -> None: | ||
| """Run the agent in non-streaming mode and publish messages to the task updater.""" | ||
| response = await self._agent.run(query, session=session, stream=False, **self._run_kwargs) |
There was a problem hiding this comment.
FYI: this is correct, it depends on the stream param
| # --8<-- [end:AgentCard] | ||
|
|
||
| agent = Agent( | ||
| client=OpenAIChatCompletionClient(), |
There was a problem hiding this comment.
let's use at least OpenAIChatClient since that is the modern one, ideally we use FoundryChatClient in our samples, but you might not have that setup, so OpenAI is fine.
There was a problem hiding this comment.
Updated, I was actually using gemini and OpenAIChatClient doesn't seems to work with it that's why I was using chat completion client
…response stream handling; clean up imports in agent_framework_to_a2a.py
…umar-2000/agent-framework into agent_framework_to_a2a
|
Hi @eavanvalkenburg I have addressed all new review comments. |
Motivation and Context
This integration enables agent framework agents to communicate and execute as a A2A hosted agent server.
Description
Contribution Checklist