Skip to content

Commit eccaa80

Browse files
committed
Move payload reduction to default_request_handler
1 parent 32cc97c commit eccaa80

File tree

9 files changed

+98
-34
lines changed

9 files changed

+98
-34
lines changed

src/a2a/server/request_handlers/default_request_handler.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,22 @@ async def on_list_tasks(
131131
) -> ListTasksResult:
132132
"""Default handler for 'tasks/list'."""
133133
page = await self.task_store.list(params, context)
134+
processed_tasks = []
135+
for task in page.tasks:
136+
processed_task = task
137+
if params.include_artifacts is not True:
138+
processed_task = processed_task.model_copy(
139+
update={'artifacts': None}
140+
)
141+
if params.history_length is not None:
142+
processed_task = apply_history_length(
143+
processed_task, params.history_length
144+
)
145+
processed_tasks.append(processed_task)
134146
return ListTasksResult(
135147
next_page_token=page.next_page_token,
136148
page_size=params.page_size or DEFAULT_LIST_TASKS_PAGE_SIZE,
137-
tasks=page.tasks,
149+
tasks=processed_tasks,
138150
total_size=page.total_size,
139151
)
140152

src/a2a/server/tasks/inmemory_task_store.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import asyncio
22
import logging
33

4-
from typing import Any
5-
64
from a2a.server.context import ServerCallContext
75
from a2a.server.tasks.task_store import TaskStore, TasksPage
86
from a2a.types import ListTasksParams, Task
@@ -65,22 +63,6 @@ async def list(
6563
task for task in tasks if task.status.state == params.status
6664
]
6765

68-
# Reduce payload
69-
base_updates: dict[str, Any] = {}
70-
if not params.include_artifacts:
71-
base_updates = {'artifacts': []}
72-
for i in range(len(tasks)):
73-
updates = dict(base_updates)
74-
history = tasks[i].history
75-
if params.history_length is not None and history:
76-
limited_history = (
77-
history[-params.history_length :]
78-
if params.history_length > 0
79-
else []
80-
)
81-
updates['history'] = limited_history
82-
tasks[i] = tasks[i].model_copy(update=updates)
83-
8466
# Apply pagination
8567
total_size = len(tasks)
8668
page_token = int(params.page_token) if params.page_token else 0

tests/client/test_grpc_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
AgentCard,
1111
Artifact,
1212
GetTaskPushNotificationConfigParams,
13+
ListTasksParams,
1314
Message,
1415
MessageSendParams,
1516
Part,
@@ -25,7 +26,6 @@
2526
TaskStatus,
2627
TaskStatusUpdateEvent,
2728
TextPart,
28-
ListTasksParams,
2929
)
3030
from a2a.utils import get_text_parts, proto_utils
3131
from a2a.utils.errors import ServerError

tests/client/test_jsonrpc_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
AgentCard,
2323
AgentSkill,
2424
InvalidParamsError,
25+
ListTasksParams,
26+
ListTasksResult,
2527
Message,
2628
MessageSendParams,
2729
PushNotificationConfig,
@@ -31,8 +33,6 @@
3133
TaskIdParams,
3234
TaskPushNotificationConfig,
3335
TaskQueryParams,
34-
ListTasksParams,
35-
ListTasksResult,
3636
)
3737
from a2a.utils import AGENT_CARD_WELL_KNOWN_PATH
3838

tests/server/request_handlers/test_default_request_handler.py

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from a2a.server.context import ServerCallContext
2222
from a2a.server.events import EventQueue, InMemoryQueueManager, QueueManager
2323
from a2a.server.request_handlers import DefaultRequestHandler
24-
from a2a.server.tasks.task_store import TasksPage
2524
from a2a.server.tasks import (
2625
InMemoryPushNotificationConfigStore,
2726
InMemoryTaskStore,
@@ -31,14 +30,15 @@
3130
TaskStore,
3231
TaskUpdater,
3332
)
33+
from a2a.server.tasks.task_store import TasksPage
3434
from a2a.types import (
35+
Artifact,
3536
DeleteTaskPushNotificationConfigParams,
3637
GetTaskPushNotificationConfigParams,
3738
InternalError,
3839
InvalidParamsError,
3940
ListTaskPushNotificationConfigParams,
4041
ListTasksParams,
41-
ListTasksResult,
4242
Message,
4343
MessageSendConfiguration,
4444
MessageSendParams,
@@ -56,9 +56,7 @@
5656
TextPart,
5757
UnsupportedOperationError,
5858
)
59-
from a2a.utils import (
60-
new_task,
61-
)
59+
from a2a.utils import new_agent_text_message, new_task
6260

6361

6462
class DummyAgentExecutor(AgentExecutor):
@@ -155,15 +153,25 @@ async def test_on_list_tasks_success():
155153
mock_page = MagicMock(spec=TasksPage)
156154
mock_page.tasks = [
157155
create_sample_task(task_id='task1'),
158-
create_sample_task(task_id='task2'),
156+
create_sample_task(task_id='task2').model_copy(
157+
update={
158+
'artifacts': [
159+
Artifact(
160+
artifact_id='artifact1',
161+
parts=[Part(root=TextPart(text='Hello world!'))],
162+
name='conversion_result',
163+
)
164+
]
165+
}
166+
),
159167
]
160168
mock_page.next_page_token = '123'
161169
mock_page.total_size = 2
162170
mock_task_store.list.return_value = mock_page
163171
request_handler = DefaultRequestHandler(
164172
agent_executor=DummyAgentExecutor(), task_store=mock_task_store
165173
)
166-
params = ListTasksParams(page_size=10)
174+
params = ListTasksParams(include_artifacts=True, page_size=10)
167175
context = create_server_call_context()
168176

169177
result = await request_handler.on_list_tasks(params, context)
@@ -175,6 +183,68 @@ async def test_on_list_tasks_success():
175183
assert result.page_size == params.page_size
176184

177185

186+
@pytest.mark.asyncio
187+
async def test_on_list_tasks_excludes_artifacts():
188+
"""Test on_list_tasks excludes artifacts from returned tasks."""
189+
mock_task_store = AsyncMock(spec=TaskStore)
190+
mock_page = MagicMock(spec=TasksPage)
191+
mock_page.tasks = [
192+
create_sample_task(task_id='task1'),
193+
create_sample_task(task_id='task2').model_copy(
194+
update={
195+
'artifacts': [
196+
Artifact(
197+
artifact_id='artifact1',
198+
parts=[Part(root=TextPart(text='Hello world!'))],
199+
name='conversion_result',
200+
)
201+
]
202+
}
203+
),
204+
]
205+
mock_page.next_page_token = '123'
206+
mock_page.total_size = 2
207+
mock_task_store.list.return_value = mock_page
208+
request_handler = DefaultRequestHandler(
209+
agent_executor=DummyAgentExecutor(), task_store=mock_task_store
210+
)
211+
params = ListTasksParams(include_artifacts=False, page_size=10)
212+
context = create_server_call_context()
213+
214+
result = await request_handler.on_list_tasks(params, context)
215+
216+
assert result.tasks[1].artifacts == None
217+
218+
219+
@pytest.mark.asyncio
220+
async def test_on_list_tasks_applies_history_length():
221+
"""Test on_list_tasks applies history length filter."""
222+
mock_task_store = AsyncMock(spec=TaskStore)
223+
mock_page = MagicMock(spec=TasksPage)
224+
history = [
225+
new_agent_text_message('Hello 1!'),
226+
new_agent_text_message('Hello 2!'),
227+
]
228+
mock_page.tasks = [
229+
create_sample_task(task_id='task1'),
230+
create_sample_task(task_id='task2').model_copy(
231+
update={'history': history}
232+
),
233+
]
234+
mock_page.next_page_token = '123'
235+
mock_page.total_size = 2
236+
mock_task_store.list.return_value = mock_page
237+
request_handler = DefaultRequestHandler(
238+
agent_executor=DummyAgentExecutor(), task_store=mock_task_store
239+
)
240+
params = ListTasksParams(history_length=1, page_size=10)
241+
context = create_server_call_context()
242+
243+
result = await request_handler.on_list_tasks(params, context)
244+
245+
assert result.tasks[1].history == [history[1]]
246+
247+
178248
@pytest.mark.asyncio
179249
async def test_on_cancel_task_task_not_found():
180250
"""Test on_cancel_task when the task is not found."""

tests/server/request_handlers/test_jsonrpc_handler.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
GetAuthenticatedExtendedCardRequest,
3737
GetAuthenticatedExtendedCardResponse,
3838
GetAuthenticatedExtendedCardSuccessResponse,
39-
ListTasksResult,
4039
GetTaskPushNotificationConfigParams,
4140
GetTaskPushNotificationConfigRequest,
4241
GetTaskPushNotificationConfigResponse,
@@ -51,7 +50,7 @@
5150
ListTaskPushNotificationConfigSuccessResponse,
5251
ListTasksParams,
5352
ListTasksRequest,
54-
ListTasksResponse,
53+
ListTasksResult,
5554
ListTasksSuccessResponse,
5655
Message,
5756
MessageSendConfiguration,

tests/server/tasks/test_database_task_store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
from a2a.server.tasks.database_task_store import DatabaseTaskStore
2020
from a2a.types import (
2121
Artifact,
22+
ListTasksParams,
2223
Message,
2324
Part,
2425
Role,
2526
Task,
2627
TaskState,
2728
TaskStatus,
2829
TextPart,
29-
ListTasksParams,
3030
)
3131

3232

tests/server/tasks/test_inmemory_task_store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44

55
from a2a.server.tasks import InMemoryTaskStore
6-
from a2a.types import Task, ListTasksParams, TaskState, TaskStatus
6+
from a2a.types import ListTasksParams, Task, TaskState, TaskStatus
77

88

99
MINIMAL_TASK: dict[str, Any] = {

tests/utils/test_proto_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import pytest
44

5+
from google.protobuf.timestamp_pb2 import Timestamp
6+
57
from a2a import types
68
from a2a.grpc import a2a_pb2
79
from a2a.utils import proto_utils
810
from a2a.utils.errors import ServerError
9-
from google.protobuf.timestamp_pb2 import Timestamp
1011

1112

1213
# --- Test Data ---

0 commit comments

Comments
 (0)