Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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",
]

Expand Down
51 changes: 47 additions & 4 deletions src/agent.py
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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",
)
52 changes: 27 additions & 25 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.