Skip to content

Commit 4d58b9e

Browse files
committed
Refactor to also make starlette optional
1 parent 2cf402c commit 4d58b9e

File tree

7 files changed

+145
-157
lines changed

7 files changed

+145
-157
lines changed

README.md

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,40 +26,28 @@ You can install the A2A SDK using either `uv` or `pip`.
2626

2727
### Using `uv`
2828

29-
When you're working within a uv project or a virtual environment managed by uv, the preferred way to add packages is using uv add.
30-
3129
```bash
3230
uv add a2a-sdk
3331
```
3432

35-
### Using `pip`
36-
37-
If you prefer to use pip, the standard Python package installer, you can install `a2a-sdk` as follows
33+
To include the optional HTTP server components (FastAPI, Starlette), install the `http-server` extra:
3834

3935
```bash
40-
pip install a2a-sdk
36+
uv add a2a-sdk[http-server]
4137
```
4238

43-
### Optional Dependencies
44-
45-
#### FastAPI-based A2A Server App
46-
47-
If you plan to build a server application using the `A2AFastAPIApplication` helper, you will need to install the `fastapi` package. This is an optional dependency.
48-
49-
You can install it with the `fastapi` extra:
39+
### Using `pip`
5040

51-
```shell
52-
# Using uv
53-
uv add a2a-sdk[fastapi]
41+
You can install `a2a-sdk` using pip, the standard Python package installer.
5442

55-
# Using pip
56-
pip install "a2a-sdk[fastapi]"
43+
```bash
44+
pip install a2a-sdk
5745
```
5846

59-
Alternatively, if you manage your dependencies separately, you can add `fastapi` to your project directly:
47+
To include the optional HTTP server components (FastAPI, Starlette), install the `http-server` extra:
6048

61-
```shell
62-
uv add fastapi
49+
```bash
50+
pip install a2a-sdk[http-server]
6351
```
6452

6553
## Examples

pyproject.toml

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,16 @@ authors = [{ name = "Google LLC", email = "googleapis-packages@google.com" }]
88
requires-python = ">=3.10"
99
keywords = ["A2A", "A2A SDK", "A2A Protocol", "Agent2Agent"]
1010
dependencies = [
11-
"httpx>=0.28.1",
12-
"httpx-sse>=0.4.0",
13-
"google-api-core>=1.26.0",
14-
"opentelemetry-api>=1.33.0",
15-
"opentelemetry-sdk>=1.33.0",
16-
"pydantic>=2.11.3",
17-
"sse-starlette",
18-
"starlette",
19-
"grpcio>=1.60",
20-
"grpcio-tools>=1.60",
21-
"grpcio_reflection>=1.7.0",
22-
"protobuf==6.31.1",
11+
"httpx>=0.28.1",
12+
"httpx-sse>=0.4.0",
13+
"google-api-core>=1.26.0",
14+
"opentelemetry-api>=1.33.0",
15+
"opentelemetry-sdk>=1.33.0",
16+
"pydantic>=2.11.3",
17+
"grpcio>=1.60",
18+
"grpcio-tools>=1.60",
19+
"grpcio_reflection>=1.7.0",
20+
"protobuf==5.29.5",
2321
]
2422

2523
classifiers = [
@@ -35,6 +33,13 @@ classifiers = [
3533
"License :: OSI Approved :: Apache Software License",
3634
]
3735

36+
[project.optional-dependencies]
37+
http-server = [
38+
"fastapi>=0.115.2",
39+
"sse-starlette",
40+
"starlette",
41+
]
42+
3843
[project.urls]
3944
homepage = "https://google-a2a.github.io/A2A/"
4045
repository = "https://github.com/google-a2a/a2a-python"
@@ -69,21 +74,20 @@ style = "pep440"
6974

7075
[dependency-groups]
7176
dev = [
72-
"datamodel-code-generator>=0.30.0",
73-
"mypy>=1.15.0",
74-
"pytest>=8.3.5",
75-
"pytest-asyncio>=0.26.0",
76-
"pytest-cov>=6.1.1",
77-
"pytest-mock>=3.14.0",
78-
"respx>=0.20.2",
79-
"ruff>=0.11.6",
80-
"uv-dynamic-versioning>=0.8.2",
81-
"types-protobuf",
82-
"types-requests",
83-
"fastapi>=0.115.2",
84-
]
85-
fastapi = [
86-
"fastapi>=0.115.2",
77+
"datamodel-code-generator>=0.30.0",
78+
"mypy>=1.15.0",
79+
"pytest>=8.3.5",
80+
"pytest-asyncio>=0.26.0",
81+
"pytest-cov>=6.1.1",
82+
"pytest-mock>=3.14.0",
83+
"respx>=0.20.2",
84+
"ruff>=0.11.6",
85+
"uv-dynamic-versioning>=0.8.2",
86+
"types-protobuf",
87+
"types-requests",
88+
"fastapi>=0.115.2",
89+
"sse-starlette",
90+
"starlette",
8791
]
8892

8993
[[tool.uv.index]]

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
from typing import Any
44

5-
from a2a.server.apps.jsonrpc.fastapi_import_helpers import (
6-
FastAPI,
7-
Request,
8-
Response,
9-
)
5+
6+
try:
7+
from fastapi import FastAPI, Request, Response
8+
except ImportError:
9+
FastAPI = object
10+
Request = object
11+
Response = object
12+
1013
from a2a.server.apps.jsonrpc.jsonrpc_app import (
1114
CallContextBuilder,
1215
JSONRPCApplication,

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

Lines changed: 0 additions & 37 deletions
This file was deleted.

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,23 @@
4242
)
4343
from a2a.utils.errors import MethodNotImplementedError
4444

45-
4645
logger = logging.getLogger(__name__)
4746

47+
try:
48+
from fastapi import FastAPI
49+
from sse_starlette.sse import EventSourceResponse
50+
from starlette.applications import Starlette
51+
from starlette.authentication import BaseUser
52+
from starlette.requests import Request
53+
from starlette.responses import JSONResponse, Response
54+
55+
_http_server_installed = True
56+
except ImportError:
57+
_http_server_installed = False
58+
# Define placeholder types for type hinting and to avoid import errors in other files.
59+
# These will not be used at runtime if deps are missing, as __init__ will raise.
60+
FastAPI, EventSourceResponse, Starlette, BaseUser, Request, JSONResponse, Response = (object,) * 7
61+
4862

4963
class StarletteUserProxy(A2AUser):
5064
"""Adapts the Starlette User class to the A2A user representation."""
@@ -108,7 +122,7 @@ def __init__(
108122
extended_agent_card: AgentCard | None = None,
109123
context_builder: CallContextBuilder | None = None,
110124
):
111-
"""Initializes the A2AStarletteApplication.
125+
"""Initializes the JSONRPCApplication.
112126
113127
Args:
114128
agent_card: The AgentCard describing the agent's capabilities.
@@ -120,6 +134,10 @@ def __init__(
120134
ServerCallContext passed to the http_handler. If None, no
121135
ServerCallContext is passed.
122136
"""
137+
if not _http_server_installed:
138+
raise ImportError(
139+
'The `a2a-sdk[http-server]` package is required to use the `JSONRPCApplication`.'
140+
)
123141
self.agent_card = agent_card
124142
self.extended_agent_card = extended_agent_card
125143
self.handler = JSONRPCHandler(
@@ -163,7 +181,7 @@ def _generate_error_response(
163181
log_level,
164182
f'Request Error (ID: {request_id}): '
165183
f"Code={error_resp.error.code}, Message='{error_resp.error.message}'"
166-
f'{", Data=" + str(error_resp.error.data) if error_resp.error.data else ""}',
184+
f'{", Data=" str(error_resp.error.data) if error_resp.error.data else ""}',
167185
)
168186
return JSONResponse(
169187
error_resp.model_dump(mode='json', exclude_none=True),
@@ -411,7 +429,7 @@ def build(
411429
agent_card_url: str = '/.well-known/agent.json',
412430
rpc_url: str = '/',
413431
**kwargs: Any,
414-
) -> FastAPI | Starlette:
432+
) -> 'FastAPI' | Starlette':
415433
"""Builds and returns the JSONRPC application instance.
416434
417435
Args:

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
from typing import Any
44

5-
from starlette.applications import Starlette
6-
from starlette.routing import Route
5+
6+
try:
7+
from starlette.applications import Starlette
8+
from starlette.routing import Route
9+
except ImportError:
10+
Starlette = object
11+
Route = object
712

813
from a2a.server.apps.jsonrpc.jsonrpc_app import (
914
CallContextBuilder,

0 commit comments

Comments
 (0)