feat: Add a new Get Started example for A2A#511
Conversation
This commit introduces several enhancements to the Weather Reporting Poet agent and its project infrastructure: - Updated Licensing and Metadata: Incorporated new copyright headers and Apache 2.0 license information across relevant files (Makefile, README.md). Version information in Makefile and app.py has been updated. - Improved Client-Server Interaction: Refactored message handling in the client (client_app.py) for a more robust user interaction. Updated agent server configuration in app.py and agent core logic in agent.py and agent_executor.py. - Streamlined README: Enhanced the README.md with clearer setup, installation, and usage instructions, including example interactions and contribution guidelines. These changes aim to improve the agent's usability, maintainability, and adherence to project standards.
There was a problem hiding this comment.
Code Review
This pull request adds a "Get Started" sample for the Agent-to-Agent (A2A) protocol, featuring a weather-reporting poet agent with server and client implementations. Key feedback includes replacing blocking sleep calls with asynchronous ones, fixing undefined variables and copy-paste errors in the Makefile, ensuring consistent role naming, optimizing event loop management, and correcting a model name typo and an error message.
| def sprint(text: str): | ||
| """Print a text with a delay to simulate a long process""" | ||
| time.sleep(0.2) | ||
| print(text) | ||
|
|
||
|
|
||
| async def show_agent_card(): | ||
| """Show the agent card in the terminal""" | ||
| print("#" * 45) | ||
| agent_card = await get_agent_card() | ||
| sprint(f"Agent Card - Name: {agent_card.name}") | ||
| sprint(f"Agent Card - Capabilities: {agent_card.capabilities}") | ||
| sprint(f"Agent Card - Description: {agent_card.description}") | ||
| sprint(f"Agent Card - Skills: ") | ||
| for i, each in enumerate(agent_card.skills): | ||
| sprint(f"Agent Card - Skills[{i + 1}]:") | ||
| sprint(f"\t Skill - Id: {each.id}") | ||
| sprint(f"\t Skill - Name: {each.name}") | ||
| sprint(f"\t Skill - Description: {each.description}") | ||
| sprint(f"\t Skill - Examples: {each.examples}") |
There was a problem hiding this comment.
Using time.sleep() in an asynchronous application blocks the event loop. It is recommended to use await asyncio.sleep() and make the sprint function asynchronous. All calls to sprint should then be updated to use await.
| def sprint(text: str): | |
| """Print a text with a delay to simulate a long process""" | |
| time.sleep(0.2) | |
| print(text) | |
| async def show_agent_card(): | |
| """Show the agent card in the terminal""" | |
| print("#" * 45) | |
| agent_card = await get_agent_card() | |
| sprint(f"Agent Card - Name: {agent_card.name}") | |
| sprint(f"Agent Card - Capabilities: {agent_card.capabilities}") | |
| sprint(f"Agent Card - Description: {agent_card.description}") | |
| sprint(f"Agent Card - Skills: ") | |
| for i, each in enumerate(agent_card.skills): | |
| sprint(f"Agent Card - Skills[{i + 1}]:") | |
| sprint(f"\t Skill - Id: {each.id}") | |
| sprint(f"\t Skill - Name: {each.name}") | |
| sprint(f"\t Skill - Description: {each.description}") | |
| sprint(f"\t Skill - Examples: {each.examples}") | |
| async def sprint(text: str): | |
| """Print a text with a delay to simulate a long process""" | |
| await asyncio.sleep(0.2) | |
| print(text) | |
| async def show_agent_card(): | |
| """Show the agent card in the terminal""" | |
| print("#" * 45) | |
| agent_card = await get_agent_card() | |
| await sprint(f"Agent Card - Name: {agent_card.name}") | |
| await sprint(f"Agent Card - Capabilities: {agent_card.capabilities}") | |
| await sprint(f"Agent Card - Description: {agent_card.description}") | |
| await sprint(f"Agent Card - Skills: ") | |
| for i, each in enumerate(agent_card.skills): | |
| await sprint(f"Agent Card - Skills[{i + 1}]:") | |
| await sprint(f"\t Skill - Id: {each.id}") | |
| await sprint(f"\t Skill - Name: {each.name}") | |
| await sprint(f"\t Skill - Description: {each.description}") | |
| await sprint(f"\t Skill - Examples: {each.examples}") |
| export APP_VERSION=${IMAGE_VERSION} | ||
| @echo "Application Version "${IMAGE_VERSION} |
| run_server: ## Run python a2a server | ||
| cd server && python app.py | ||
|
|
||
| run_client: ## Prints the Application Version |
| # create a client message object | ||
| parts = [types.Part(text=text_query)] | ||
| message = types.Message( | ||
| role=types.Role.ROLE_USER, |
| if __name__ == "__main__": | ||
| # Ensure the event loop is properly managed | ||
| try: | ||
| asyncio.run(show_agent_card()) | ||
| asyncio.run(send_message("How is the Weather in Poland, Warsaw?")) | ||
| # asyncio.run(WeatherClient().run_interactive_terminal()) | ||
| except KeyboardInterrupt: | ||
| print("Exiting client due to user interruption.") |
There was a problem hiding this comment.
Calling asyncio.run() multiple times is inefficient as it creates and closes a new event loop for each call. It is better to wrap the logic in a single async main() function and run it once.
| if __name__ == "__main__": | |
| # Ensure the event loop is properly managed | |
| try: | |
| asyncio.run(show_agent_card()) | |
| asyncio.run(send_message("How is the Weather in Poland, Warsaw?")) | |
| # asyncio.run(WeatherClient().run_interactive_terminal()) | |
| except KeyboardInterrupt: | |
| print("Exiting client due to user interruption.") | |
| if __name__ == "__main__": | |
| async def main(): | |
| await show_agent_card() | |
| await send_message("How is the Weather in Poland, Warsaw?") | |
| try: | |
| asyncio.run(main()) | |
| except KeyboardInterrupt: | |
| print("Exiting client due to user interruption.") |
|
|
||
| def create_agent() -> LlmAgent: | ||
| return LlmAgent( | ||
| model="gemini-2.5-flash-lite", |
There was a problem hiding this comment.
| async def cancel( | ||
| self, request: RequestContext, event_queue: EventQueue | ||
| ) -> Task | None: | ||
| raise UnsupportedOperationError("Error: Streaming Operation not supported") |
There was a problem hiding this comment.
The error message in the cancel method incorrectly refers to "Streaming Operation". It should be updated to reflect that the "Cancel" operation is not supported.
| raise UnsupportedOperationError("Error: Streaming Operation not supported") | |
| raise UnsupportedOperationError("Error: Cancel operation not supported") |
Description
Created a Google ADK-powered agent(as A2A Server) that poetically reports weather updates. It leverages Google Search to gather factual weather data and presents it in the form of haikus or short poems, making weather forecasts engaging and unique.
DEMO: Using A2A client(pure python), we will send queries and receive messages from A2A Server.
Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
CONTRIBUTINGGuide.Fixes #<issue_number_goes_here> 🦕