Skip to content

Commit 9f773ef

Browse files
authored
docs: Write/Update Docstrings for Classes/Methods (#59)
1 parent 16195d2 commit 9f773ef

40 files changed

+1411
-1107
lines changed

.github/actions/spelling/line_forbidden.patterns

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@
295295
(?!'")[‘’“”]
296296

297297
# "an" should only be before vowels.
298-
\ban\s+(?![FHLMNRSX][A-Z0-9]+\b)(?!hour\b)(?!honest\b)([b-df-hj-np-tv-zB-DF-HJ-NP-TV-Z]{1}\w*)
298+
\ban\s+(?![FHLMNRSX][A-Z0-9]+\b)(?!hour\b)(?!honest\b)(?!httpx?\b)([b-df-hj-np-tv-zB-DF-HJ-NP-TV-Z]{1}\w*)
299299

300300
# Don't use Google internal links
301301
((corp|prod|sandbox).google.com|googleplex.com|https?://(?!localhost/)[0-9a-z][0-9a-z-]+/|(?:^|[^/.-])\b(?:go|b|cl|cr)/[a-z0-9_.-]+\b)

.github/linters/.mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[mypy]
22
exclude = examples/
3-
disable_error_code = import-not-found
3+
disable_error_code = import-not-found,annotation-unchecked
44

55
[mypy-examples.*]
66
follow_imports = skip

src/a2a/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""The A2A Python SDK."""

src/a2a/client/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Client-side components for interacting with an A2A agent."""
2+
13
from a2a.client.client import A2ACardResolver, A2AClient
24
from a2a.client.errors import (
35
A2AClientError,

src/a2a/client/client.py

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,34 @@ def __init__(
3636
base_url: str,
3737
agent_card_path: str = '/.well-known/agent.json',
3838
):
39+
"""Initializes the A2ACardResolver.
40+
41+
Args:
42+
httpx_client: An async HTTP client instance (e.g., httpx.AsyncClient).
43+
base_url: The base URL of the agent's host.
44+
agent_card_path: The path to the agent card endpoint, relative to the base URL.
45+
"""
3946
self.base_url = base_url.rstrip('/')
4047
self.agent_card_path = agent_card_path.lstrip('/')
4148
self.httpx_client = httpx_client
4249

4350
async def get_agent_card(
4451
self, http_kwargs: dict[str, Any] | None = None
4552
) -> AgentCard:
53+
"""Fetches the agent card from the specified URL.
54+
55+
Args:
56+
http_kwargs: Optional dictionary of keyword arguments to pass to the
57+
underlying httpx.get request.
58+
59+
Returns:
60+
An `AgentCard` object representing the agent's capabilities.
61+
62+
Raises:
63+
A2AClientHTTPError: If an HTTP error occurs during the request.
64+
A2AClientJSONError: If the response body cannot be decoded as JSON
65+
or validated against the AgentCard schema.
66+
"""
4667
try:
4768
response = await self.httpx_client.get(
4869
f'{self.base_url}/{self.agent_card_path}',
@@ -62,14 +83,26 @@ async def get_agent_card(
6283

6384
@trace_class(kind=SpanKind.CLIENT)
6485
class A2AClient:
65-
"""A2A Client."""
86+
"""A2A Client for interacting with an A2A agent."""
6687

6788
def __init__(
6889
self,
6990
httpx_client: httpx.AsyncClient,
7091
agent_card: AgentCard | None = None,
7192
url: str | None = None,
7293
):
94+
"""Initializes the A2AClient.
95+
96+
Requires either an `AgentCard` or a direct `url` to the agent's RPC endpoint.
97+
98+
Args:
99+
httpx_client: An async HTTP client instance (e.g., httpx.AsyncClient).
100+
agent_card: The agent card object. If provided, `url` is taken from `agent_card.url`.
101+
url: The direct URL to the agent's A2A RPC endpoint. Required if `agent_card` is None.
102+
103+
Raises:
104+
ValueError: If neither `agent_card` nor `url` is provided.
105+
"""
73106
if agent_card:
74107
self.url = agent_card.url
75108
elif url:
@@ -86,7 +119,22 @@ async def get_client_from_agent_card_url(
86119
agent_card_path: str = '/.well-known/agent.json',
87120
http_kwargs: dict[str, Any] | None = None,
88121
) -> 'A2AClient':
89-
"""Get a A2A client for provided agent card URL."""
122+
"""Fetches the AgentCard and initializes an A2A client.
123+
124+
Args:
125+
httpx_client: An async HTTP client instance (e.g., httpx.AsyncClient).
126+
base_url: The base URL of the agent's host.
127+
agent_card_path: The path to the agent card endpoint, relative to the base URL.
128+
http_kwargs: Optional dictionary of keyword arguments to pass to the
129+
underlying httpx.get request when fetching the agent card.
130+
131+
Returns:
132+
An initialized `A2AClient` instance.
133+
134+
Raises:
135+
A2AClientHTTPError: If an HTTP error occurs fetching the agent card.
136+
A2AClientJSONError: If the agent card response is invalid.
137+
"""
90138
agent_card: AgentCard = await A2ACardResolver(
91139
httpx_client, base_url=base_url, agent_card_path=agent_card_path
92140
).get_agent_card(http_kwargs=http_kwargs)
@@ -98,6 +146,20 @@ async def send_message(
98146
*,
99147
http_kwargs: dict[str, Any] | None = None,
100148
) -> SendMessageResponse:
149+
"""Sends a non-streaming message request to the agent.
150+
151+
Args:
152+
request: The `SendMessageRequest` object containing the message and configuration.
153+
http_kwargs: Optional dictionary of keyword arguments to pass to the
154+
underlying httpx.post request.
155+
156+
Returns:
157+
A `SendMessageResponse` object containing the agent's response (Task or Message) or an error.
158+
159+
Raises:
160+
A2AClientHTTPError: If an HTTP error occurs during the request.
161+
A2AClientJSONError: If the response body cannot be decoded as JSON or validated.
162+
"""
101163
if not request.id:
102164
request.id = str(uuid4())
103165

@@ -114,6 +176,23 @@ async def send_message_streaming(
114176
*,
115177
http_kwargs: dict[str, Any] | None = None,
116178
) -> AsyncGenerator[SendStreamingMessageResponse]:
179+
"""Sends a streaming message request to the agent and yields responses as they arrive.
180+
181+
This method uses Server-Sent Events (SSE) to receive a stream of updates from the agent.
182+
183+
Args:
184+
request: The `SendStreamingMessageRequest` object containing the message and configuration.
185+
http_kwargs: Optional dictionary of keyword arguments to pass to the
186+
underlying httpx.post request. A default `timeout=None` is set but can be overridden.
187+
188+
Yields:
189+
`SendStreamingMessageResponse` objects as they are received in the SSE stream.
190+
These can be Task, Message, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent.
191+
192+
Raises:
193+
A2AClientHTTPError: If an HTTP or SSE protocol error occurs during the request.
194+
A2AClientJSONError: If an SSE event data cannot be decoded as JSON or validated.
195+
"""
117196
if not request.id:
118197
request.id = str(uuid4())
119198

@@ -153,8 +232,16 @@ async def _send_request(
153232
"""Sends a non-streaming JSON-RPC request to the agent.
154233
155234
Args:
156-
rpc_request_payload: JSON RPC payload for sending the request
157-
**kwargs: Additional keyword arguments to pass to the httpx client.
235+
rpc_request_payload: JSON RPC payload for sending the request.
236+
http_kwargs: Optional dictionary of keyword arguments to pass to the
237+
underlying httpx.post request.
238+
239+
Returns:
240+
The JSON response payload as a dictionary.
241+
242+
Raises:
243+
A2AClientHTTPError: If an HTTP error occurs during the request.
244+
A2AClientJSONError: If the response body cannot be decoded as JSON.
158245
"""
159246
try:
160247
response = await self.httpx_client.post(
@@ -177,6 +264,20 @@ async def get_task(
177264
*,
178265
http_kwargs: dict[str, Any] | None = None,
179266
) -> GetTaskResponse:
267+
"""Retrieves the current state and history of a specific task.
268+
269+
Args:
270+
request: The `GetTaskRequest` object specifying the task ID and history length.
271+
http_kwargs: Optional dictionary of keyword arguments to pass to the
272+
underlying httpx.post request.
273+
274+
Returns:
275+
A `GetTaskResponse` object containing the Task or an error.
276+
277+
Raises:
278+
A2AClientHTTPError: If an HTTP error occurs during the request.
279+
A2AClientJSONError: If the response body cannot be decoded as JSON or validated.
280+
"""
180281
if not request.id:
181282
request.id = str(uuid4())
182283

@@ -193,6 +294,20 @@ async def cancel_task(
193294
*,
194295
http_kwargs: dict[str, Any] | None = None,
195296
) -> CancelTaskResponse:
297+
"""Requests the agent to cancel a specific task.
298+
299+
Args:
300+
request: The `CancelTaskRequest` object specifying the task ID.
301+
http_kwargs: Optional dictionary of keyword arguments to pass to the
302+
underlying httpx.post request.
303+
304+
Returns:
305+
A `CancelTaskResponse` object containing the updated Task with canceled status or an error.
306+
307+
Raises:
308+
A2AClientHTTPError: If an HTTP error occurs during the request.
309+
A2AClientJSONError: If the response body cannot be decoded as JSON or validated.
310+
"""
196311
if not request.id:
197312
request.id = str(uuid4())
198313

@@ -209,6 +324,20 @@ async def set_task_callback(
209324
*,
210325
http_kwargs: dict[str, Any] | None = None,
211326
) -> SetTaskPushNotificationConfigResponse:
327+
"""Sets or updates the push notification configuration for a specific task.
328+
329+
Args:
330+
request: The `SetTaskPushNotificationConfigRequest` object specifying the task ID and configuration.
331+
http_kwargs: Optional dictionary of keyword arguments to pass to the
332+
underlying httpx.post request.
333+
334+
Returns:
335+
A `SetTaskPushNotificationConfigResponse` object containing the confirmation or an error.
336+
337+
Raises:
338+
A2AClientHTTPError: If an HTTP error occurs during the request.
339+
A2AClientJSONError: If the response body cannot be decoded as JSON or validated.
340+
"""
212341
if not request.id:
213342
request.id = str(uuid4())
214343

@@ -225,6 +354,20 @@ async def get_task_callback(
225354
*,
226355
http_kwargs: dict[str, Any] | None = None,
227356
) -> GetTaskPushNotificationConfigResponse:
357+
"""Retrieves the push notification configuration for a specific task.
358+
359+
Args:
360+
request: The `GetTaskPushNotificationConfigRequest` object specifying the task ID.
361+
http_kwargs: Optional dictionary of keyword arguments to pass to the
362+
underlying httpx.post request.
363+
364+
Returns:
365+
A `GetTaskPushNotificationConfigResponse` object containing the configuration or an error.
366+
367+
Raises:
368+
A2AClientHTTPError: If an HTTP error occurs during the request.
369+
A2AClientJSONError: If the response body cannot be decoded as JSON or validated.
370+
"""
228371
if not request.id:
229372
request.id = str(uuid4())
230373

src/a2a/client/errors.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
1+
"""Custom exceptions for the A2A client."""
2+
3+
14
class A2AClientError(Exception):
2-
"""Base exception for client A2A Client errors."""
5+
"""Base exception for A2A Client errors."""
36

47

58
class A2AClientHTTPError(A2AClientError):
6-
"""Client exception for HTTP errors."""
9+
"""Client exception for HTTP errors received from the server."""
710

811
def __init__(self, status_code: int, message: str):
12+
"""Initializes the A2AClientHTTPError.
13+
14+
Args:
15+
status_code: The HTTP status code of the response.
16+
message: A descriptive error message.
17+
"""
918
self.status_code = status_code
1019
self.message = message
1120
super().__init__(f'HTTP Error {status_code}: {message}')
1221

1322

1423
class A2AClientJSONError(A2AClientError):
15-
"""Client exception for JSON errors."""
24+
"""Client exception for JSON errors during response parsing or validation."""
1625

1726
def __init__(self, message: str):
27+
"""Initializes the A2AClientJSONError.
28+
29+
Args:
30+
message: A descriptive error message.
31+
"""
1832
self.message = message
1933
super().__init__(f'JSON Error: {message}')

src/a2a/client/helpers.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Helper functions for the A2A client."""
2+
13
from uuid import uuid4
24

35
from a2a.types import Message, Part, Role, TextPart
@@ -6,7 +8,15 @@
68
def create_text_message_object(
79
role: Role = Role.user, content: str = ''
810
) -> Message:
9-
"""Create a Message object for the given role and content."""
11+
"""Create a Message object containing a single TextPart.
12+
13+
Args:
14+
role: The role of the message sender (user or agent). Defaults to Role.user.
15+
content: The text content of the message. Defaults to an empty string.
16+
17+
Returns:
18+
A `Message` object with a new UUID messageId.
19+
"""
1020
return Message(
1121
role=role, parts=[Part(TextPart(text=content))], messageId=str(uuid4())
1222
)

src/a2a/server/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Server-side components for implementing an A2A agent."""

src/a2a/server/agent_execution/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Components for executing agent logic within the A2A server."""
2+
13
from a2a.server.agent_execution.agent_executor import AgentExecutor
24
from a2a.server.agent_execution.context import RequestContext
35

src/a2a/server/agent_execution/agent_executor.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,36 @@
55

66

77
class AgentExecutor(ABC):
8-
"""Agent Executor interface."""
8+
"""Agent Executor interface.
9+
10+
Implementations of this interface contain the core logic of the agent,
11+
executing tasks based on requests and publishing updates to an event queue.
12+
"""
913

1014
@abstractmethod
1115
async def execute(self, context: RequestContext, event_queue: EventQueue):
12-
pass
16+
"""Execute the agent's logic for a given request context.
17+
18+
The agent should read necessary information from the `context` and
19+
publish `Task` or `Message` events, or `TaskStatusUpdateEvent` /
20+
`TaskArtifactUpdateEvent` to the `event_queue`. This method should
21+
return once the agent's execution for this request is complete or
22+
yields control (e.g., enters an input-required state).
23+
24+
Args:
25+
context: The request context containing the message, task ID, etc.
26+
event_queue: The queue to publish events to.
27+
"""
1328

1429
@abstractmethod
1530
async def cancel(self, context: RequestContext, event_queue: EventQueue):
16-
pass
31+
"""Request the agent to cancel an ongoing task.
32+
33+
The agent should attempt to stop the task identified by the task_id
34+
in the context and publish a `TaskStatusUpdateEvent` with state
35+
`TaskState.canceled` to the `event_queue`.
36+
37+
Args:
38+
context: The request context containing the task ID to cancel.
39+
event_queue: The queue to publish the cancellation status update to.
40+
"""

0 commit comments

Comments
 (0)