diff --git a/lib/crewai/src/crewai/flow/flow.py b/lib/crewai/src/crewai/flow/flow.py index 3ccd4c723..89580e87f 100644 --- a/lib/crewai/src/crewai/flow/flow.py +++ b/lib/crewai/src/crewai/flow/flow.py @@ -3256,7 +3256,7 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): if isinstance(state, dict): return dict(state) if hasattr(state, "model_dump"): - return state.model_dump() + return cast(dict[str, Any], state.model_dump()) return {} def ask( @@ -3338,6 +3338,7 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): from crewai.flow.input_provider import InputResponse method_name = current_flow_method_name.get("unknown") + response: str | None = None # GAP-34: If a pending response was set (from from_ask_pending()), return it if self._pending_response is not None: @@ -3436,7 +3437,7 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): raw = None # Normalize provider response: str, InputResponse, or None - response: str | None = None + response = None response_metadata: dict[str, Any] | None = None if isinstance(raw, InputResponse): @@ -3549,10 +3550,10 @@ class Flow(BaseModel, Generic[T], metaclass=FlowMeta): # on the running loop and block until it completes. import concurrent.futures - future: concurrent.futures.Future[Any] = ( + coro_future: concurrent.futures.Future[Any] = ( asyncio.run_coroutine_threadsafe(_round_trip(), loop) ) - response = future.result() + response = coro_future.result() else: response = asyncio.run(_round_trip()) except KeyboardInterrupt: diff --git a/lib/crewai/src/crewai/new_agent/coworker_tools.py b/lib/crewai/src/crewai/new_agent/coworker_tools.py index 8bf55e5d3..15ada40de 100644 --- a/lib/crewai/src/crewai/new_agent/coworker_tools.py +++ b/lib/crewai/src/crewai/new_agent/coworker_tools.py @@ -349,7 +349,7 @@ class DelegateToCoworkerTool(BaseTool): def _delegate_a2a(self, message: str) -> str: """Delegate to an A2A remote coworker.""" try: - from crewai.a2a.client import A2AClient + from crewai.a2a.client import A2AClient # type: ignore[import-not-found] url = getattr(self.coworker, "url", None) or str(self.coworker) client = A2AClient(url=url) @@ -412,7 +412,8 @@ class MultiDelegateTool(BaseTool): coros.append(_error_result(cw_name)) else: coros.append(coworker.amessage(message)) - return await asyncio.gather(*coros, return_exceptions=True) + raw_results = await asyncio.gather(*coros, return_exceptions=True) + return [r for r in raw_results if not isinstance(r, BaseException)] async def _error_result(name: str) -> str: return f"[Error] Coworker '{name}' not found." diff --git a/lib/crewai/src/crewai/new_agent/executor.py b/lib/crewai/src/crewai/new_agent/executor.py index 61dcde247..a27760e56 100644 --- a/lib/crewai/src/crewai/new_agent/executor.py +++ b/lib/crewai/src/crewai/new_agent/executor.py @@ -737,7 +737,9 @@ class ConversationalAgentExecutor(BaseModel): # Try to emit as an event try: from crewai.events.event_bus import crewai_event_bus - from crewai.utilities.events.checkpoint_events import CheckpointEvent + from crewai.utilities.events.checkpoint_events import ( # type: ignore[import-not-found] + CheckpointEvent, + ) crewai_event_bus.emit(self, CheckpointEvent(data=checkpoint_data)) except (ImportError, Exception): @@ -2242,7 +2244,7 @@ class ConversationalAgentExecutor(BaseModel): except Exception: pass else: - result_text = f"[Subtask {i + 1}] {r.content}" + result_text = f"[Subtask {i + 1}] {r.content if isinstance(r, Message) else str(r)}" try: from crewai.new_agent.events import NewAgentSpawnCompletedEvent diff --git a/lib/crewai/tests/agents/test_lite_agent.py b/lib/crewai/tests/agents/test_lite_agent.py index 37d115228..24708d455 100644 --- a/lib/crewai/tests/agents/test_lite_agent.py +++ b/lib/crewai/tests/agents/test_lite_agent.py @@ -836,18 +836,30 @@ def test_lite_agent_kickoff_async_inside_flow(): 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. """ + from crewai.types.usage_metrics import UsageMetrics + + mock_llm = Mock(spec=LLM) + mock_llm.call.return_value = "10" + mock_llm.stop = [] + mock_llm.get_token_usage_summary.return_value = UsageMetrics( + total_tokens=10, + prompt_tokens=5, + completion_tokens=5, + cached_prompt_tokens=0, + successful_requests=1, + ) + agent = Agent( role="Standalone Agent", goal="Answer questions", backstory="A helpful assistant", - llm=LLM(model="gpt-4o-mini"), + llm=mock_llm, verbose=False, )