[BREAKING] Python: Refactor workflow events to unified discriminated union pattern (#3690)

* Refactor events

* Merge main

* Fixes

* Cleanup

* Update samples and tests

* Remove unused imports

* PR feedback

* Merge main. Add properties for events to help typing

* Formatting

* Cleanup

* use builtins.type to avoid shadowing by WorkflowEvent.type attribute

* Final improvements
This commit is contained in:
Evan Mattson
2026-02-06 16:47:20 +09:00
committed by GitHub
Unverified
parent 09f59b21ad
commit 0f3f4dbcaf
127 changed files with 1646 additions and 1703 deletions
+23 -24
View File
@@ -32,10 +32,8 @@ from agent_framework import (
from agent_framework._clients import TOptions_co
from agent_framework._workflows._agent_executor import AgentExecutorResponse
from agent_framework._workflows._events import (
ExecutorCompletedEvent,
ExecutorFailedEvent,
ExecutorInvokedEvent,
WorkflowErrorDetails,
WorkflowEvent,
)
from agent_framework.orchestrations import ConcurrentBuilder, SequentialBuilder
@@ -284,7 +282,8 @@ def _create_agent_executor_response(
executor_id: str = "test_executor",
response_text: str = "Executor response",
) -> AgentExecutorResponse:
"""Create an AgentExecutorResponse - the type that's nested in ExecutorCompletedEvent.data."""
"""Create an AgentExecutorResponse - the type that's nested in
executor_completed event (type='executor_completed').data."""
agent_response = _create_agent_run_response(response_text)
return AgentExecutorResponse(
executor_id=executor_id,
@@ -306,32 +305,32 @@ def create_agent_run_response(text: str = "Test response") -> AgentResponse:
return _create_agent_run_response(text)
def create_executor_invoked_event(executor_id: str = "test_executor") -> ExecutorInvokedEvent:
"""Create an ExecutorInvokedEvent."""
return ExecutorInvokedEvent(executor_id=executor_id)
def create_executor_invoked_event(executor_id: str = "test_executor") -> WorkflowEvent[Any]:
"""Create a WorkflowEvent(type='executor_invoked')."""
return WorkflowEvent.executor_invoked(executor_id=executor_id)
def create_executor_completed_event(
executor_id: str = "test_executor",
with_agent_response: bool = True,
) -> ExecutorCompletedEvent:
"""Create an ExecutorCompletedEvent with realistic nested data.
) -> WorkflowEvent[Any]:
"""Create a WorkflowEvent(type='executor_completed') with realistic nested data.
This creates the exact data structure that caused the serialization bug:
ExecutorCompletedEvent.data contains AgentExecutorResponse which contains
WorkflowEvent.data contains AgentExecutorResponse which contains
AgentResponse and ChatMessage objects (SerializationMixin, not Pydantic).
"""
data = _create_agent_executor_response(executor_id) if with_agent_response else {"simple": "dict"}
return ExecutorCompletedEvent(executor_id=executor_id, data=data)
return WorkflowEvent.executor_completed(executor_id=executor_id, data=data)
def create_executor_failed_event(
executor_id: str = "test_executor",
error_message: str = "Test error",
) -> ExecutorFailedEvent:
"""Create an ExecutorFailedEvent."""
) -> WorkflowEvent[WorkflowErrorDetails]:
"""Create a WorkflowEvent(type='executor_failed')."""
details = WorkflowErrorDetails(error_type="TestError", message=error_message)
return ExecutorFailedEvent(executor_id=executor_id, details=details)
return WorkflowEvent.executor_failed(executor_id=executor_id, details=details)
# =============================================================================
@@ -386,28 +385,28 @@ def agent_run_response() -> AgentResponse:
@pytest.fixture
def executor_completed_event() -> ExecutorCompletedEvent:
"""Create an ExecutorCompletedEvent with realistic nested data.
def executor_completed_event() -> WorkflowEvent[Any]:
"""Create a WorkflowEvent(type='executor_completed') with realistic nested data.
This creates the exact data structure that caused the serialization bug:
ExecutorCompletedEvent.data contains AgentExecutorResponse which contains
executor_completed event (type='executor_completed').data contains AgentExecutorResponse which contains
AgentResponse and ChatMessage objects (SerializationMixin, not Pydantic).
"""
data = _create_agent_executor_response("test_executor")
return ExecutorCompletedEvent(executor_id="test_executor", data=data)
return WorkflowEvent.executor_completed(executor_id="test_executor", data=data)
@pytest.fixture
def executor_invoked_event() -> ExecutorInvokedEvent:
"""Create an ExecutorInvokedEvent."""
return ExecutorInvokedEvent(executor_id="test_executor")
def executor_invoked_event() -> WorkflowEvent[Any]:
"""Create a WorkflowEvent(type='executor_invoked')."""
return WorkflowEvent.executor_invoked(executor_id="test_executor")
@pytest.fixture
def executor_failed_event() -> ExecutorFailedEvent:
"""Create an ExecutorFailedEvent."""
def executor_failed_event() -> WorkflowEvent[WorkflowErrorDetails]:
"""Create a WorkflowEvent(type='executor_failed')."""
details = WorkflowErrorDetails(error_type="TestError", message="Test error")
return ExecutorFailedEvent(executor_id="test_executor", details=details)
return WorkflowEvent.executor_failed(executor_id="test_executor", details=details)
@pytest.fixture
@@ -8,10 +8,8 @@ import pytest
from agent_framework import (
Executor,
InMemoryCheckpointStorage,
RequestInfoEvent,
WorkflowBuilder,
WorkflowContext,
WorkflowStatusEvent,
handler,
response_handler,
)
@@ -428,13 +426,13 @@ class TestIntegration:
# Run workflow until it reaches IDLE_WITH_PENDING_REQUESTS (after checkpoint is created)
saw_request_event = False
async for event in test_workflow.run(WorkflowTestData(value="test"), stream=True):
if isinstance(event, RequestInfoEvent):
if event.type == "request_info":
saw_request_event = True
# Wait for IDLE_WITH_PENDING_REQUESTS status (comes after checkpoint creation)
if isinstance(event, WorkflowStatusEvent) and "IDLE_WITH_PENDING_REQUESTS" in str(event.state):
if event.type == "status" and "IDLE_WITH_PENDING_REQUESTS" in str(event.state):
break
assert saw_request_event, "Test workflow should have emitted RequestInfoEvent"
assert saw_request_event, "Test workflow should have emitted request_info event (type='request_info')"
# Verify checkpoint was AUTOMATICALLY saved to our storage by the framework
checkpoints_after = await checkpoint_storage.list_checkpoints()
@@ -292,7 +292,7 @@ async def test_full_pipeline_workflow_events_are_json_serializable():
"""CRITICAL TEST: Verify ALL events from workflow execution can be JSON serialized.
This is particularly important for workflows with AgentExecutor because:
- AgentExecutor produces ExecutorCompletedEvent with AgentExecutorResponse
- AgentExecutor produces executor_completed event (type='executor_completed') with AgentExecutorResponse
- AgentExecutorResponse contains AgentResponse and ChatMessage objects
- These are SerializationMixin objects, not Pydantic, which caused the original bug
@@ -672,10 +672,10 @@ async def test_full_pipeline_concurrent_workflow(concurrent_workflow):
@pytest.mark.asyncio
async def test_full_pipeline_workflow_output_event_serialization():
"""Test that WorkflowOutputEvent from ctx.yield_output() serializes correctly.
"""Test that output event (type='output') from ctx.yield_output() serializes correctly.
This tests the pattern where executors yield output via ctx.yield_output(),
which emits WorkflowOutputEvent that DevUI must serialize for SSE.
which emits output event (type='output') that DevUI must serialize for SSE.
"""
from agent_framework import Executor, WorkflowBuilder, WorkflowContext, handler
@@ -19,9 +19,8 @@ from agent_framework._types import (
# Import real workflow event classes - NOT mocks!
from agent_framework._workflows._events import (
ExecutorCompletedEvent,
WorkflowStartedEvent,
WorkflowStatusEvent,
WorkflowEvent,
WorkflowRunState,
)
# Import factory functions from conftest for parameterized test data creation
@@ -261,7 +260,7 @@ async def test_agent_run_response_mapping(mapper: MessageMapper, test_request: A
async def test_executor_invoked_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test ExecutorInvokedEvent using the REAL class from agent_framework."""
"""Test WorkflowEvent(type='executor_invoked') using the REAL class from agent_framework."""
# Use real class, not mock!
event = create_executor_invoked_event(executor_id="exec_123")
@@ -277,9 +276,9 @@ async def test_executor_invoked_event(mapper: MessageMapper, test_request: Agent
async def test_executor_completed_event_simple_data(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test ExecutorCompletedEvent with simple dict data."""
"""Test WorkflowEvent(type='executor_completed') with simple dict data."""
# Create event with simple data
event = ExecutorCompletedEvent(executor_id="exec_123", data={"simple": "result"})
event = WorkflowEvent.executor_completed(executor_id="exec_123", data={"simple": "result"})
# First need to invoke the executor to set up context
invoke_event = create_executor_invoked_event(executor_id="exec_123")
@@ -301,10 +300,10 @@ async def test_executor_completed_event_simple_data(mapper: MessageMapper, test_
async def test_executor_completed_event_with_agent_response(
mapper: MessageMapper, test_request: AgentFrameworkRequest
) -> None:
"""Test ExecutorCompletedEvent with nested AgentExecutorResponse.
"""Test WorkflowEvent(type='executor_completed') with nested AgentExecutorResponse.
This is a REGRESSION TEST for the serialization bug where
ExecutorCompletedEvent.data contained AgentExecutorResponse with nested
WorkflowEvent.data contained AgentExecutorResponse with nested
AgentResponse and ChatMessage objects (SerializationMixin) that
Pydantic couldn't serialize.
"""
@@ -374,7 +373,7 @@ async def test_executor_completed_event_serialization_to_json(
async def test_executor_failed_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test ExecutorFailedEvent using the REAL class."""
"""Test WorkflowEvent(type='executor_failed') using the REAL class."""
# First invoke the executor
invoke_event = create_executor_invoked_event(executor_id="exec_fail")
await mapper.convert_event(invoke_event, test_request)
@@ -398,22 +397,21 @@ async def test_executor_failed_event(mapper: MessageMapper, test_request: AgentF
async def test_workflow_started_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowStartedEvent using the REAL class."""
"""Test WorkflowEvent(type='started') using the REAL class."""
event = WorkflowStartedEvent(data=None)
event = WorkflowEvent.started()
events = await mapper.convert_event(event, test_request)
# WorkflowStartedEvent should emit response.created and response.in_progress
# WorkflowEvent(type='started') should emit response.created and response.in_progress
assert len(events) == 2
assert events[0].type == "response.created"
assert events[1].type == "response.in_progress"
async def test_workflow_status_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowStatusEvent using the REAL class."""
from agent_framework._workflows._events import WorkflowRunState
"""Test WorkflowEvent(type='status') using the REAL class."""
event = WorkflowStatusEvent(state=WorkflowRunState.IN_PROGRESS)
event = WorkflowEvent.status(state=WorkflowRunState.IN_PROGRESS)
events = await mapper.convert_event(event, test_request)
# Should emit some status-related event
@@ -421,20 +419,20 @@ async def test_workflow_status_event(mapper: MessageMapper, test_request: AgentF
# =============================================================================
# Magentic Event Tests - Testing WorkflowOutputEvent with additional_properties
# Magentic Event Tests - Testing WorkflowEvent[AgentResponseUpdate] with additional_properties
# =============================================================================
async def test_magentic_agent_run_update_event_with_agent_delta_metadata(
async def test_magentic_executor_event_with_agent_delta_metadata(
mapper: MessageMapper, test_request: AgentFrameworkRequest
) -> None:
"""Test that WorkflowOutputEvent with magentic_event_type='agent_delta' is handled correctly.
"""Test that WorkflowEvent[AgentResponseUpdate] with magentic_event_type='agent_delta' is handled correctly.
This tests the ACTUAL event format Magentic emits - not a fake MagenticAgentDeltaEvent class.
Magentic uses WorkflowOutputEvent wrapping AgentResponseUpdate with additional_properties.
Magentic uses WorkflowEvent.emit() with additional_properties containing magentic_event_type.
"""
from agent_framework._types import AgentResponseUpdate
from agent_framework._workflows._events import WorkflowOutputEvent
from agent_framework._workflows._events import WorkflowEvent
# Create the REAL event format that Magentic emits
update = AgentResponseUpdate(
@@ -446,11 +444,11 @@ async def test_magentic_agent_run_update_event_with_agent_delta_metadata(
"agent_id": "writer_agent",
},
)
event = WorkflowOutputEvent(executor_id="magentic_executor", data=update)
event = WorkflowEvent.emit(executor_id="magentic_executor", data=update)
events = await mapper.convert_event(event, test_request)
# Should be treated as a regular WorkflowOutputEvent with text content
# Should be treated as a regular WorkflowEvent[AgentResponseUpdate] with text content
# The mapper should emit text delta events
assert len(events) >= 1
text_events = [e for e in events if getattr(e, "type", "") == "response.output_text.delta"]
@@ -459,13 +457,13 @@ async def test_magentic_agent_run_update_event_with_agent_delta_metadata(
async def test_magentic_orchestrator_message_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test that WorkflowOutputEvent with magentic_event_type='orchestrator_message' is handled.
"""Test that WorkflowEvent[AgentResponseUpdate] with magentic_event_type='orchestrator_message' is handled.
Magentic emits orchestrator planning/instruction messages using WorkflowOutputEvent
wrapping AgentResponseUpdate with additional_properties.
Magentic emits orchestrator planning/instruction messages using WorkflowEvent.emit()
with additional_properties containing magentic_event_type='orchestrator_message'.
"""
from agent_framework._types import AgentResponseUpdate
from agent_framework._workflows._events import WorkflowOutputEvent
from agent_framework._workflows._events import WorkflowEvent
# Create orchestrator message event (REAL format from Magentic)
update = AgentResponseUpdate(
@@ -478,11 +476,11 @@ async def test_magentic_orchestrator_message_event(mapper: MessageMapper, test_r
"orchestrator_id": "magentic_orchestrator",
},
)
event = WorkflowOutputEvent(executor_id="magentic_orchestrator", data=update)
event = WorkflowEvent.emit(executor_id="magentic_orchestrator", data=update)
events = await mapper.convert_event(event, test_request)
# Currently, mapper treats this as regular WorkflowOutputEvent (no special handling)
# Currently, mapper treats this as regular WorkflowEvent[AgentResponseUpdate] (no special handling)
# This test documents the current behavior
assert len(events) >= 1
text_events = [e for e in events if getattr(e, "type", "") == "response.output_text.delta"]
@@ -493,15 +491,15 @@ async def test_magentic_orchestrator_message_event(mapper: MessageMapper, test_r
async def test_magentic_events_use_same_event_class_as_other_workflows(
mapper: MessageMapper, test_request: AgentFrameworkRequest
) -> None:
"""Verify Magentic uses the same WorkflowOutputEvent class as other workflows.
"""Verify Magentic uses the same WorkflowEvent class as other workflows.
This test documents that Magentic does NOT define separate event classes like
MagenticAgentDeltaEvent - it reuses WorkflowOutputEvent with metadata in
MagenticAgentDeltaEvent - it reuses WorkflowEvent with metadata in
additional_properties. Any mapper code checking for 'MagenticAgentDeltaEvent'
class names is dead code.
"""
from agent_framework._types import AgentResponseUpdate
from agent_framework._workflows._events import WorkflowOutputEvent
from agent_framework._workflows._events import WorkflowEvent
# Create events the way different workflows do it
# 1. Regular workflow (no additional_properties)
@@ -509,7 +507,7 @@ async def test_magentic_events_use_same_event_class_as_other_workflows(
contents=[Content.from_text(text="Regular workflow response")],
role="assistant",
)
regular_event = WorkflowOutputEvent(executor_id="regular_executor", data=regular_update)
regular_event = WorkflowEvent.emit(executor_id="regular_executor", data=regular_update)
# 2. Magentic workflow (with additional_properties)
magentic_update = AgentResponseUpdate(
@@ -517,12 +515,12 @@ async def test_magentic_events_use_same_event_class_as_other_workflows(
role="assistant",
additional_properties={"magentic_event_type": "agent_delta"},
)
magentic_event = WorkflowOutputEvent(executor_id="magentic_executor", data=magentic_update)
magentic_event = WorkflowEvent.emit(executor_id="magentic_executor", data=magentic_update)
# Both should be the SAME class
assert type(regular_event) is type(magentic_event)
assert isinstance(regular_event, WorkflowOutputEvent)
assert isinstance(magentic_event, WorkflowOutputEvent)
assert isinstance(regular_event, WorkflowEvent)
assert isinstance(magentic_event, WorkflowEvent)
# Both should be handled by the same isinstance check in mapper
regular_events = await mapper.convert_event(regular_event, test_request)
@@ -559,18 +557,18 @@ async def test_unknown_content_fallback(mapper: MessageMapper, test_request: Age
# =============================================================================
# WorkflowOutputEvent Tests
# output event (type='output') Tests
# =============================================================================
async def test_workflow_output_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowOutputEvent is converted to output_item.added."""
from agent_framework._workflows._events import WorkflowOutputEvent
"""Test output event (type='output') is converted to output_item.added."""
from agent_framework._workflows._events import WorkflowEvent
event = WorkflowOutputEvent(data="Final workflow output", executor_id="final_executor")
event = WorkflowEvent.output(executor_id="final_executor", data="Final workflow output")
events = await mapper.convert_event(event, test_request)
# WorkflowOutputEvent should emit output_item.added
# output event (type='output') should emit output_item.added
assert len(events) == 1
assert events[0].type == "response.output_item.added"
# Check item contains the output text
@@ -580,16 +578,16 @@ async def test_workflow_output_event(mapper: MessageMapper, test_request: AgentF
async def test_workflow_output_event_with_list_data(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowOutputEvent with list data (common for sequential/concurrent workflows)."""
"""Test output event (type='output') with list data (common for sequential/concurrent workflows)."""
from agent_framework import ChatMessage
from agent_framework._workflows._events import WorkflowOutputEvent
from agent_framework._workflows._events import WorkflowEvent
# Sequential/Concurrent workflows often output list[ChatMessage]
messages = [
ChatMessage(role="user", contents=[Content.from_text(text="Hello")]),
ChatMessage(role="assistant", contents=[Content.from_text(text="World")]),
]
event = WorkflowOutputEvent(data=messages, executor_id="complete")
event = WorkflowEvent.output(executor_id="complete", data=messages)
events = await mapper.convert_event(event, test_request)
assert len(events) == 1
@@ -597,23 +595,23 @@ async def test_workflow_output_event_with_list_data(mapper: MessageMapper, test_
# =============================================================================
# WorkflowFailedEvent Tests
# failed event (type='failed') Tests
# =============================================================================
async def test_workflow_failed_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowFailedEvent is converted to response.failed."""
from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowFailedEvent
"""Test failed event (type='failed') is converted to response.failed."""
from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowEvent
details = WorkflowErrorDetails(
error_type="TestError",
message="Workflow failed due to test error",
executor_id="failing_executor",
)
event = WorkflowFailedEvent(details=details)
event = WorkflowEvent.failed(details=details)
events = await mapper.convert_event(event, test_request)
# WorkflowFailedEvent should emit response.failed
# failed event (type='failed') should emit response.failed
assert len(events) >= 1
# Find the failed event
failed_events = [e for e in events if getattr(e, "type", "") == "response.failed"]
@@ -628,8 +626,8 @@ async def test_workflow_failed_event(mapper: MessageMapper, test_request: AgentF
async def test_workflow_failed_event_with_extra(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowFailedEvent includes extra context when available."""
from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowFailedEvent
"""Test failed event (type='failed') includes extra context when available."""
from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowEvent
details = WorkflowErrorDetails(
error_type="ValidationError",
@@ -637,7 +635,7 @@ async def test_workflow_failed_event_with_extra(mapper: MessageMapper, test_requ
executor_id="validation_executor",
extra={"field": "email", "reason": "invalid format"},
)
event = WorkflowFailedEvent(details=details)
event = WorkflowEvent.failed(details=details)
events = await mapper.convert_event(event, test_request)
assert len(events) == 1
@@ -650,8 +648,8 @@ async def test_workflow_failed_event_with_extra(mapper: MessageMapper, test_requ
async def test_workflow_failed_event_with_traceback(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowFailedEvent includes traceback when available."""
from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowFailedEvent
"""Test failed event (type='failed') includes traceback when available."""
from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowEvent
details = WorkflowErrorDetails(
error_type="ValueError",
@@ -659,7 +657,7 @@ async def test_workflow_failed_event_with_traceback(mapper: MessageMapper, test_
traceback="Traceback (most recent call last):\n File ...\nValueError: Invalid input",
executor_id="validation_executor",
)
event = WorkflowFailedEvent(details=details)
event = WorkflowEvent.failed(details=details)
events = await mapper.convert_event(event, test_request)
assert len(events) == 1
@@ -672,41 +670,41 @@ async def test_workflow_failed_event_with_traceback(mapper: MessageMapper, test_
async def test_workflow_warning_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowWarningEvent is converted to trace event."""
from agent_framework._workflows._events import WorkflowWarningEvent
"""Test WorkflowEvent(type='warning') is converted to trace event."""
from agent_framework._workflows._events import WorkflowEvent
event = WorkflowWarningEvent(data="This is a warning message")
event = WorkflowEvent.warning("This is a warning message")
events = await mapper.convert_event(event, test_request)
# WorkflowWarningEvent should emit a trace event
# WorkflowEvent(type='warning') should emit a trace event
assert len(events) == 1
assert events[0].type == "response.trace.completed"
assert events[0].data["event_type"] == "WorkflowWarningEvent"
assert events[0].data["event_type"] == "warning"
async def test_workflow_error_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test WorkflowErrorEvent is converted to trace event."""
from agent_framework._workflows._events import WorkflowErrorEvent
"""Test WorkflowEvent(type='error') is converted to trace event."""
from agent_framework._workflows._events import WorkflowEvent
event = WorkflowErrorEvent(data=ValueError("Something went wrong"))
event = WorkflowEvent.error(ValueError("Something went wrong"))
events = await mapper.convert_event(event, test_request)
# WorkflowErrorEvent should emit a trace event
# WorkflowEvent(type='error') should emit a trace event
assert len(events) == 1
assert events[0].type == "response.trace.completed"
assert events[0].data["event_type"] == "WorkflowErrorEvent"
assert events[0].data["event_type"] == "error"
# =============================================================================
# RequestInfoEvent Tests (Human-in-the-Loop)
# request_info event (type='request_info') Tests (Human-in-the-Loop)
# =============================================================================
async def test_request_info_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test RequestInfoEvent is converted to HIL request event."""
from agent_framework._workflows._events import RequestInfoEvent
"""Test request_info event (type='request_info') is converted to HIL request event."""
from agent_framework._workflows._events import WorkflowEvent
event = RequestInfoEvent(
event = WorkflowEvent.request_info(
request_id="req_123",
source_executor_id="approval_executor",
request_data={"action": "approve", "details": "Please approve this action"},
@@ -714,7 +712,7 @@ async def test_request_info_event(mapper: MessageMapper, test_request: AgentFram
)
events = await mapper.convert_event(event, test_request)
# RequestInfoEvent should emit response.request_info.requested
# request_info event (type='request_info') should emit response.request_info.requested
assert len(events) >= 1
# Check that request info is captured
has_hil_event = any(getattr(e, "type", "") == "response.request_info.requested" for e in events)
@@ -732,24 +730,24 @@ async def test_request_info_event(mapper: MessageMapper, test_request: AgentFram
async def test_superstep_started_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test SuperStepStartedEvent is handled gracefully."""
from agent_framework._workflows._events import SuperStepStartedEvent
"""Test superstep_started event (type='superstep_started') is handled gracefully."""
from agent_framework._workflows._events import WorkflowEvent
event = SuperStepStartedEvent(iteration=1)
event = WorkflowEvent.superstep_started(iteration=1)
events = await mapper.convert_event(event, test_request)
# SuperStepStartedEvent may not emit events (internal workflow signal)
# superstep_started event (type='superstep_started') may not emit events (internal workflow signal)
# Just ensure it doesn't crash
assert isinstance(events, list)
async def test_superstep_completed_event(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
"""Test SuperStepCompletedEvent is handled gracefully."""
from agent_framework._workflows._events import SuperStepCompletedEvent
"""Test superstep_completed event (type='superstep_completed') is handled gracefully."""
from agent_framework._workflows._events import WorkflowEvent
event = SuperStepCompletedEvent(iteration=1)
event = WorkflowEvent.superstep_completed(iteration=1)
events = await mapper.convert_event(event, test_request)
# SuperStepCompletedEvent may not emit events (internal workflow signal)
# superstep_completed event (type='superstep_completed') may not emit events (internal workflow signal)
# Just ensure it doesn't crash
assert isinstance(events, list)