Skip to content

Commit 9b6f0ee

Browse files
committed
fix: update samples/cli.py
1 parent 6b56511 commit 9b6f0ee

2 files changed

Lines changed: 85 additions & 11 deletions

File tree

samples/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# A2A Python SDK — Samples
2+
3+
This directory contains runnable examples demonstrating how to build and interact with an A2A-compliant agent using the Python SDK.
4+
5+
## Contents
6+
7+
| File | Role | Description |
8+
|---|---|---|
9+
| `hello_world_agent.py` | **Server** | A2A agent server |
10+
| `cli.py` | **Client** | Interactive terminal client |
11+
12+
All three samples are designed to work together out of the box: the agent listens on `http://127.0.0.1:41241`, which is the default URL used by both clients.
13+
---
14+
15+
## `hello_world_agent.py` — Agent Server
16+
17+
Implements an A2A agent that responds to simple greeting messages (e.g., "hello", "how are you", "bye") with text replies, simulating a 1-second processing delay.
18+
19+
Demonstrates:
20+
- Subclassing `AgentExecutor` and implementing `execute()` / `cancel()`
21+
- Publishing streaming status updates and artifacts via `TaskUpdater`
22+
- Exposing all three transports in both protocol versions (v1.0 and v0.3 compat) simultaneously:
23+
- **JSON-RPC** (v1.0 and v0.3) at `http://127.0.0.1:41241/a2a/jsonrpc`
24+
- **HTTP+JSON (REST)** (v1.0 and v0.3) at `http://127.0.0.1:41241/a2a/rest`
25+
- **gRPC v1.0** on port `50051`
26+
- **gRPC v0.3 (compat)** on port `50052`
27+
- Serving the agent card at `http://127.0.0.1:41241/.well-known/agent-card.json`
28+
29+
**Run:**
30+
31+
```bash
32+
uv run python samples/hello_world_agent.py
33+
```
34+
35+
---
36+
37+
## `cli.py` — Client
38+
39+
An interactive terminal client with full visibility into the streaming event flow. Each `TaskStatusUpdate` and `TaskArtifactUpdate` event is printed as it arrives.
40+
41+
Features:
42+
- Transport selection via `--transport` flag (`JSONRPC`, `HTTP+JSON`, `GRPC`)
43+
- Session management (`context_id` persisted across messages, `task_id` per task)
44+
- Graceful error handling for HTTP and gRPC failures
45+
46+
**Run:**
47+
48+
```bash
49+
# Connect to the local hello_world_agent (default):
50+
uv run python samples/cli.py
51+
52+
# Connect to a different URL, using gRPC:
53+
uv run python samples/cli.py --url http://192.168.1.10:41241 --transport GRPC
54+
```
55+
56+
Type `/quit` or `/exit` to stop, or press `Ctrl+C`.
57+
58+
---
59+
60+
61+
## Quick Start
62+
63+
In two separate terminals:
64+
65+
```bash
66+
# Terminal 1 — start the agent
67+
uv run python samples/hello_world_agent.py
68+
69+
# Terminal 2 — start the client
70+
uv run python samples/cli.py
71+
```
72+
73+
Then type a message like `hello` and press Enter.

samples/cli.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,22 @@
1111

1212
from a2a.client import A2ACardResolver, ClientConfig, create_client
1313
from a2a.types import Message, Part, Role, SendMessageRequest, TaskState
14+
from a2a.utils import get_artifact_text, get_message_text
1415

1516

1617
async def _handle_stream(
1718
stream: Any, current_task_id: str | None
1819
) -> str | None:
19-
async for event, task in stream:
20-
if not task:
21-
continue
20+
async for event in stream:
2221
if not current_task_id:
23-
current_task_id = task.id
24-
22+
current_task_id = event.task.id
2523
if event:
2624
if event.HasField('status_update'):
2725
state_name = TaskState.Name(event.status_update.status.state)
2826
print(f'TaskStatusUpdate [state={state_name}]:', end=' ')
2927
if event.status_update.status.HasField('message'):
30-
for part in event.status_update.status.message.parts:
31-
if part.text:
32-
print(part.text, end=' ')
28+
message = event.status_update.status.message
29+
print(get_message_text(message, delimiter=' '))
3330
print()
3431

3532
if (
@@ -44,9 +41,11 @@ async def _handle_stream(
4441
f'TaskArtifactUpdate [name={event.artifact_update.artifact.name}]:',
4542
end=' ',
4643
)
47-
for part in event.artifact_update.artifact.parts:
48-
if part.text:
49-
print(part.text, end=' ')
44+
print(
45+
get_artifact_text(
46+
event.artifact_update.artifact, delimiter=' '
47+
)
48+
)
5049
print()
5150

5251
return current_task_id
@@ -68,6 +67,8 @@ async def main() -> None:
6867
config = ClientConfig()
6968
if args.transport:
7069
config.supported_protocol_bindings = [args.transport]
70+
if args.transport == 'GRPC':
71+
config.grpc_channel_factory = grpc.aio.insecure_channel
7172

7273
print(
7374
f'Connecting to {args.url} (preferred transport: {args.transport or "Any"})'

0 commit comments

Comments
 (0)