Skip to content

Commit df60860

Browse files
committed
Add support for status detail updates
1 parent 908d0d7 commit df60860

3 files changed

Lines changed: 105 additions & 0 deletions

File tree

src/a2a/contrib/tasks/vertex_task_store.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,32 @@ def _get_status_change_event(
8080
)
8181
return None
8282

83+
def _get_status_details_change_event(
84+
self,
85+
previous_task: Task,
86+
task: Task,
87+
event_sequence_number: int,
88+
) -> vertexai_types.TaskEvent | None:
89+
if task.status.message != previous_task.status.message:
90+
status_details = (
91+
vertexai_types.TaskStatusDetails(
92+
task_message=vertex_task_converter.to_stored_message(
93+
task.status.message
94+
)
95+
)
96+
if task.status.message
97+
else vertexai_types.TaskStatusDetails()
98+
)
99+
return vertexai_types.TaskEvent(
100+
event_data=vertexai_types.TaskEventData(
101+
status_details_change=vertexai_types.TaskStatusDetailsChange(
102+
new_task_status=status_details,
103+
),
104+
),
105+
event_sequence_number=event_sequence_number,
106+
)
107+
return None
108+
83109
def _get_metadata_change_event(
84110
self, previous_task: Task, task: Task, event_sequence_number: int
85111
) -> vertexai_types.TaskEvent | None:
@@ -158,6 +184,13 @@ async def _update(
158184
events.append(status_event)
159185
event_sequence_number += 1
160186

187+
status_details_event = self._get_status_details_change_event(
188+
previous_task, task, event_sequence_number
189+
)
190+
if status_details_event:
191+
events.append(status_details_event)
192+
event_sequence_number += 1
193+
161194
metadata_event = self._get_metadata_change_event(
162195
previous_task, task, event_sequence_number
163196
)

tests/contrib/tasks/fake_vertex_client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ async def append(
3636
data = event.event_data
3737
if getattr(data, 'state_change', None):
3838
task.state = getattr(data.state_change, 'new_state', task.state)
39+
if getattr(data, 'status_details_change', None):
40+
task.status_details = getattr(
41+
data.status_details_change,
42+
'new_task_status',
43+
getattr(task, 'status_details', None),
44+
)
3945
if getattr(data, 'metadata_change', None):
4046
task.metadata = getattr(
4147
data.metadata_change, 'new_metadata', task.metadata

tests/contrib/tasks/test_vertex_task_store.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ def backend_type(request) -> str:
6363
from a2a.contrib.tasks.vertex_task_store import VertexTaskStore
6464
from a2a.types import (
6565
Artifact,
66+
Message,
6667
Part,
68+
Role,
6769
Task,
6870
TaskState,
6971
TaskStatus,
@@ -504,3 +506,67 @@ async def test_metadata_field_mapping(
504506
retrieved_none = await vertex_store.get('task-metadata-test-4')
505507
assert retrieved_none is not None
506508
assert retrieved_none.metadata == {}
509+
510+
511+
@pytest.mark.asyncio
512+
async def test_update_task_status_details(
513+
vertex_store: VertexTaskStore,
514+
) -> None:
515+
"""Test updating an existing task by changing the status details (message) with part metadata."""
516+
task_id = 'update-test-task-status-details'
517+
original_task = Task(
518+
id=task_id,
519+
context_id='session-update',
520+
status=TaskStatus(state=TaskState.submitted),
521+
kind='task',
522+
metadata=None,
523+
artifacts=[],
524+
history=[],
525+
)
526+
await vertex_store.save(original_task)
527+
528+
retrieved_before_update = await vertex_store.get(task_id)
529+
assert retrieved_before_update is not None
530+
assert retrieved_before_update.status.message is None
531+
532+
updated_task = original_task.model_copy(deep=True)
533+
updated_task.status.state = TaskState.failed
534+
updated_task.status.timestamp = '2023-01-02T11:00:00Z'
535+
updated_task.status.message = Message(
536+
message_id='msg-error-1',
537+
role=Role.agent,
538+
parts=[
539+
Part(
540+
root=TextPart(
541+
text='Task failed due to an unknown error',
542+
metadata={'error_code': 'UNKNOWN', 'retryable': False},
543+
)
544+
)
545+
],
546+
)
547+
548+
await vertex_store.save(updated_task)
549+
550+
retrieved_after_update = await vertex_store.get(task_id)
551+
assert retrieved_after_update is not None
552+
assert retrieved_after_update.status.state == TaskState.failed
553+
assert retrieved_after_update.status.message is not None
554+
assert retrieved_after_update.status.message.message_id == 'msg-error-1'
555+
assert retrieved_after_update.status.message.role == Role.agent
556+
assert len(retrieved_after_update.status.message.parts) == 1
557+
558+
assert isinstance(
559+
retrieved_after_update.status.message.parts[0].root, TextPart
560+
)
561+
text_part = retrieved_after_update.status.message.parts[0].root
562+
assert text_part.text == 'Task failed due to an unknown error'
563+
assert text_part.metadata == {'error_code': 'UNKNOWN', 'retryable': False}
564+
565+
# Also test clearing the message
566+
cleared_task = updated_task.model_copy(deep=True)
567+
cleared_task.status.message = None
568+
569+
await vertex_store.save(cleared_task)
570+
retrieved_cleared = await vertex_store.get(task_id)
571+
assert retrieved_cleared is not None
572+
assert retrieved_cleared.status.message is None

0 commit comments

Comments
 (0)