mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [Breaking] removed pydantic from types and workflows (#917)
* removed pydantic from types * fix test * fix test * fix tests * fix assistants client * Remove Pydantic usage from workflow code. * updated pydantic removal * updated lock and test fixes * fix mypy * updated build system * updated chat client parsing * fix broken test --------- Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
647db9635a
commit
b4ebafa9b1
@@ -159,7 +159,6 @@ def test_concurrent_custom_aggregator_uses_callback_name_for_id() -> None:
|
||||
assert aggregator.id == "summarize"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_concurrent_checkpoint_resume_round_trip() -> None:
|
||||
storage = InMemoryCheckpointStorage()
|
||||
|
||||
|
||||
@@ -44,8 +44,10 @@ class MockMessageSecondary:
|
||||
class MockExecutor(Executor):
|
||||
"""A mock executor for testing purposes."""
|
||||
|
||||
call_count: int = 0
|
||||
last_message: Any = None
|
||||
def __init__(self, *, id: str) -> None:
|
||||
super().__init__(id=id)
|
||||
self.call_count: int = 0
|
||||
self.last_message: MockMessage | None = None
|
||||
|
||||
@handler
|
||||
async def mock_handler(self, message: MockMessage, ctx: WorkflowContext) -> None:
|
||||
@@ -57,8 +59,10 @@ class MockExecutor(Executor):
|
||||
class MockExecutorSecondary(Executor):
|
||||
"""A secondary mock executor for testing purposes."""
|
||||
|
||||
call_count: int = 0
|
||||
last_message: Any = None
|
||||
def __init__(self, *, id: str) -> None:
|
||||
super().__init__(id=id)
|
||||
self.call_count: int = 0
|
||||
self.last_message: MockMessageSecondary | None = None
|
||||
|
||||
@handler
|
||||
async def mock_handler_secondary(self, message: MockMessageSecondary, ctx: WorkflowContext) -> None:
|
||||
@@ -70,8 +74,10 @@ class MockExecutorSecondary(Executor):
|
||||
class MockAggregator(Executor):
|
||||
"""A mock aggregator for testing purposes."""
|
||||
|
||||
call_count: int = 0
|
||||
last_message: Any = None
|
||||
def __init__(self, *, id: str) -> None:
|
||||
super().__init__(id=id)
|
||||
self.call_count: int = 0
|
||||
self.last_message: list[MockMessage] | list[MockMessageSecondary] | None = None
|
||||
|
||||
@handler
|
||||
async def mock_aggregator_handler(self, message: list[MockMessage], ctx: WorkflowContext) -> None:
|
||||
@@ -93,8 +99,10 @@ class MockAggregator(Executor):
|
||||
class MockAggregatorSecondary(Executor):
|
||||
"""A mock aggregator that has a handler for a union type for testing purposes."""
|
||||
|
||||
call_count: int = 0
|
||||
last_message: Any = None
|
||||
def __init__(self, *, id: str) -> None:
|
||||
super().__init__(id=id)
|
||||
self.call_count: int = 0
|
||||
self.last_message: list[MockMessage | MockMessageSecondary] | None = None
|
||||
|
||||
@handler
|
||||
async def mock_aggregator_handler_combine(
|
||||
|
||||
@@ -9,6 +9,8 @@ import pytest
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
BaseAgent,
|
||||
ChatClientProtocol,
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
@@ -31,8 +33,6 @@ from agent_framework import (
|
||||
WorkflowStatusEvent,
|
||||
handler,
|
||||
)
|
||||
from agent_framework._agents import BaseAgent
|
||||
from agent_framework._clients import ChatClientProtocol as AFChatClient
|
||||
from agent_framework._workflow._checkpoint import InMemoryCheckpointStorage
|
||||
from agent_framework._workflow._magentic import (
|
||||
MagenticAgentExecutor,
|
||||
@@ -105,8 +105,8 @@ class FakeManager(MagenticManagerBase):
|
||||
if self.task_ledger is not None:
|
||||
state = dict(state)
|
||||
state["task_ledger"] = {
|
||||
"facts": self.task_ledger.facts.model_dump(mode="json"),
|
||||
"plan": self.task_ledger.plan.model_dump(mode="json"),
|
||||
"facts": self.task_ledger.facts.to_dict(),
|
||||
"plan": self.task_ledger.plan.to_dict(),
|
||||
}
|
||||
return state
|
||||
|
||||
@@ -118,8 +118,8 @@ class FakeManager(MagenticManagerBase):
|
||||
plan_payload = ledger_state.get("plan") # type: ignore[reportUnknownMemberType]
|
||||
if facts_payload is not None and plan_payload is not None:
|
||||
try:
|
||||
facts = ChatMessage.model_validate(facts_payload)
|
||||
plan = ChatMessage.model_validate(plan_payload)
|
||||
facts = ChatMessage.from_dict(facts_payload)
|
||||
plan = ChatMessage.from_dict(plan_payload)
|
||||
self.task_ledger = _SimpleLedger(facts=facts, plan=plan)
|
||||
except Exception: # pragma: no cover - defensive
|
||||
pass
|
||||
@@ -159,11 +159,11 @@ async def test_standard_manager_plan_and_replan_combined_ledger():
|
||||
participant_descriptions={"agentA": "Agent A"},
|
||||
)
|
||||
|
||||
first = await manager.plan(ctx.model_copy(deep=True))
|
||||
first = await manager.plan(ctx.clone())
|
||||
assert first.role == Role.ASSISTANT and "Facts:" in first.text and "Plan:" in first.text
|
||||
assert manager.task_ledger is not None
|
||||
|
||||
replanned = await manager.replan(ctx.model_copy(deep=True))
|
||||
replanned = await manager.replan(ctx.clone())
|
||||
assert "A2" in replanned.text or "Do Z" in replanned.text
|
||||
|
||||
|
||||
@@ -174,12 +174,12 @@ async def test_standard_manager_progress_ledger_and_fallback():
|
||||
participant_descriptions={"agentA": "Agent A"},
|
||||
)
|
||||
|
||||
ledger = await manager.create_progress_ledger(ctx.model_copy(deep=True))
|
||||
ledger = await manager.create_progress_ledger(ctx.clone())
|
||||
assert isinstance(ledger, MagenticProgressLedger)
|
||||
assert ledger.next_speaker.answer == "agentA"
|
||||
|
||||
manager.satisfied_after_signoff = False
|
||||
ledger2 = await manager.create_progress_ledger(ctx.model_copy(deep=True))
|
||||
ledger2 = await manager.create_progress_ledger(ctx.clone())
|
||||
assert ledger2.is_request_satisfied.answer is False
|
||||
|
||||
|
||||
@@ -379,7 +379,7 @@ def test_magentic_agent_executor_snapshot_roundtrip():
|
||||
from agent_framework import StandardMagenticManager # noqa: E402
|
||||
|
||||
|
||||
class _StubChatClient(AFChatClient):
|
||||
class _StubChatClient(ChatClientProtocol):
|
||||
@property
|
||||
def additional_properties(self) -> dict[str, Any]:
|
||||
"""Get additional properties associated with the client."""
|
||||
@@ -412,7 +412,7 @@ async def test_standard_manager_plan_and_replan_via_complete_monkeypatch():
|
||||
task=ChatMessage(role=Role.USER, text="T"),
|
||||
participant_descriptions={"A": "desc"},
|
||||
)
|
||||
combined = await mgr.plan(ctx.model_copy(deep=True))
|
||||
combined = await mgr.plan(ctx.clone())
|
||||
# Assert structural headings and that steps appear in the combined ledger output.
|
||||
assert "We are working to address the following user request:" in combined.text
|
||||
assert "Here is the plan to follow as best as possible:" in combined.text
|
||||
@@ -425,7 +425,7 @@ async def test_standard_manager_plan_and_replan_via_complete_monkeypatch():
|
||||
return ChatMessage(role=Role.ASSISTANT, text="GIVEN OR VERIFIED FACTS\n- updated")
|
||||
|
||||
mgr._complete = fake_complete_replan # type: ignore[attr-defined]
|
||||
combined2 = await mgr.replan(ctx.model_copy(deep=True))
|
||||
combined2 = await mgr.replan(ctx.clone())
|
||||
assert "updated" in combined2.text or "new step" in combined2.text
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ async def test_standard_manager_progress_ledger_success_and_error():
|
||||
return ChatMessage(role=Role.ASSISTANT, text=json_text)
|
||||
|
||||
mgr._complete = fake_complete_ok # type: ignore[attr-defined]
|
||||
ledger = await mgr.create_progress_ledger(ctx.model_copy(deep=True))
|
||||
ledger = await mgr.create_progress_ledger(ctx.clone())
|
||||
assert ledger.next_speaker.answer == "alice"
|
||||
|
||||
# Error path: invalid JSON now raises to avoid emitting planner-oriented instructions to agents
|
||||
@@ -457,7 +457,7 @@ async def test_standard_manager_progress_ledger_success_and_error():
|
||||
|
||||
mgr._complete = fake_complete_bad # type: ignore[attr-defined]
|
||||
with pytest.raises(RuntimeError):
|
||||
await mgr.create_progress_ledger(ctx.model_copy(deep=True))
|
||||
await mgr.create_progress_ledger(ctx.clone())
|
||||
|
||||
|
||||
class InvokeOnceManager(MagenticManagerBase):
|
||||
|
||||
@@ -48,8 +48,8 @@ class TestSerializationWorkflowClasses:
|
||||
"""Test that Executor can be serialized and has correct fields, including type."""
|
||||
executor = SampleExecutor(id="test-executor")
|
||||
|
||||
# Test model_dump
|
||||
data = executor.model_dump(by_alias=True)
|
||||
# Test to_dict
|
||||
data = executor.to_dict()
|
||||
assert data["id"] == "test-executor"
|
||||
|
||||
# Test type field
|
||||
@@ -57,7 +57,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert data["type"] == "SampleExecutor", f"Expected type 'SampleExecutor', got {data['type']}"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = executor.model_dump_json(by_alias=True)
|
||||
json_str = executor.to_json()
|
||||
parsed = json.loads(json_str)
|
||||
assert parsed["id"] == "test-executor"
|
||||
|
||||
@@ -70,14 +70,14 @@ class TestSerializationWorkflowClasses:
|
||||
# Test edge without condition
|
||||
edge = Edge(source_id="source", target_id="target")
|
||||
|
||||
# Test model_dump
|
||||
data = edge.model_dump()
|
||||
# Test to_dict
|
||||
data = edge.to_dict()
|
||||
assert data["source_id"] == "source"
|
||||
assert data["target_id"] == "target"
|
||||
assert "condition_name" not in data or data["condition_name"] is None
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge.model_dump_json()
|
||||
json_str = json.dumps(edge.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert parsed["source_id"] == "source"
|
||||
assert parsed["target_id"] == "target"
|
||||
@@ -91,14 +91,14 @@ class TestSerializationWorkflowClasses:
|
||||
|
||||
edge = Edge(source_id="source", target_id="target", condition=is_positive)
|
||||
|
||||
# Test model_dump
|
||||
data = edge.model_dump()
|
||||
# Test to_dict
|
||||
data = edge.to_dict()
|
||||
assert data["source_id"] == "source"
|
||||
assert data["target_id"] == "target"
|
||||
assert data["condition_name"] == "is_positive"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge.model_dump_json()
|
||||
json_str = json.dumps(edge.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert parsed["source_id"] == "source"
|
||||
assert parsed["target_id"] == "target"
|
||||
@@ -108,14 +108,14 @@ class TestSerializationWorkflowClasses:
|
||||
"""Test that Edge with lambda condition serializes condition_name as '<lambda>'."""
|
||||
edge = Edge(source_id="source", target_id="target", condition=lambda x: x > 0)
|
||||
|
||||
# Test model_dump
|
||||
data = edge.model_dump()
|
||||
# Test to_dict
|
||||
data = edge.to_dict()
|
||||
assert data["source_id"] == "source"
|
||||
assert data["target_id"] == "target"
|
||||
assert data["condition_name"] == "<lambda>"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge.model_dump_json()
|
||||
json_str = json.dumps(edge.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert parsed["source_id"] == "source"
|
||||
assert parsed["target_id"] == "target"
|
||||
@@ -125,8 +125,8 @@ class TestSerializationWorkflowClasses:
|
||||
"""Test that SingleEdgeGroup can be serialized and has correct fields, including edges and type."""
|
||||
edge_group = SingleEdgeGroup(source_id="source", target_id="target")
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump(by_alias=True)
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "id" in data
|
||||
assert data["id"].startswith("SingleEdgeGroup/")
|
||||
|
||||
@@ -144,7 +144,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert edge["target_id"] == "target", f"Expected target_id 'target', got {edge['target_id']}"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert "id" in parsed
|
||||
assert parsed["id"].startswith("SingleEdgeGroup/")
|
||||
@@ -164,8 +164,8 @@ class TestSerializationWorkflowClasses:
|
||||
"""Test that FanOutEdgeGroup can be serialized and has correct fields, including edges and type."""
|
||||
edge_group = FanOutEdgeGroup(source_id="source", target_ids=["target1", "target2"])
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump()
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "id" in data
|
||||
assert data["id"].startswith("FanOutEdgeGroup/")
|
||||
|
||||
@@ -191,7 +191,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert set(targets) == {"target1", "target2"}, f"Expected targets {{'target1', 'target2'}}, got {set(targets)}"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert "id" in parsed
|
||||
assert parsed["id"].startswith("FanOutEdgeGroup/")
|
||||
@@ -227,15 +227,15 @@ class TestSerializationWorkflowClasses:
|
||||
source_id="source", target_ids=["target1", "target2"], selection_func=custom_selector
|
||||
)
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump()
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "selection_func_name" in data, "FanOutEdgeGroup should have 'selection_func_name' field"
|
||||
assert data["selection_func_name"] == "custom_selector", (
|
||||
f"Expected selection_func_name 'custom_selector', got {data['selection_func_name']}"
|
||||
)
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert "selection_func_name" in parsed, "JSON should have 'selection_func_name' field"
|
||||
assert parsed["selection_func_name"] == "custom_selector", "JSON should preserve selection_func_name"
|
||||
@@ -246,15 +246,15 @@ class TestSerializationWorkflowClasses:
|
||||
source_id="source", target_ids=["target1", "target2"], selection_func=lambda data, targets: targets[:1]
|
||||
)
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump()
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "selection_func_name" in data, "FanOutEdgeGroup should have 'selection_func_name' field"
|
||||
assert data["selection_func_name"] == "<lambda>", (
|
||||
f"Expected selection_func_name '<lambda>', got {data['selection_func_name']}"
|
||||
)
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert "selection_func_name" in parsed, "JSON should have 'selection_func_name' field"
|
||||
assert parsed["selection_func_name"] == "<lambda>", "JSON should preserve selection_func_name as '<lambda>'"
|
||||
@@ -263,8 +263,8 @@ class TestSerializationWorkflowClasses:
|
||||
"""Test that FanInEdgeGroup can be serialized and has correct fields, including edges and type."""
|
||||
edge_group = FanInEdgeGroup(source_ids=["source1", "source2"], target_id="target")
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump()
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "id" in data
|
||||
assert data["id"].startswith("FanInEdgeGroup/")
|
||||
|
||||
@@ -284,7 +284,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert all(target == "target" for target in targets), f"All edges should have target 'target', got {targets}"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert "id" in parsed
|
||||
assert parsed["id"].startswith("FanInEdgeGroup/")
|
||||
@@ -311,8 +311,8 @@ class TestSerializationWorkflowClasses:
|
||||
]
|
||||
edge_group = SwitchCaseEdgeGroup(source_id="source", cases=cases)
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump()
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "id" in data
|
||||
assert data["id"].startswith("SwitchCaseEdgeGroup/")
|
||||
|
||||
@@ -364,7 +364,7 @@ class TestSerializationWorkflowClasses:
|
||||
)
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
assert "id" in parsed
|
||||
assert parsed["id"].startswith("SwitchCaseEdgeGroup/")
|
||||
@@ -436,7 +436,7 @@ class TestSerializationWorkflowClasses:
|
||||
)
|
||||
|
||||
# Test serialization of the nested structure
|
||||
data = outer_workflow.model_dump(by_alias=True)
|
||||
data = outer_workflow.to_dict()
|
||||
|
||||
# Verify outer structure
|
||||
assert data["start_executor_id"] == "outer-exec"
|
||||
@@ -475,7 +475,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert "inner-exec" in innermost_workflow_data["executors"]
|
||||
|
||||
# Test JSON serialization preserves the complete nested structure
|
||||
json_str = outer_workflow.model_dump_json(by_alias=True)
|
||||
json_str = outer_workflow.to_json()
|
||||
parsed = json.loads(json_str)
|
||||
|
||||
# Verify the complete structure is preserved in JSON
|
||||
@@ -501,7 +501,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert "inner-exec" in innermost_workflow_json["executors"]
|
||||
|
||||
# Test that WorkflowExecutor also serializes correctly when accessed directly
|
||||
direct_middle_data = middle_workflow_executor.model_dump(by_alias=True)
|
||||
direct_middle_data = middle_workflow_executor.to_dict()
|
||||
assert "workflow" in direct_middle_data
|
||||
assert direct_middle_data["type"] == "WorkflowExecutor"
|
||||
assert "executors" in direct_middle_data["workflow"]
|
||||
@@ -519,8 +519,8 @@ class TestSerializationWorkflowClasses:
|
||||
]
|
||||
edge_group = SwitchCaseEdgeGroup(source_id="source", cases=cases)
|
||||
|
||||
# Test model_dump
|
||||
data = edge_group.model_dump()
|
||||
# Test to_dict
|
||||
data = edge_group.to_dict()
|
||||
assert "cases" in data, "SwitchCaseEdgeGroup should have 'cases' field"
|
||||
|
||||
cases_data = data["cases"]
|
||||
@@ -530,7 +530,7 @@ class TestSerializationWorkflowClasses:
|
||||
)
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = edge_group.model_dump_json()
|
||||
json_str = json.dumps(edge_group.to_dict())
|
||||
parsed = json.loads(json_str)
|
||||
json_cases = parsed["cases"]
|
||||
json_case_obj = json_cases[0]
|
||||
@@ -544,7 +544,7 @@ class TestSerializationWorkflowClasses:
|
||||
workflow = WorkflowBuilder().add_edge(executor1, executor2).set_start_executor(executor1).build()
|
||||
|
||||
# Test model_dump
|
||||
data = workflow.model_dump()
|
||||
data = workflow.to_dict()
|
||||
assert "edge_groups" in data
|
||||
assert "executors" in data
|
||||
assert "start_executor_id" in data
|
||||
@@ -569,7 +569,7 @@ class TestSerializationWorkflowClasses:
|
||||
assert edge["target_id"] == "executor2", f"Expected target_id 'executor2', got {edge['target_id']}"
|
||||
|
||||
# Test model_dump_json
|
||||
json_str = workflow.model_dump_json()
|
||||
json_str = workflow.to_json()
|
||||
parsed = json.loads(json_str)
|
||||
assert parsed["start_executor_id"] == "executor1"
|
||||
assert "executor1" in parsed["executors"]
|
||||
@@ -592,7 +592,7 @@ class TestSerializationWorkflowClasses:
|
||||
workflow = WorkflowBuilder().add_edge(executor1, executor2).set_start_executor(executor1).build()
|
||||
|
||||
# Test model_dump - should not include private runtime objects
|
||||
data = workflow.model_dump()
|
||||
data = workflow.to_dict()
|
||||
|
||||
# These private runtime fields should not be in the serialized data
|
||||
assert "_runner_context" not in data
|
||||
@@ -616,13 +616,11 @@ class TestSerializationWorkflowClasses:
|
||||
assert edge.target_id == "target"
|
||||
|
||||
# Test validation failure for empty source_id
|
||||
from pydantic import ValidationError
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
with pytest.raises(ValueError):
|
||||
Edge(source_id="", target_id="target")
|
||||
|
||||
# Test validation failure for empty target_id
|
||||
with pytest.raises(ValidationError):
|
||||
with pytest.raises(ValueError):
|
||||
Edge(source_id="source", target_id="")
|
||||
|
||||
|
||||
@@ -660,7 +658,7 @@ def test_comprehensive_edge_groups_workflow_serialization() -> None:
|
||||
)
|
||||
|
||||
# Test workflow serialization
|
||||
data = workflow.model_dump()
|
||||
data = workflow.to_dict()
|
||||
|
||||
# Verify basic workflow structure
|
||||
assert "edge_groups" in data
|
||||
@@ -683,7 +681,7 @@ def test_comprehensive_edge_groups_workflow_serialization() -> None:
|
||||
assert "SingleEdgeGroup" in edge_group_types, f"Expected SingleEdgeGroup in {edge_group_types}"
|
||||
|
||||
# Test JSON serialization
|
||||
json_str = workflow.model_dump_json()
|
||||
json_str = workflow.to_json()
|
||||
parsed = json.loads(json_str)
|
||||
|
||||
# Verify JSON structure matches model_dump
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from pydantic import Field
|
||||
from typing_extensions import Never
|
||||
|
||||
from agent_framework import (
|
||||
@@ -62,13 +61,10 @@ def create_email_validation_workflow() -> Workflow:
|
||||
class BasicParent(Executor):
|
||||
"""Basic parent executor for simple sub-workflow tests."""
|
||||
|
||||
result: ValidationResult | None = Field(default=None)
|
||||
cache: dict[str, bool] = Field(default_factory=dict)
|
||||
|
||||
def __init__(self, cache: dict[str, bool] | None = None, **kwargs: Any):
|
||||
if cache is not None:
|
||||
kwargs["cache"] = cache
|
||||
super().__init__(id="basic_parent", **kwargs)
|
||||
def __init__(self, cache: dict[str, bool] | None = None) -> None:
|
||||
super().__init__(id="basic_parent")
|
||||
self.result: ValidationResult | None = None
|
||||
self.cache: dict[str, bool] = dict(cache) if cache is not None else {}
|
||||
|
||||
@handler
|
||||
async def start(self, email: str, ctx: WorkflowContext[EmailValidationRequest]) -> None:
|
||||
@@ -140,13 +136,12 @@ class EmailValidator(Executor):
|
||||
class ParentOrchestrator(Executor):
|
||||
"""Parent workflow orchestrator with domain knowledge."""
|
||||
|
||||
approved_domains: set[str] = Field(default_factory=lambda: {"example.com", "test.org"})
|
||||
results: list[ValidationResult] = Field(default_factory=list)
|
||||
|
||||
def __init__(self, approved_domains: set[str] | None = None, **kwargs: Any):
|
||||
if approved_domains is not None:
|
||||
kwargs["approved_domains"] = approved_domains
|
||||
super().__init__(id="parent_orchestrator", **kwargs)
|
||||
def __init__(self, approved_domains: set[str] | None = None) -> None:
|
||||
super().__init__(id="parent_orchestrator")
|
||||
self.approved_domains: set[str] = (
|
||||
set(approved_domains) if approved_domains is not None else {"example.com", "test.org"}
|
||||
)
|
||||
self.results: list[ValidationResult] = []
|
||||
|
||||
@handler
|
||||
async def start(self, emails: list[str], ctx: WorkflowContext[EmailValidationRequest]) -> None:
|
||||
@@ -278,10 +273,9 @@ async def test_workflow_scoped_interception() -> None:
|
||||
class MultiWorkflowParent(Executor):
|
||||
"""Parent handling multiple sub-workflows."""
|
||||
|
||||
results: dict[str, ValidationResult] = Field(default_factory=dict)
|
||||
|
||||
def __init__(self, **kwargs: Any):
|
||||
super().__init__(id="multi_parent", **kwargs)
|
||||
def __init__(self) -> None:
|
||||
super().__init__(id="multi_parent")
|
||||
self.results: dict[str, ValidationResult] = {}
|
||||
|
||||
@handler
|
||||
async def start(self, data: dict[str, str], ctx: WorkflowContext[EmailValidationRequest]) -> None:
|
||||
@@ -362,10 +356,9 @@ async def test_concurrent_sub_workflow_execution() -> None:
|
||||
class ConcurrentProcessor(Executor):
|
||||
"""Processor that sends multiple concurrent requests to the same sub-workflow."""
|
||||
|
||||
results: list[ValidationResult] = Field(default_factory=list)
|
||||
|
||||
def __init__(self, **kwargs: Any):
|
||||
super().__init__(id="concurrent_processor", **kwargs)
|
||||
def __init__(self) -> None:
|
||||
super().__init__(id="concurrent_processor")
|
||||
self.results: list[ValidationResult] = []
|
||||
|
||||
@handler
|
||||
async def start(self, emails: list[str], ctx: WorkflowContext[EmailValidationRequest]) -> None:
|
||||
|
||||
@@ -35,8 +35,10 @@ class NumberMessage:
|
||||
class IncrementExecutor(Executor):
|
||||
"""An executor that increments message data by a specified amount for testing purposes."""
|
||||
|
||||
limit: int = 10
|
||||
increment: int = 1
|
||||
def __init__(self, id: str, *, limit: int = 10, increment: int = 1) -> None:
|
||||
super().__init__(id=id)
|
||||
self.limit = limit
|
||||
self.increment = increment
|
||||
|
||||
@handler
|
||||
async def mock_handler(self, message: NumberMessage, ctx: WorkflowContext[NumberMessage, int]) -> None:
|
||||
|
||||
@@ -29,11 +29,10 @@ from agent_framework import (
|
||||
class SimpleExecutor(Executor):
|
||||
"""Simple executor that emits AgentRunEvent or AgentRunStreamingEvent."""
|
||||
|
||||
response_text: str
|
||||
emit_streaming: bool = False
|
||||
|
||||
def __init__(self, id: str, response_text: str, emit_streaming: bool = False):
|
||||
super().__init__(id=id, response_text=response_text, emit_streaming=emit_streaming)
|
||||
super().__init__(id=id)
|
||||
self.response_text = response_text
|
||||
self.emit_streaming = emit_streaming
|
||||
|
||||
@handler
|
||||
async def handle_message(self, message: list[ChatMessage], ctx: WorkflowContext[list[ChatMessage]]) -> None:
|
||||
|
||||
@@ -273,7 +273,7 @@ async def test_end_to_end_workflow_tracing(span_exporter: InMemorySpanExporter)
|
||||
assert build_span.attributes.get(OtelAttr.WORKFLOW_ID) == workflow.id
|
||||
assert build_span.attributes.get("workflow.definition") is not None
|
||||
definition = build_span.attributes.get("workflow.definition")
|
||||
assert definition == workflow.model_dump_json(by_alias=True)
|
||||
assert definition == workflow.to_json()
|
||||
|
||||
# Check build events
|
||||
assert build_span.events is not None
|
||||
|
||||
Reference in New Issue
Block a user