Skip to content

Commit e7618d2

Browse files
committed
docs: update v1.0 migration guide
1 parent 24db37e commit e7618d2

1 file changed

Lines changed: 76 additions & 51 deletions

File tree

docs/migrations/v1_0/README.md

Lines changed: 76 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ Beyond protocol support, `v1.0` enhances the developer experience by introducing
66

77
This documentation details the technical upgrades and architectural modifications introduced in A2A Python SDK v1.0. For developers using the database persistence layer, please refer to the [Database Migration Guide](database/) for specific update instructions.
88

9+
> ### **Why Upgrade to v1.0?**
10+
> * **Protocol v1.0 Compliance**: Full alignment with the latest A2A industry standard for cross-agent interoperability.
11+
> * **Reduced Boilerplate**: Unified helper utilities that simplify common tasks like message and task creation.
12+
> * **Architectural Flexibility**: Direct Starlette/FastAPI integration allows you to mount A2A routes into existing applications with full control over middleware.
13+
914
---
1015

1116
## Table of Contents
@@ -55,9 +60,9 @@ pip install --upgrade a2a-sdk
5560
Types have migrated from Pydantic models to Protobuf-based classes.
5661

5762

58-
### Enum values: snake_case → SCREAMING_SNAKE_CASE
63+
### Enum values: `snake_case``SCREAMING_SNAKE_CASE`
5964

60-
All the enum values are now standardized from snake_case to **SCREAMING_SNAKE_CASE** format.
65+
All the enum values are now [standardised](https://a2a-protocol.org/v1.0.0/specification/#55-json-field-naming-convention) to use `SCREAMING_SNAKE_CASE` format.
6166

6267
This affects every enum in the SDK: `TaskState`, `Role`.
6368

@@ -131,14 +136,14 @@ message = Message(
131136
132137
### AgentCard Structure
133138

134-
The new `AgentCard` can supports multiple transport bindings using `AgentInterface` class.
135-
136-
Key differences:
137-
- `url` is gone; use `supported_interfaces` with one or more `AgentInterface` entries
138-
- `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` are removed; use `AgentCard.default_input_modes` / `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` / `AgentSkill.output_modes` for per-skill overrides
139-
- `supports_authenticated_extended_card` is no longer a top-level `AgentCard` field; it has moved into `AgentCapabilities` and is renamed to `extended_agent_card`
140-
- `AgentInterface.protocol_binding` accepted values: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'`
141-
- `examples` field was removed; set it per `AgentSkill` instead
139+
Key changes:
140+
- Added `AgentInterface` class to support multiple transport bindings via the `supported_interfaces` field in AgentCard.
141+
- The `url` parameter in `AgentCapabilities` is removed and is now part of `AgentInterface`.
142+
- Accepted values for `AgentInterface.protocol_binding`: `'JSONRPC'`, `'HTTP+JSON'`, `'GRPC'`
143+
- The `AgentCard.capabilities` field is renamed to `AgentCard.agent_capabilities`.
144+
- The `AgentCard.supports_authenticated_extended_card` field is renamed to `AgentCapabilities.extended_agent_card`.
145+
- The `AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` fields are removed; use `AgentCard.default_input_modes` / `AgentCard.default_output_modes` for card-level defaults, or `AgentSkill.input_modes` / `AgentSkill.output_modes` for per-skill overrides.
146+
- The `examples` parameter in `AgentCard` is removed and is now part of `AgentSkill`.
142147

143148
**Before (v0.3):**
144149
```python
@@ -164,7 +169,7 @@ agent_card = AgentCard(
164169

165170
**After (v1.0):**
166171
```python
167-
from a2a.types import AgentCard, AgentCapabilities, AgentInterface, AgentSkill,
172+
from a2a.types import AgentCard, AgentCapabilities, AgentInterface, AgentSkill
168173

169174
agent_card = AgentCard(
170175
name='My Agent',
@@ -232,10 +237,13 @@ The wrapper classes (`A2AStarletteApplication`, `A2AFastApiApplication` and `A2A
232237
from a2a.server.apps import A2AStarletteApplication
233238
import uvicorn
234239

240+
# Create application using A2AStarletteApplication wrapper class
235241
server = A2AStarletteApplication(
236242
agent_card=agent_card,
237243
http_handler=request_handler,
238244
)
245+
246+
# Start the server
239247
uvicorn.run(server.build(), host=host, port=port)
240248
```
241249

@@ -245,26 +253,19 @@ from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
245253
from starlette.applications import Starlette
246254
import uvicorn
247255

248-
routes = []
249-
routes.extend(create_agent_card_routes(agent_card))
250-
routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/'))
251-
252-
app = Starlette(routes=routes)
253-
uvicorn.run(app, host=host, port=port)
254-
```
255-
256-
If you need REST transport in addition to JSON-RPC:
257-
```python
258-
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes, create_rest_routes
259-
from starlette.applications import Starlette
260-
import uvicorn
261256

257+
# Define routes for transports as per AgentCard
262258
routes = []
263259
routes.extend(create_agent_card_routes(agent_card))
264-
routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/'))
265-
routes.extend(create_rest_routes(request_handler))
260+
routes.extend(create_jsonrpc_routes(request_handler, rpc_url='/api/v1/jsonrpc/'))
261+
262+
# Optional: Add routes for other transports
263+
# routes.extend(create_rest_routes(request_handler, path_prefix='/api/v1/rest/'))
266264

265+
# Create application using routes
267266
app = Starlette(routes=routes)
267+
268+
# Start the server
268269
uvicorn.run(app, host=host, port=port)
269270
```
270271

@@ -297,19 +298,18 @@ create_rest_routes(request_handler, enable_v0_3_compat=True)
297298

298299
## 6. Client: Creating a Client
299300

300-
New `create_client()` `ClientFactory` function that creates a client for the agent.
301+
In `v1.0`, use `a2a.client.create_client()` helper function to create a `Client` for the agent.
301302

302-
> **Note**: The legacy `A2AClient` class has been removed.
303303

304304
**Before (v0.3):**
305305
```python
306306
from a2a.client import ClientFactory
307307

308-
# From URL
308+
# Option 1: Using Agent Server URL
309309
factory = ClientFactory()
310310
client = factory.create_client('http://localhost:9999/')
311311

312-
# From an already-resolved AgentCard
312+
# Option 2: Using AgentCard
313313
factory = ClientFactory()
314314
client = factory.create_client(agent_card)
315315
```
@@ -318,10 +318,10 @@ client = factory.create_client(agent_card)
318318
```python
319319
from a2a.client import create_client
320320

321-
# From URL — resolves the agent card automatically
321+
# Option 1: Using Agent Server URL
322322
client = await create_client('http://localhost:9999/')
323323

324-
# From an already-resolved AgentCard
324+
# Option 2: Using AgentCard
325325
client = await create_client(agent_card)
326326
```
327327

@@ -332,9 +332,9 @@ client = await create_client(agent_card)
332332

333333
## 7. Client: Send Message
334334

335-
The `BaseClient.send_message()` return type is standardised from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`.
335+
The `BaseClient.send_message()` return type is standardised from `AsyncIterator[ClientEvent | Message]` to `AsyncIterator[StreamResponse]`.
336336

337-
Each `StreamResponse` yields exactly one of: `task`, `message`, `status_update`, or `artifact_update`. Use `HasField()` to check which field is set.
337+
Each `StreamResponse` yields exactly one of: (`task`, `message`, `status_update`, or `artifact_update`). Use `HasField()` to check which field is set.
338338

339339

340340
**Before (v0.3):**
@@ -368,6 +368,7 @@ async for chunk in client.send_message(request):
368368

369369
`ClientConfig.push_notification_config` is now **singular** (a single `TaskPushNotificationConfig` or `None`), not a list.
370370

371+
371372
**Before (v0.3):**
372373
```python
373374
config = ClientConfig(
@@ -386,31 +387,55 @@ config = ClientConfig(
386387

387388
## 9. Helper Utilities
388389

389-
A new `a2a.helpers` module consolidates helper functions into a single import. Most were previously available under `a2a.utils.*`; a few are new in v1.0.
390+
To improve the developer experience, we have consolidated helper functions into a single import. In v0.3, these helper functions were scattered across different modules; In v1.0, they are all available under `a2a.helpers`.
391+
392+
| Helper Function | Description |
393+
|---|---|
394+
| `display_agent_card` | Prints a human-readable summary of an `AgentCard` to stdout. |
395+
| `get_artifact_text` | Joins all text parts of an `Artifact` into a single string (using `\n` as delimiter). |
396+
| `get_message_text` | Joins all text parts of a `Message` into a single string (using `\n` as delimiter). |
397+
| `get_stream_response_text` | Extracts text from a `StreamResponse` protobuf message. |
398+
| `get_text_parts` | Returns a list of raw text strings from a sequence of `Part` objects, skipping non-text parts. |
399+
| `new_artifact` | Creates an `Artifact` from a list of `Part` objects, a name, and an optional description and ID. |
400+
| `new_message` | Creates a `Message` from a list of `Part` objects with a role (defaults to `ROLE_AGENT`), and optional task/context IDs. |
401+
| `new_task` | Creates a `Task` with an explicit task ID, context ID, and state. |
402+
| `new_task_from_user_message` | Creates a `TASK_STATE_SUBMITTED` `Task` from a user `Message`. Raises an error if the role is not `ROLE_USER` or if parts are empty. |
403+
| `new_text_artifact` | Creates an `Artifact` with a single text `Part`, a name, and an optional description and ID. |
404+
| `new_text_artifact_update_event` | Creates a `TaskArtifactUpdateEvent` with a text artifact. |
405+
| `new_text_message` | Creates a `Message` with a single text `Part`; role defaults to `ROLE_AGENT`. |
406+
| `new_text_status_update_event` | Creates a `TaskStatusUpdateEvent` with a text message. |
407+
408+
Example Usage:
409+
410+
**1. Create a user message**
390411

391412
```python
392-
from a2a.helpers import (
393-
display_agent_card, # print a human-readable summary of an AgentCard to stdout
394-
get_artifact_text, # join all text parts of an Artifact into a single string (delimiter='\n')
395-
get_message_text, # join all text parts of a Message into a single string (delimiter='\n')
396-
get_stream_response_text, # extract text from a StreamResponse proto message
397-
get_text_parts, # return a list of raw text strings from a sequence of Parts (skips non-text parts)
398-
new_artifact, # create an Artifact from a list of Parts, name, optional description and artifact_id
399-
new_message, # create a Message from a list of Parts with role (default ROLE_AGENT), optional task_id/context_id
400-
new_task, # create a Task with explicit task_id, context_id, and state
401-
new_task_from_user_message, # create a TASK_STATE_SUBMITTED Task from a user Message; raises if role != ROLE_USER or parts are empty
402-
new_text_artifact, # create an Artifact with a single text Part, name, optional description and artifact_id
403-
new_text_artifact_update_event, # create a TaskArtifactUpdateEvent with a text artifact
404-
new_text_message, # create a Message with a single text Part; role defaults to ROLE_AGENT
405-
new_text_status_update_event, # create a TaskStatusUpdateEvent with a text message
406-
)
413+
from a2a.helpers import new_text_message
414+
from a2a.types import Role
415+
416+
# Create a user message
417+
user_message = new_text_message("What's the weather?", role=Role.ROLE_USER)
418+
419+
# Create an agent response message
420+
response_message = new_text_message("It is sunny today!")
421+
```
422+
423+
**2. Extract the text out of a message**
424+
425+
```python
426+
from a2a.helpers import get_message_text
427+
428+
# Get text from a message
429+
text = get_message_text(response_message)
430+
print(text)
407431
```
408432

409433
---
410434

411435
## 10. Summary of Key Changes in v1.0
412436

413-
- **Standardisation to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `kebab-case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification.
437+
- **Migration to Protobuf** — Core types have migrated from Pydantic models to Protobuf-based classes. Protobuf objects do not support arbitrary attribute assignment. Use `MessageToDict` from `google.protobuf.json_format` to convert objects to dictionaries, and `HasField('field_name')` to check for optional fields.
438+
- **Standardisation to `SCREAMING_SNAKE_CASE`** — All enum values have been renamed from `snake_case` strings to `SCREAMING_SNAKE_CASE` for compliance with the ProtoJSON specification.
414439
- **`AgentCard`** — Significantly restructured to support multiple transport interfaces.
415440
- **`AgentInterface`** — The top-level `url` field is replaced by `supported_interfaces`, a list of `AgentInterface` objects. Each entry describes a single transport endpoint carrying `protocol_binding`, `protocol_version`, and `url`.
416441
- **Input and output modes**`AgentCapabilities.input_modes` and `AgentCapabilities.output_modes` are removed and now live directly on `AgentCard` as `default_input_modes` and `default_output_modes`. Individual skills can override these with their own `input_modes` and `output_modes`.

0 commit comments

Comments
 (0)