Skip to content

Commit 7a83c97

Browse files
authored
Merge branch 'main' into test/refactor-tests
2 parents 4f86b2c + b2e3a29 commit 7a83c97

File tree

11 files changed

+639
-23
lines changed

11 files changed

+639
-23
lines changed

.gemini/config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
code_review:
2+
comment_severity_threshold: LOW
3+
ignore_patterns: ['CHANGELOG.md']

.github/actions/spelling/allow.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ coc
2626
codegen
2727
coro
2828
datamodel
29+
deepwiki
2930
drivername
3031
DSNs
3132
dunders
@@ -80,5 +81,6 @@ tagwords
8081
taskupdate
8182
testuuid
8283
Tful
84+
tiangolo
8385
typeerror
8486
vulnz

.github/workflows/unit-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
run: |
5454
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
5555
- name: Install dependencies
56-
run: uv sync --dev --extra sql --extra encryption --extra grpc --extra telemetry
56+
run: uv sync --dev --extra all
5757
- name: Run tests and check coverage
5858
run: PYTHONPATH=. uv run pytest --cov=a2a --cov-report term --cov-fail-under=88
5959
- name: Show coverage summary in log

.ruff.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ ignore = [
3232
"TRY003",
3333
"TRY201",
3434
"FIX002",
35+
"UP038",
3536
]
3637

3738
select = [

README.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,17 @@ Install the core SDK and any desired extras using your preferred package manager
4747
| Feature | `uv` Command | `pip` Command |
4848
| ------------------------ | ------------------------------------------ | -------------------------------------------- |
4949
| **Core SDK** | `uv add a2a-sdk` | `pip install a2a-sdk` |
50+
| **All Extras** | `uv add a2a-sdk[all]` | `pip install a2a-sdk[all]` |
5051
| **HTTP Server** | `uv add "a2a-sdk[http-server]"` | `pip install "a2a-sdk[http-server]"` |
5152
| **gRPC Support** | `uv add "a2a-sdk[grpc]"` | `pip install "a2a-sdk[grpc]"` |
5253
| **OpenTelemetry Tracing**| `uv add "a2a-sdk[telemetry]"` | `pip install "a2a-sdk[telemetry]"` |
53-
54-
#### Database Support
55-
56-
Install the necessary drivers for your chosen SQL database.
57-
58-
| Database | `uv` Command | `pip` Command |
59-
| ------------- | ---------------------------------- | ------------------------------------ |
60-
| **PostgreSQL**| `uv add "a2a-sdk[postgresql]"` | `pip install "a2a-sdk[postgresql]"` |
61-
| **MySQL** | `uv add "a2a-sdk[mysql]"` | `pip install "a2a-sdk[mysql]"` |
62-
| **SQLite** | `uv add "a2a-sdk[sqlite]"` | `pip install "a2a-sdk[sqlite]"` |
63-
| **All SQL Drivers** | `uv add "a2a-sdk[sql]"` | `pip install "a2a-sdk[sql]"` |
54+
| **Encryption** | `uv add "a2a-sdk[encryption]"` | `pip install "a2a-sdk[encryption]"` |
55+
| | | |
56+
| **Database Drivers** | | |
57+
| **PostgreSQL** | `uv add "a2a-sdk[postgresql]"` | `pip install "a2a-sdk[postgresql]"` |
58+
| **MySQL** | `uv add "a2a-sdk[mysql]"` | `pip install "a2a-sdk[mysql]"` |
59+
| **SQLite** | `uv add "a2a-sdk[sqlite]"` | `pip install "a2a-sdk[sqlite]"` |
60+
| **All SQL Drivers** | `uv add "a2a-sdk[sql]"` | `pip install "a2a-sdk[sql]"` |
6461

6562
## Examples
6663

pyproject.toml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,22 @@ classifiers = [
3030

3131
[project.optional-dependencies]
3232
http-server = ["fastapi>=0.115.2", "sse-starlette", "starlette"]
33-
postgresql = ["sqlalchemy[asyncio,postgresql-asyncpg]>=2.0.0"]
34-
mysql = ["sqlalchemy[asyncio,aiomysql]>=2.0.0"]
35-
sqlite = ["sqlalchemy[asyncio,aiosqlite]>=2.0.0"]
36-
sql = ["sqlalchemy[asyncio,postgresql-asyncpg,aiomysql,aiosqlite]>=2.0.0"]
3733
encryption = ["cryptography>=43.0.0"]
3834
grpc = ["grpcio>=1.60", "grpcio-tools>=1.60", "grpcio_reflection>=1.7.0"]
3935
telemetry = ["opentelemetry-api>=1.33.0", "opentelemetry-sdk>=1.33.0"]
36+
postgresql = ["sqlalchemy[asyncio,postgresql-asyncpg]>=2.0.0"]
37+
mysql = ["sqlalchemy[asyncio,aiomysql]>=2.0.0"]
38+
sqlite = ["sqlalchemy[asyncio,aiosqlite]>=2.0.0"]
39+
40+
sql = ["a2a-sdk[postgresql,mysql,sqlite]"]
41+
42+
all = [
43+
"a2a-sdk[http-server]",
44+
"a2a-sdk[sql]",
45+
"a2a-sdk[encryption]",
46+
"a2a-sdk[grpc]",
47+
"a2a-sdk[telemetry]",
48+
]
4049

4150
[project.urls]
4251
homepage = "https://a2a-protocol.org/"

src/a2a/server/apps/jsonrpc/jsonrpc_app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
337337

338338
# 3) Build call context and wrap the request for downstream handling
339339
call_context = self._context_builder.build(request)
340+
call_context.state['method'] = method
340341

341342
request_id = specific_request.id
342343
a2a_request = A2ARequest(root=specific_request)

src/a2a/utils/proto_utils.py

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919

2020
# Regexp patterns for matching
21-
_TASK_NAME_MATCH = re.compile(r'tasks/([\w-]+)')
21+
_TASK_NAME_MATCH = re.compile(r'tasks/([^/]+)')
2222
_TASK_PUSH_CONFIG_NAME_MATCH = re.compile(
23-
r'tasks/([\w-]+)/pushNotificationConfigs/([\w-]+)'
23+
r'tasks/([^/]+)/pushNotificationConfigs/([^/]+)'
2424
)
2525

2626

@@ -46,6 +46,86 @@ def dict_to_struct(dictionary: dict[str, Any]) -> struct_pb2.Struct:
4646
return struct
4747

4848

49+
def make_dict_serializable(value: Any) -> Any:
50+
"""Dict pre-processing utility: converts non-serializable values to serializable form.
51+
52+
Use this when you want to normalize a dictionary before dict->Struct conversion.
53+
54+
Args:
55+
value: The value to convert.
56+
57+
Returns:
58+
A serializable value.
59+
"""
60+
if isinstance(value, (str, int, float, bool)) or value is None:
61+
return value
62+
if isinstance(value, dict):
63+
return {k: make_dict_serializable(v) for k, v in value.items()}
64+
if isinstance(value, list | tuple):
65+
return [make_dict_serializable(item) for item in value]
66+
return str(value)
67+
68+
69+
def normalize_large_integers_to_strings(
70+
value: Any, max_safe_digits: int = 15
71+
) -> Any:
72+
"""Integer preprocessing utility: converts large integers to strings.
73+
74+
Use this when you want to convert large integers to strings considering
75+
JavaScript's MAX_SAFE_INTEGER (2^53 - 1) limitation.
76+
77+
Args:
78+
value: The value to convert.
79+
max_safe_digits: Maximum safe integer digits (default: 15).
80+
81+
Returns:
82+
A normalized value.
83+
"""
84+
max_safe_int = 10**max_safe_digits - 1
85+
86+
def _normalize(item: Any) -> Any:
87+
if isinstance(item, int) and abs(item) > max_safe_int:
88+
return str(item)
89+
if isinstance(item, dict):
90+
return {k: _normalize(v) for k, v in item.items()}
91+
if isinstance(item, list | tuple):
92+
return [_normalize(i) for i in item]
93+
return item
94+
95+
return _normalize(value)
96+
97+
98+
def parse_string_integers_in_dict(value: Any, max_safe_digits: int = 15) -> Any:
99+
"""String post-processing utility: converts large integer strings back to integers.
100+
101+
Use this when you want to restore large integer strings to integers
102+
after Struct->dict conversion.
103+
104+
Args:
105+
value: The value to convert.
106+
max_safe_digits: Maximum safe integer digits (default: 15).
107+
108+
Returns:
109+
A parsed value.
110+
"""
111+
if isinstance(value, dict):
112+
return {
113+
k: parse_string_integers_in_dict(v, max_safe_digits)
114+
for k, v in value.items()
115+
}
116+
if isinstance(value, list | tuple):
117+
return [
118+
parse_string_integers_in_dict(item, max_safe_digits)
119+
for item in value
120+
]
121+
if isinstance(value, str):
122+
# Handle potential negative numbers.
123+
stripped_value = value.lstrip('-')
124+
if stripped_value.isdigit() and len(stripped_value) > max_safe_digits:
125+
return int(value)
126+
return value
127+
128+
49129
class ToProto:
50130
"""Converts Python types to proto types."""
51131

0 commit comments

Comments
 (0)