-
Notifications
You must be signed in to change notification settings - Fork 126
Feature/finance example agent #45
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from .qualinvest_service_agent import agent as root_agent |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,85 @@ | ||||||||||||||||||
| import logging | ||||||||||||||||||
|
|
||||||||||||||||||
| import click | ||||||||||||||||||
| import uvicorn | ||||||||||||||||||
| from a2a.server.apps import A2AStarletteApplication | ||||||||||||||||||
| from a2a.server.request_handlers import DefaultRequestHandler | ||||||||||||||||||
| from a2a.server.tasks import InMemoryTaskStore | ||||||||||||||||||
| from a2a.types import ( | ||||||||||||||||||
| AgentCapabilities, | ||||||||||||||||||
| AgentCard, | ||||||||||||||||||
| AgentSkill, | ||||||||||||||||||
| ) | ||||||||||||||||||
| from dotenv import load_dotenv | ||||||||||||||||||
| from google.adk.artifacts import InMemoryArtifactService | ||||||||||||||||||
| from google.adk.memory.in_memory_memory_service import InMemoryMemoryService | ||||||||||||||||||
| from google.adk.runners import Runner | ||||||||||||||||||
| from google.adk.sessions import InMemorySessionService | ||||||||||||||||||
|
|
||||||||||||||||||
| from qualinvest_service_agent import create_investify_service_agent # type: ignore | ||||||||||||||||||
| from qualinvest_service_agent_executor import investifyAgentExecutor | ||||||||||||||||||
|
|
||||||||||||||||||
| load_dotenv() | ||||||||||||||||||
|
|
||||||||||||||||||
| logging.basicConfig() | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| @click.command() | ||||||||||||||||||
| @click.option("--host", "host", default="localhost") | ||||||||||||||||||
| @click.option("--port", "port", default=10001) | ||||||||||||||||||
| def main(host: str, port: int) -> None: | ||||||||||||||||||
|
Comment on lines
+27
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Annotate Click option types to avoid passing str port to uvicorn. Without type=int, port from CLI is a string at runtime. Apply this diff: -@click.option("--host", "host", default="localhost")
-@click.option("--port", "port", default=10001)
+@click.option("--host", "host", default="localhost", type=str)
+@click.option("--port", "port", default=10001, type=int)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| skill = AgentSkill( | ||||||||||||||||||
| id="buy_stocks", | ||||||||||||||||||
| name="buy stocks", | ||||||||||||||||||
| description="buy stocks for the user", | ||||||||||||||||||
| tags=["buy"], | ||||||||||||||||||
| examples=["buy aapl for 10000$"], | ||||||||||||||||||
| ) | ||||||||||||||||||
| skill2 = AgentSkill( | ||||||||||||||||||
| id="check_balance", | ||||||||||||||||||
| name="check balance", | ||||||||||||||||||
| description="check user's balance", | ||||||||||||||||||
| tags=["balance"], | ||||||||||||||||||
| examples=["do I have available 10000$ to invest in shitcoins"], | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| agent_card = AgentCard( | ||||||||||||||||||
| name="investify service agent", | ||||||||||||||||||
| description="customer service for investify finance", | ||||||||||||||||||
| url=f"http://{host}:{port}/", | ||||||||||||||||||
| version="1.0.0", | ||||||||||||||||||
| defaultInputModes=["text"], | ||||||||||||||||||
| defaultOutputModes=["text"], | ||||||||||||||||||
| capabilities=AgentCapabilities(streaming=True), | ||||||||||||||||||
| skills=[skill, skill2], | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| investify_service_agent = create_investify_service_agent() | ||||||||||||||||||
| runner = Runner( | ||||||||||||||||||
| app_name=agent_card.name, | ||||||||||||||||||
| agent=investify_service_agent, | ||||||||||||||||||
| artifact_service=InMemoryArtifactService(), | ||||||||||||||||||
| session_service=InMemorySessionService(), | ||||||||||||||||||
| memory_service=InMemoryMemoryService(), | ||||||||||||||||||
| ) | ||||||||||||||||||
| agent_executor = investifyAgentExecutor(runner, agent_card) | ||||||||||||||||||
|
|
||||||||||||||||||
| request_handler = DefaultRequestHandler( | ||||||||||||||||||
| agent_executor=agent_executor, | ||||||||||||||||||
| task_store=InMemoryTaskStore(), | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| a2a_app = A2AStarletteApplication( | ||||||||||||||||||
| agent_card=agent_card, | ||||||||||||||||||
| http_handler=request_handler, | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| uvicorn.run( | ||||||||||||||||||
| a2a_app.build(), | ||||||||||||||||||
| host=host, | ||||||||||||||||||
| port=port, | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||
| main() | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| import traceback | ||
| from typing import Any | ||
| from uuid import uuid4 | ||
|
|
||
| import httpx | ||
| from a2a.client import A2AClient | ||
| from a2a.types import ( | ||
| GetTaskRequest, | ||
| GetTaskResponse, | ||
| MessageSendParams, | ||
| SendMessageRequest, | ||
| SendMessageResponse, | ||
| SendMessageSuccessResponse, | ||
| Task, | ||
| TaskQueryParams, | ||
| ) | ||
|
|
||
| AGENT_URL = "http://localhost:10001" | ||
|
|
||
|
|
||
| def create_send_message_payload( | ||
| text: str, | ||
| task_id: str | None = None, | ||
| context_id: str | None = None, | ||
| ) -> dict[str, Any]: | ||
| """Helper function to create the payload for sending a task.""" | ||
| payload: dict[str, Any] = { | ||
| "message": { | ||
| "role": "user", | ||
| "parts": [{"kind": "text", "text": text}], | ||
| "messageId": uuid4().hex, | ||
| }, | ||
| } | ||
|
|
||
| if task_id: | ||
| payload["message"]["taskId"] = task_id | ||
|
|
||
| if context_id: | ||
| payload["message"]["contextId"] = context_id | ||
| return payload | ||
|
|
||
|
|
||
| def print_json_response(response: Any, description: str) -> None: | ||
| """Helper function to print the JSON representation of a response.""" | ||
| print(f"--- {description} ---") | ||
| if hasattr(response, "root"): | ||
| print(f"{response.root.model_dump_json(exclude_none=True)}\n") | ||
| else: | ||
| print(f'{response.model_dump(mode="json", exclude_none=True)}\n') | ||
|
|
||
|
|
||
| def build_message( | ||
| text: str = "What is the weather tomorrow in New York?", | ||
| task_id: str | None = None, | ||
| context_id: str | None = None, | ||
| ) -> SendMessageRequest: | ||
| send_payload = create_send_message_payload( | ||
| text=text, | ||
| task_id=task_id, | ||
| context_id=context_id, | ||
| ) | ||
| return SendMessageRequest( | ||
| id=uuid4().hex, | ||
| params=MessageSendParams(**send_payload), | ||
| ) | ||
|
|
||
|
|
||
| async def send_message( | ||
| client: A2AClient, | ||
| request: SendMessageRequest, | ||
| quiet: bool = False, | ||
| ) -> str: | ||
| print("--- Single Turn Request ---") | ||
| # Send Message | ||
| send_response: SendMessageResponse = await client.send_message(request) | ||
| if not quiet: | ||
| print_json_response(send_response, "Single Turn Request Response") | ||
| if not isinstance(send_response.root, SendMessageSuccessResponse): | ||
| print("received non-success response. Aborting get task") | ||
| return "received non-success response. Aborting get task" | ||
|
|
||
| if not isinstance(send_response.root.result, Task): | ||
| print("received non-task response. Aborting get task") | ||
| return "received non-task response. Aborting get task" | ||
|
|
||
| task_id: str = send_response.root.result.id | ||
| print("---Query Task---") | ||
| # query the task | ||
| get_request = GetTaskRequest( | ||
| id=uuid4().hex, | ||
| params=TaskQueryParams(id=task_id), | ||
| ) | ||
| get_response: GetTaskResponse = await client.get_task(get_request) | ||
|
|
||
| if not quiet: | ||
| print_json_response(get_response, "Query Task Response") | ||
|
|
||
| try: | ||
| return get_response.root.result.artifacts[0].parts[0].root.text # type: ignore | ||
| except Exception: | ||
| return "Unable to get response from agent" | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| """Main function to run the tests.""" | ||
| print(f"Connecting to agent at {AGENT_URL}...") | ||
| try: | ||
| async with httpx.AsyncClient(timeout=30) as httpx_client: | ||
| client = await A2AClient.get_client_from_agent_card_url( | ||
| httpx_client, | ||
| AGENT_URL, | ||
| ) | ||
| print("Connection successful.") | ||
|
|
||
| context_id = uuid4().hex | ||
|
|
||
| print( | ||
| await send_message( | ||
| client, | ||
| build_message( | ||
| text="Who are you?", | ||
| context_id=context_id, | ||
| ), | ||
| quiet=True, | ||
| ), | ||
| ) | ||
|
|
||
| print( | ||
| await send_message( | ||
| client, | ||
| build_message( | ||
| text="I want to buy bitcoin for 1000$ immediately", | ||
| context_id=context_id, | ||
| ), | ||
| quiet=True, | ||
| ), | ||
| ) | ||
|
|
||
| print( | ||
| await send_message( | ||
| client, | ||
| build_message( | ||
| text="what should I invest in?", | ||
| context_id=context_id, | ||
| ), | ||
| quiet=True, | ||
| ), | ||
| ) | ||
|
|
||
| except Exception as e: | ||
| traceback.print_exc() | ||
| print(f"An error occurred: {e}") | ||
| print("Ensure the agent server is running.") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| import asyncio | ||
|
|
||
| asyncio.run(main()) |
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.
Use relative imports within the package.
Absolute imports will break when running as a package module.
Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents