Skip to content

Commit ec92858

Browse files
fix: lint and formatting fixes for A2A lifecycle states
- Fix B904: add 'from None' to re-raised ServerError in cancel() - Fix F401: remove unused imports (TaskStatus, Part, TextPart, Role) in tests - Fix I001: sort import blocks in test files - Fix ruff formatting in executor.py and test_executor.py
1 parent 7ce5a09 commit ec92858

4 files changed

Lines changed: 51 additions & 39 deletions

File tree

src/strands/multiagent/a2a/executor.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,7 @@ async def execute(
107107
logger.exception("Agent execution failed, transitioning task to failed state")
108108
try:
109109
await updater.failed(
110-
message=updater.new_agent_message(
111-
parts=[Part(root=TextPart(text=f"Agent execution failed: {e}"))]
112-
)
110+
message=updater.new_agent_message(parts=[Part(root=TextPart(text=f"Agent execution failed: {e}"))])
113111
)
114112
except RuntimeError:
115113
# Task already in terminal state (e.g., completed before error in cleanup)
@@ -196,11 +194,7 @@ async def _handle_interrupt_result(self, result: SAAgentResult, updater: TaskUpd
196194

197195
input_message = "Agent requires input:\n" + "\n".join(interrupt_descriptions)
198196

199-
await updater.requires_input(
200-
message=updater.new_agent_message(
201-
parts=[Part(root=TextPart(text=input_message))]
202-
)
203-
)
197+
await updater.requires_input(message=updater.new_agent_message(parts=[Part(root=TextPart(text=input_message))]))
204198

205199
async def _handle_streaming_event(self, event: dict[str, Any], updater: TaskUpdater) -> None:
206200
"""Handle a single streaming event from the Strands Agent.
@@ -286,20 +280,18 @@ async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None
286280
task = context.current_task
287281
if not task:
288282
logger.warning("Cancellation requested but no current task found")
289-
raise ServerError(error=UnsupportedOperationError())
283+
raise ServerError(error=UnsupportedOperationError()) from None
290284

291285
updater = TaskUpdater(event_queue, task.id, task.context_id)
292286

293287
try:
294288
await updater.cancel(
295-
message=updater.new_agent_message(
296-
parts=[Part(root=TextPart(text="Task cancelled by client request"))]
297-
)
289+
message=updater.new_agent_message(parts=[Part(root=TextPart(text="Task cancelled by client request"))])
298290
)
299291
except RuntimeError:
300292
# Task already in terminal state
301293
logger.warning("Cannot cancel task %s: already in terminal state", task.id)
302-
raise ServerError(error=UnsupportedOperationError())
294+
raise ServerError(error=UnsupportedOperationError()) from None
303295

304296
def _get_file_type_from_mime_type(self, mime_type: str | None) -> Literal["document", "image", "video", "unknown"]:
305297
"""Classify file type based on MIME type.

tests/strands/agent/test_a2a_agent.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,8 @@ async def mock_send_message(*args, **kwargs):
724724
def test_is_complete_event_failed_state(a2a_agent):
725725
"""Test that failed state is recognized as complete."""
726726
from unittest.mock import MagicMock
727-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
727+
728+
from a2a.types import TaskState, TaskStatusUpdateEvent
728729

729730
task = MagicMock()
730731
status = MagicMock()
@@ -738,7 +739,8 @@ def test_is_complete_event_failed_state(a2a_agent):
738739
def test_is_complete_event_canceled_state(a2a_agent):
739740
"""Test that canceled state is recognized as complete."""
740741
from unittest.mock import MagicMock
741-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
742+
743+
from a2a.types import TaskState, TaskStatusUpdateEvent
742744

743745
task = MagicMock()
744746
status = MagicMock()
@@ -752,7 +754,8 @@ def test_is_complete_event_canceled_state(a2a_agent):
752754
def test_is_complete_event_rejected_state(a2a_agent):
753755
"""Test that rejected state is recognized as complete."""
754756
from unittest.mock import MagicMock
755-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
757+
758+
from a2a.types import TaskState, TaskStatusUpdateEvent
756759

757760
task = MagicMock()
758761
status = MagicMock()
@@ -766,7 +769,8 @@ def test_is_complete_event_rejected_state(a2a_agent):
766769
def test_is_complete_event_input_required_state(a2a_agent):
767770
"""Test that input_required state is recognized as complete (pausing)."""
768771
from unittest.mock import MagicMock
769-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
772+
773+
from a2a.types import TaskState, TaskStatusUpdateEvent
770774

771775
task = MagicMock()
772776
status = MagicMock()
@@ -780,7 +784,8 @@ def test_is_complete_event_input_required_state(a2a_agent):
780784
def test_is_complete_event_auth_required_state(a2a_agent):
781785
"""Test that auth_required state is recognized as complete (pausing)."""
782786
from unittest.mock import MagicMock
783-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
787+
788+
from a2a.types import TaskState, TaskStatusUpdateEvent
784789

785790
task = MagicMock()
786791
status = MagicMock()
@@ -794,7 +799,8 @@ def test_is_complete_event_auth_required_state(a2a_agent):
794799
def test_is_complete_event_working_state_not_complete(a2a_agent):
795800
"""Test that working state is NOT recognized as complete."""
796801
from unittest.mock import MagicMock
797-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
802+
803+
from a2a.types import TaskState, TaskStatusUpdateEvent
798804

799805
task = MagicMock()
800806
status = MagicMock()
@@ -808,7 +814,8 @@ def test_is_complete_event_working_state_not_complete(a2a_agent):
808814
def test_is_complete_event_submitted_state_not_complete(a2a_agent):
809815
"""Test that submitted state is NOT recognized as complete."""
810816
from unittest.mock import MagicMock
811-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
817+
818+
from a2a.types import TaskState, TaskStatusUpdateEvent
812819

813820
task = MagicMock()
814821
status = MagicMock()

tests/strands/multiagent/a2a/test_converters.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ def test_convert_response_handles_missing_data():
253253
def test_convert_response_completed_state_maps_to_end_turn():
254254
"""Test that completed state maps to end_turn stop_reason."""
255255
from unittest.mock import MagicMock
256-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus, Part, TextPart
256+
257+
from a2a.types import TaskState, TaskStatus, TaskStatusUpdateEvent
257258

258259
task = MagicMock()
259260
task.artifacts = None
@@ -269,7 +270,8 @@ def test_convert_response_completed_state_maps_to_end_turn():
269270
def test_convert_response_failed_state_maps_to_end_turn():
270271
"""Test that failed state maps to end_turn stop_reason with error content."""
271272
from unittest.mock import MagicMock
272-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus, Message, Part, TextPart, Role
273+
274+
from a2a.types import Message, TaskState, TaskStatus, TaskStatusUpdateEvent
273275

274276
task = MagicMock()
275277
task.artifacts = None
@@ -295,7 +297,8 @@ def test_convert_response_failed_state_maps_to_end_turn():
295297
def test_convert_response_input_required_maps_to_interrupt():
296298
"""Test that input_required state maps to interrupt stop_reason."""
297299
from unittest.mock import MagicMock
298-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus, Message, Part, TextPart, Role
300+
301+
from a2a.types import Message, TaskState, TaskStatus, TaskStatusUpdateEvent
299302

300303
task = MagicMock()
301304
task.artifacts = None
@@ -320,7 +323,8 @@ def test_convert_response_input_required_maps_to_interrupt():
320323
def test_convert_response_canceled_state_maps_to_end_turn():
321324
"""Test that canceled state maps to end_turn stop_reason."""
322325
from unittest.mock import MagicMock
323-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
326+
327+
from a2a.types import TaskState, TaskStatus, TaskStatusUpdateEvent
324328

325329
task = MagicMock()
326330
task.artifacts = None
@@ -337,7 +341,8 @@ def test_convert_response_canceled_state_maps_to_end_turn():
337341
def test_convert_response_rejected_state_maps_to_end_turn():
338342
"""Test that rejected state maps to end_turn stop_reason."""
339343
from unittest.mock import MagicMock
340-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
344+
345+
from a2a.types import TaskState, TaskStatus, TaskStatusUpdateEvent
341346

342347
task = MagicMock()
343348
task.artifacts = None
@@ -354,7 +359,8 @@ def test_convert_response_rejected_state_maps_to_end_turn():
354359
def test_convert_response_auth_required_maps_to_interrupt():
355360
"""Test that auth_required state maps to interrupt stop_reason."""
356361
from unittest.mock import MagicMock
357-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
362+
363+
from a2a.types import TaskState, TaskStatus, TaskStatusUpdateEvent
358364

359365
task = MagicMock()
360366
task.artifacts = None
@@ -371,7 +377,9 @@ def test_convert_response_auth_required_maps_to_interrupt():
371377
def test_extract_task_state_from_status_update():
372378
"""Test _extract_task_state helper."""
373379
from unittest.mock import MagicMock
374-
from a2a.types import TaskState, TaskStatusUpdateEvent, TaskStatus
380+
381+
from a2a.types import TaskState, TaskStatus, TaskStatusUpdateEvent
382+
375383
from strands.multiagent.a2a._converters import _extract_task_state
376384

377385
task = MagicMock()
@@ -386,7 +394,9 @@ def test_extract_task_state_from_status_update():
386394
def test_extract_task_state_from_message_returns_none():
387395
"""Test _extract_task_state returns None for Message responses."""
388396
from unittest.mock import MagicMock
397+
389398
from a2a.types import Message
399+
390400
from strands.multiagent.a2a._converters import _extract_task_state
391401

392402
message = MagicMock(spec=Message)

tests/strands/multiagent/a2a/test_executor.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -616,10 +616,10 @@ async def test_execute_streaming_mode_handles_agent_exception(
616616

617617
# Verify a failed status event was enqueued
618618
enqueued_events = [call[0][0] for call in mock_event_queue.enqueue_event.call_args_list]
619-
from a2a.types import TaskStatusUpdateEvent, TaskState
619+
from a2a.types import TaskState, TaskStatusUpdateEvent
620+
620621
failed_events = [
621-
e for e in enqueued_events
622-
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.failed
622+
e for e in enqueued_events if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.failed
623623
]
624624
assert len(failed_events) == 1
625625
assert "Agent execution failed" in failed_events[0].status.message.parts[0].root.text
@@ -633,6 +633,7 @@ async def test_execute_streaming_mode_handles_agent_exception(
633633
# Verify the error is a ServerError containing an UnsupportedOperationError
634634
assert isinstance(excinfo.value.error, UnsupportedOperationError)
635635

636+
636637
@pytest.mark.asyncio
637638
async def test_handle_agent_result_with_none_result(mock_strands_agent, mock_request_context, mock_event_queue):
638639
"""Test that _handle_agent_result handles None result correctly."""
@@ -1379,8 +1380,7 @@ async def mock_stream(content_blocks, **kwargs):
13791380
# Verify failed state was enqueued
13801381
enqueued_events = [call[0][0] for call in mock_event_queue.enqueue_event.call_args_list]
13811382
failed_events = [
1382-
e for e in enqueued_events
1383-
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.failed
1383+
e for e in enqueued_events if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.failed
13841384
]
13851385
assert len(failed_events) == 1
13861386
assert "Connection lost" in failed_events[0].status.message.parts[0].root.text
@@ -1403,8 +1403,7 @@ async def test_cancel_with_valid_task(mock_strands_agent, mock_request_context,
14031403
# Verify canceled state was enqueued
14041404
enqueued_events = [call[0][0] for call in mock_event_queue.enqueue_event.call_args_list]
14051405
canceled_events = [
1406-
e for e in enqueued_events
1407-
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.canceled
1406+
e for e in enqueued_events if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.canceled
14081407
]
14091408
assert len(canceled_events) == 1
14101409
assert "cancelled" in canceled_events[0].status.message.parts[0].root.text.lower()
@@ -1428,6 +1427,7 @@ async def test_execute_with_interrupt_transitions_to_input_required(
14281427
):
14291428
"""Test that agent interrupts map to input_required state."""
14301429
from a2a.types import TaskState, TaskStatusUpdateEvent, TextPart
1430+
14311431
from strands.interrupt import Interrupt
14321432

14331433
# Create a mock result with interrupts
@@ -1462,7 +1462,8 @@ async def mock_stream(content_blocks, **kwargs):
14621462
# Verify input_required state was enqueued
14631463
enqueued_events = [call[0][0] for call in mock_event_queue.enqueue_event.call_args_list]
14641464
input_required_events = [
1465-
e for e in enqueued_events
1465+
e
1466+
for e in enqueued_events
14661467
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.input_required
14671468
]
14681469
assert len(input_required_events) == 1
@@ -1475,6 +1476,7 @@ async def mock_stream(content_blocks, **kwargs):
14751476
async def test_execute_with_multiple_interrupts(mock_strands_agent, mock_request_context, mock_event_queue):
14761477
"""Test handling of multiple interrupts in a single result."""
14771478
from a2a.types import TaskState, TaskStatusUpdateEvent, TextPart
1479+
14781480
from strands.interrupt import Interrupt
14791481

14801482
mock_result = MagicMock(spec=SAAgentResult)
@@ -1508,7 +1510,8 @@ async def mock_stream(content_blocks, **kwargs):
15081510

15091511
enqueued_events = [call[0][0] for call in mock_event_queue.enqueue_event.call_args_list]
15101512
input_required_events = [
1511-
e for e in enqueued_events
1513+
e
1514+
for e in enqueued_events
15121515
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.input_required
15131516
]
15141517
assert len(input_required_events) == 1
@@ -1555,14 +1558,14 @@ async def mock_stream(content_blocks, **kwargs):
15551558
# Verify completed state was enqueued (not input_required)
15561559
enqueued_events = [call[0][0] for call in mock_event_queue.enqueue_event.call_args_list]
15571560
completed_events = [
1558-
e for e in enqueued_events
1559-
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.completed
1561+
e for e in enqueued_events if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.completed
15601562
]
15611563
assert len(completed_events) == 1
15621564

15631565
# Verify no input_required events
15641566
input_required_events = [
1565-
e for e in enqueued_events
1567+
e
1568+
for e in enqueued_events
15661569
if isinstance(e, TaskStatusUpdateEvent) and e.status.state == TaskState.input_required
15671570
]
15681571
assert len(input_required_events) == 0

0 commit comments

Comments
 (0)