mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-02 15:52:34 +00:00
linting and tests
This commit is contained in:
@@ -73,6 +73,7 @@ from crewai.flow.utils import (
|
|||||||
is_simple_flow_condition,
|
is_simple_flow_condition,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from crewai.flow.async_feedback.types import PendingFeedbackContext
|
from crewai.flow.async_feedback.types import PendingFeedbackContext
|
||||||
from crewai.flow.human_feedback import HumanFeedbackResult
|
from crewai.flow.human_feedback import HumanFeedbackResult
|
||||||
@@ -570,7 +571,7 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
flow_id: str,
|
flow_id: str,
|
||||||
persistence: FlowPersistence | None = None,
|
persistence: FlowPersistence | None = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> "Flow[Any]":
|
) -> Flow[Any]:
|
||||||
"""Create a Flow instance from a pending feedback state.
|
"""Create a Flow instance from a pending feedback state.
|
||||||
|
|
||||||
This classmethod is used to restore a flow that was paused waiting
|
This classmethod is used to restore a flow that was paused waiting
|
||||||
@@ -631,7 +632,7 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pending_feedback(self) -> "PendingFeedbackContext | None":
|
def pending_feedback(self) -> PendingFeedbackContext | None:
|
||||||
"""Get the pending feedback context if this flow is waiting for feedback.
|
"""Get the pending feedback context if this flow is waiting for feedback.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -716,9 +717,10 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError: If no pending feedback context exists
|
ValueError: If no pending feedback context exists
|
||||||
"""
|
"""
|
||||||
from crewai.flow.human_feedback import HumanFeedbackResult
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from crewai.flow.human_feedback import HumanFeedbackResult
|
||||||
|
|
||||||
if self._pending_feedback_context is None:
|
if self._pending_feedback_context is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"No pending feedback context. Use from_pending() to restore a paused flow."
|
"No pending feedback context. Use from_pending() to restore a paused flow."
|
||||||
|
|||||||
@@ -683,3 +683,151 @@ def test_agent_kickoff_with_mcp_tools(mock_get_mcp_tools):
|
|||||||
|
|
||||||
# Verify MCP tools were retrieved
|
# Verify MCP tools were retrieved
|
||||||
mock_get_mcp_tools.assert_called_once_with("https://mcp.exa.ai/mcp?api_key=test_exa_key&profile=research")
|
mock_get_mcp_tools.assert_called_once_with("https://mcp.exa.ai/mcp?api_key=test_exa_key&profile=research")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Tests for LiteAgent inside Flow (magic auto-async pattern)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
from crewai.flow.flow import listen
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_lite_agent_inside_flow_sync():
|
||||||
|
"""Test that LiteAgent.kickoff() works magically inside a Flow.
|
||||||
|
|
||||||
|
This tests the "magic auto-async" pattern where calling agent.kickoff()
|
||||||
|
from within a Flow automatically detects the event loop and returns a
|
||||||
|
coroutine that the Flow framework awaits. Users don't need to use async/await.
|
||||||
|
"""
|
||||||
|
# Track execution
|
||||||
|
execution_log = []
|
||||||
|
|
||||||
|
class TestFlow(Flow):
|
||||||
|
@start()
|
||||||
|
def run_agent(self):
|
||||||
|
execution_log.append("flow_started")
|
||||||
|
agent = Agent(
|
||||||
|
role="Test Agent",
|
||||||
|
goal="Answer questions",
|
||||||
|
backstory="A helpful test assistant",
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
# Magic: just call kickoff() normally - it auto-detects Flow context
|
||||||
|
result = agent.kickoff(messages="What is 2+2? Reply with just the number.")
|
||||||
|
execution_log.append("agent_completed")
|
||||||
|
return result
|
||||||
|
|
||||||
|
flow = TestFlow()
|
||||||
|
result = flow.kickoff()
|
||||||
|
|
||||||
|
# Verify the flow executed successfully
|
||||||
|
assert "flow_started" in execution_log
|
||||||
|
assert "agent_completed" in execution_log
|
||||||
|
assert result is not None
|
||||||
|
assert isinstance(result, LiteAgentOutput)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_lite_agent_inside_flow_with_tools():
|
||||||
|
"""Test that LiteAgent with tools works correctly inside a Flow."""
|
||||||
|
class TestFlow(Flow):
|
||||||
|
@start()
|
||||||
|
def run_agent_with_tools(self):
|
||||||
|
agent = Agent(
|
||||||
|
role="Calculator Agent",
|
||||||
|
goal="Perform calculations",
|
||||||
|
backstory="A math expert",
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
tools=[CalculatorTool()],
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
result = agent.kickoff(messages="Calculate 10 * 5")
|
||||||
|
return result
|
||||||
|
|
||||||
|
flow = TestFlow()
|
||||||
|
result = flow.kickoff()
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
assert isinstance(result, LiteAgentOutput)
|
||||||
|
assert result.raw is not None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_multiple_agents_in_same_flow():
|
||||||
|
"""Test that multiple LiteAgents can run sequentially in the same Flow."""
|
||||||
|
class MultiAgentFlow(Flow):
|
||||||
|
@start()
|
||||||
|
def first_step(self):
|
||||||
|
agent1 = Agent(
|
||||||
|
role="First Agent",
|
||||||
|
goal="Greet users",
|
||||||
|
backstory="A friendly greeter",
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
return agent1.kickoff(messages="Say hello")
|
||||||
|
|
||||||
|
@listen(first_step)
|
||||||
|
def second_step(self, first_result):
|
||||||
|
agent2 = Agent(
|
||||||
|
role="Second Agent",
|
||||||
|
goal="Say goodbye",
|
||||||
|
backstory="A polite farewell agent",
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
return agent2.kickoff(messages="Say goodbye")
|
||||||
|
|
||||||
|
flow = MultiAgentFlow()
|
||||||
|
result = flow.kickoff()
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
assert isinstance(result, LiteAgentOutput)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
async def test_lite_agent_kickoff_async_inside_flow():
|
||||||
|
"""Test that LiteAgent.kickoff_async() works correctly from async Flow methods."""
|
||||||
|
class AsyncAgentFlow(Flow):
|
||||||
|
@start()
|
||||||
|
async def async_agent_step(self):
|
||||||
|
agent = Agent(
|
||||||
|
role="Async Test Agent",
|
||||||
|
goal="Answer questions asynchronously",
|
||||||
|
backstory="An async helper",
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
result = await agent.kickoff_async(messages="What is 3+3?")
|
||||||
|
return result
|
||||||
|
|
||||||
|
flow = AsyncAgentFlow()
|
||||||
|
result = flow.kickoff()
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
assert isinstance(result, LiteAgentOutput)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr()
|
||||||
|
def test_lite_agent_standalone_still_works():
|
||||||
|
"""Test that LiteAgent.kickoff() still works normally outside of a Flow.
|
||||||
|
|
||||||
|
This verifies that the magic auto-async pattern doesn't break standalone usage
|
||||||
|
where there's no event loop running.
|
||||||
|
"""
|
||||||
|
agent = Agent(
|
||||||
|
role="Standalone Agent",
|
||||||
|
goal="Answer questions",
|
||||||
|
backstory="A helpful assistant",
|
||||||
|
llm=LLM(model="gpt-4o-mini"),
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# This should work normally - no Flow, no event loop
|
||||||
|
result = agent.kickoff(messages="What is 5+5? Reply with just the number.")
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
assert isinstance(result, LiteAgentOutput)
|
||||||
|
assert result.raw is not None
|
||||||
|
|||||||
Reference in New Issue
Block a user