Skip to content

Commit 205078b

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/guglielmoc/refactor_json_rpc_server' into guglielmoc/refactor_json_rpc_server
2 parents d48a3c2 + 128bda3 commit 205078b

23 files changed

Lines changed: 1192 additions & 794 deletions

.github/actions/spelling/allow.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ cls
3535
coc
3636
codegen
3737
coro
38+
culsans
3839
datamodel
3940
deepwiki
4041
drivername
@@ -127,7 +128,7 @@ taskupdate
127128
testuuid
128129
Tful
129130
tiangolo
131+
TResponse
130132
typ
131133
typeerror
132134
vulnz
133-
TResponse

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Always check @./GEMINI.md for the full instruction list.
2+
3+
This file exists for compatibility with tools that look for AGENTS.md.

GEMINI.md

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1-
**A2A specification:** https://a2a-protocol.org/latest/specification/
1+
# Agent Command Center
22

3-
## Project frameworks
4-
- uv as package manager
3+
## 1. Project Overview & Purpose
4+
**Primary Goal**: This is the Python SDK for the Agent2Agent (A2A) Protocol. It allows developers to build and run agentic applications as A2A-compliant servers. It handles complex messaging, task management, and communication across different transports (REST, gRPC, JSON-RPC).
5+
**Specification**: [A2A-Protocol](https://a2a-protocol.org/latest/specification/)
56

6-
## Code style and mandatory checks
7-
1. Whenever writing python code, write types as well.
8-
2. After making the changes run ruff to check and fix the formatting issues
9-
```
10-
uv run ruff check --fix
11-
uv run ruff format
12-
```
13-
3. Run mypy type checkers to check for type errors
14-
```
15-
uv run mypy src
16-
```
17-
4. Run the unit tests to make sure that none of the unit tests are broken.
18-
```
19-
uv run pytest
20-
```
7+
## 2. Technology Stack & Architecture
8+
9+
- **Language**: Python 3.10+
10+
- **Package Manager**: `uv`
11+
- **Lead Transports**: FastAPI (REST/JSON-RPC), gRPC
12+
- **Data Layer**: SQLAlchemy (SQL), Pydantic (Logic/Legacy), Protobuf (Modern Messaging)
13+
- **Key Directories**:
14+
- `/src`: Core implementation logic.
15+
- `/tests`: Comprehensive test suite.
16+
- `/docs`: AI guides.
17+
18+
## 3. Style Guidelines & Mandatory Checks
19+
- **Style Guidelines**: Follow the rules in @./docs/ai/coding_conventions.md for every response involving code.
20+
- **Mandatory Checks**: Run the commands in @./docs/ai/mandatory_checks.md after making any changes to the code and before committing.
21+
22+
## 4. Mandatory AI Workflow for Coding Tasks
23+
1. **Required Reading**: You MUST read the contents of @./docs/ai/coding_conventions.md and @./docs/ai/mandatory_checks.md at the very beginning of EVERY coding task.
24+
2. **Initial Checklist**: Every `task.md` you create MUST include a section for **Mandatory Checks** from @./docs/ai/mandatory_checks.md.
25+
3. **Verification Requirement**: You MUST run all mandatory checks before declaring any task finished.

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636

3737
## 🧩 Compatibility
3838

39-
This SDK implements the A2A Protocol Specification [`v0.3.0`](https://a2a-protocol.org/v0.3.0/specification).
39+
This SDK implements the A2A Protocol Specification [`0.3`](https://a2a-protocol.org/v0.3.0/specification).
40+
41+
> [!IMPORTANT]
42+
> There is an [**alpha version**](https://github.com/a2aproject/a2a-python/releases?q=%22v1.0.0-alpha%22&expanded=true) available with support for both [`1.0`](https://a2a-protocol.org/v1.0.0/specification/) and [`0.3`](https://a2a-protocol.org/v0.3.0/specification) versions. Development for this version is taking place in the [`1.0-dev`](https://github.com/a2aproject/a2a-python/tree/1.0-dev) branch, tracked in [#701](https://github.com/a2aproject/a2a-python/issues/701).
4043
4144
| Transport | Client | Server |
4245
| :--- | :---: | :---: |
@@ -65,6 +68,7 @@ Install the core SDK and any desired extras using your preferred package manager
6568
| **gRPC Support** | `uv add "a2a-sdk[grpc]"` | `pip install "a2a-sdk[grpc]"` |
6669
| **OpenTelemetry Tracing**| `uv add "a2a-sdk[telemetry]"` | `pip install "a2a-sdk[telemetry]"` |
6770
| **Encryption** | `uv add "a2a-sdk[encryption]"` | `pip install "a2a-sdk[encryption]"` |
71+
| **Vertex AI Task Store** | `uv add "a2a-sdk[vertex]"` | `pip install "a2a-sdk[vertex]"` |
6872
| | | |
6973
| **Database Drivers** | | |
7074
| **PostgreSQL** | `uv add "a2a-sdk[postgresql]"` | `pip install "a2a-sdk[postgresql]"` |

docs/ai/coding_conventions.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
### Coding Conventions & Style Guide
2+
3+
Non-negotiable rules for code quality and style.
4+
5+
1. **Python Types**: All Python code MUST include type hints. All function definitions MUST include return types.
6+
2. **Type Safety**: All code MUST pass `mypy` and `pyright` checks.
7+
3. **Formatting & Linting**: All code MUST be formatted with `ruff`.
8+
9+
#### Examples:
10+
11+
**Correct Typing:**
12+
```python
13+
async def get_task_status(task: Task) -> TaskStatus:
14+
return task.status
15+
```
16+
17+
**Incorrect (Do NOT do this):**
18+
```python
19+
async def get_task_status(task): # Missing type hints for argument and return value
20+
return task.status
21+
```

docs/ai/mandatory_checks.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
### Test and Fix Commands
2+
3+
Exact shell commands required to test the project and fix formatting issues.
4+
5+
1. **Formatting & Linting**:
6+
```bash
7+
uv run ruff check --fix
8+
uv run ruff format
9+
```
10+
11+
2. **Type Checking**:
12+
```bash
13+
uv run mypy src
14+
uv run pyright src
15+
```
16+
17+
3. **Testing**:
18+
```bash
19+
uv run pytest
20+
```
21+
22+
4. **Coverage**:
23+
Only run this command after adding new source code and before committing.
24+
```bash
25+
uv run pytest --cov=src --cov-report=term-missing
26+
```

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies = [
1515
"google-api-core>=1.26.0",
1616
"json-rpc>=1.15.0",
1717
"googleapis-common-protos>=1.70.0",
18+
"culsans>=0.11.0 ; python_full_version < '3.13'",
1819
]
1920

2021
classifiers = [

src/a2a/client/transports/http_helpers.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,22 @@ async def send_http_stream_request(
7878
async with aconnect_sse(
7979
httpx_client, method, url, **kwargs
8080
) as event_source:
81-
event_source.response.raise_for_status()
81+
try:
82+
event_source.response.raise_for_status()
83+
except httpx.HTTPStatusError as e:
84+
# Read upfront streaming error content immediately, otherwise lower-level handlers
85+
# (e.g. response.json()) crash with 'ResponseNotRead' Access errors.
86+
await event_source.response.aread()
87+
raise e
88+
89+
# If the response is not a stream, read it standardly (e.g., upfront JSON-RPC error payload)
90+
if 'text/event-stream' not in event_source.response.headers.get(
91+
'content-type', ''
92+
):
93+
content = await event_source.response.aread()
94+
yield content.decode('utf-8')
95+
return
96+
8297
async for sse in event_source.aiter_sse():
8398
if not sse.data:
8499
continue

src/a2a/compat/v0_3/grpc_handler.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from a2a.server.request_handlers.request_handler import RequestHandler
3030
from a2a.types.a2a_pb2 import AgentCard
3131
from a2a.utils.errors import A2AError, InvalidParamsError
32-
from a2a.utils.helpers import maybe_await, validate, validate_async_generator
32+
from a2a.utils.helpers import maybe_await, validate
3333

3434

3535
logger = logging.getLogger(__name__)
@@ -170,17 +170,17 @@ async def _handler(
170170
context, _handler, a2a_v0_3_pb2.SendMessageResponse()
171171
)
172172

173-
@validate_async_generator(
174-
lambda self: self.agent_card.capabilities.streaming,
175-
'Streaming is not supported by the agent',
176-
)
177173
async def SendStreamingMessage(
178174
self,
179175
request: a2a_v0_3_pb2.SendMessageRequest,
180176
context: grpc.aio.ServicerContext,
181177
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
182178
"""Handles the 'SendStreamingMessage' gRPC method (v0.3)."""
183179

180+
@validate(
181+
lambda _: self.agent_card.capabilities.streaming,
182+
'Streaming is not supported by the agent',
183+
)
184184
async def _handler(
185185
server_context: ServerCallContext,
186186
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
@@ -233,17 +233,17 @@ async def _handler(
233233

234234
return await self._handle_unary(context, _handler, a2a_v0_3_pb2.Task())
235235

236-
@validate_async_generator(
237-
lambda self: self.agent_card.capabilities.streaming,
238-
'Streaming is not supported by the agent',
239-
)
240236
async def TaskSubscription(
241237
self,
242238
request: a2a_v0_3_pb2.TaskSubscriptionRequest,
243239
context: grpc.aio.ServicerContext,
244240
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
245241
"""Handles the 'TaskSubscription' gRPC method (v0.3)."""
246242

243+
@validate(
244+
lambda _: self.agent_card.capabilities.streaming,
245+
'Streaming is not supported by the agent',
246+
)
247247
async def _handler(
248248
server_context: ServerCallContext,
249249
) -> AsyncIterable[a2a_v0_3_pb2.StreamResponse]:
@@ -260,17 +260,17 @@ async def _handler(
260260
async for item in self._handle_stream(context, _handler):
261261
yield item
262262

263-
@validate(
264-
lambda self: self.agent_card.capabilities.push_notifications,
265-
'Push notifications are not supported by the agent',
266-
)
267263
async def CreateTaskPushNotificationConfig(
268264
self,
269265
request: a2a_v0_3_pb2.CreateTaskPushNotificationConfigRequest,
270266
context: grpc.aio.ServicerContext,
271267
) -> a2a_v0_3_pb2.TaskPushNotificationConfig:
272268
"""Handles the 'CreateTaskPushNotificationConfig' gRPC method (v0.3)."""
273269

270+
@validate(
271+
lambda _: self.agent_card.capabilities.push_notifications,
272+
'Push notifications are not supported by the agent',
273+
)
274274
async def _handler(
275275
server_context: ServerCallContext,
276276
) -> a2a_v0_3_pb2.TaskPushNotificationConfig:

src/a2a/compat/v0_3/rest_handler.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from a2a.utils import constants
3232
from a2a.utils.helpers import (
3333
validate,
34-
validate_async_generator,
3534
validate_version,
3635
)
3736
from a2a.utils.telemetry import SpanKind, trace_class
@@ -85,7 +84,7 @@ async def on_message_send(
8584
return MessageToDict(pb2_v03_resp)
8685

8786
@validate_version(constants.PROTOCOL_VERSION_0_3)
88-
@validate_async_generator(
87+
@validate(
8988
lambda self: self.agent_card.capabilities.streaming,
9089
'Streaming is not supported by the agent',
9190
)
@@ -143,7 +142,7 @@ async def on_cancel_task(
143142
return MessageToDict(pb2_v03_task)
144143

145144
@validate_version(constants.PROTOCOL_VERSION_0_3)
146-
@validate_async_generator(
145+
@validate(
147146
lambda self: self.agent_card.capabilities.streaming,
148147
'Streaming is not supported by the agent',
149148
)

0 commit comments

Comments
 (0)