diff --git a/README.md b/README.md index e47164d..e219194 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # A2A Agent Template -A minimal template for building [A2A (Agent-to-Agent)](https://a2a-protocol.org/latest/) agents. +A minimal template for building [A2A (Agent-to-Agent)](https://a2a-protocol.org/latest/) green agents compatible with the [AgentBeats](https://agentbeats.dev) platform. ## Project Structure diff --git a/pyproject.toml b/pyproject.toml index a98a765..186c121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,12 @@ [project] -name = "agent-template" +name = "green-agent-template" version = "0.1.0" -description = "A template for A2A agents" +description = "A template for A2A green agents" readme = "README.md" requires-python = ">=3.13" dependencies = [ "a2a-sdk[http-server]>=0.3.20", + "pydantic>=2.12.5", "uvicorn>=0.38.0", ] diff --git a/src/agent.py b/src/agent.py index 75ad45d..b539db4 100644 --- a/src/agent.py +++ b/src/agent.py @@ -1,15 +1,41 @@ +from typing import Any +from pydantic import BaseModel, HttpUrl, ValidationError from a2a.server.tasks import TaskUpdater -from a2a.types import Message, TaskState, Part, TextPart +from a2a.types import Message, TaskState, Part, TextPart, DataPart from a2a.utils import get_message_text, new_agent_text_message from messenger import Messenger +class EvalRequest(BaseModel): + """Request format sent by the AgentBeats platform to green agents.""" + participants: dict[str, HttpUrl] # role -> agent URL + config: dict[str, Any] + + class Agent: + # Fill in: list of required participant roles, e.g. ["pro_debater", "con_debater"] + required_roles: list[str] = [] + # Fill in: list of required config keys, e.g. ["topic", "num_rounds"] + required_config_keys: list[str] = [] + def __init__(self): self.messenger = Messenger() # Initialize other state here + def validate_request(self, request: EvalRequest) -> tuple[bool, str]: + missing_roles = set(self.required_roles) - set(request.participants.keys()) + if missing_roles: + return False, f"Missing roles: {missing_roles}" + + missing_config_keys = set(self.required_config_keys) - set(request.config.keys()) + if missing_config_keys: + return False, f"Missing config keys: {missing_config_keys}" + + # Add additional request validation here + + return True, "ok" + async def run(self, message: Message, updater: TaskUpdater) -> None: """Implement your agent logic here. @@ -21,12 +47,29 @@ async def run(self, message: Message, updater: TaskUpdater) -> None: """ input_text = get_message_text(message) - # Replace this example code with your agent logic + try: + request: EvalRequest = EvalRequest.model_validate_json(input_text) + ok, msg = self.validate_request(request) + if not ok: + await updater.reject(new_agent_text_message(msg)) + return + except ValidationError as e: + await updater.reject(new_agent_text_message(f"Invalid request: {e}")) + return + + # Replace example code below with your agent logic + # Use request.participants to get participant agent URLs by role + # Use request.config for assessment parameters await updater.update_status( TaskState.working, new_agent_text_message("Thinking...") ) await updater.add_artifact( - parts=[Part(root=TextPart(text=input_text))], - name="Echo", + parts=[ + Part(root=TextPart(text="The agent performed well.")), + Part(root=DataPart(data={ + # structured assessment results + })) + ], + name="Result", ) diff --git a/uv.lock b/uv.lock index 204a82a..76e8bac 100644 --- a/uv.lock +++ b/uv.lock @@ -24,31 +24,6 @@ http-server = [ { name = "starlette" }, ] -[[package]] -name = "agent-template" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "a2a-sdk", extra = ["http-server"] }, - { name = "uvicorn" }, -] - -[package.optional-dependencies] -test = [ - { name = "httpx" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, -] - -[package.metadata] -requires-dist = [ - { name = "a2a-sdk", extras = ["http-server"], specifier = ">=0.3.20" }, - { name = "httpx", marker = "extra == 'test'", specifier = ">=0.28.1" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.24.0" }, - { name = "uvicorn", specifier = ">=0.38.0" }, -] - [[package]] name = "annotated-doc" version = "0.0.4" @@ -216,6 +191,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515 }, ] +[[package]] +name = "green-agent-template" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "a2a-sdk", extra = ["http-server"] }, + { name = "pydantic" }, + { name = "uvicorn" }, +] + +[package.optional-dependencies] +test = [ + { name = "httpx" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, +] + +[package.metadata] +requires-dist = [ + { name = "a2a-sdk", extras = ["http-server"], specifier = ">=0.3.20" }, + { name = "httpx", marker = "extra == 'test'", specifier = ">=0.28.1" }, + { name = "pydantic", specifier = ">=2.12.5" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.24.0" }, + { name = "uvicorn", specifier = ">=0.38.0" }, +] + [[package]] name = "h11" version = "0.16.0"