mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-20 23:21:06 +00:00
Fix(subagent): Event loop conflict in SubagentExecutor.execute() (#1965)
* Fix event loop conflict in SubagentExecutor.execute() When SubagentExecutor.execute() is called from within an already-running event loop (e.g., when the parent agent uses async/await), calling asyncio.run() creates a new event loop that conflicts with asyncio primitives (like httpx.AsyncClient) that were created in and bound to the parent loop. This fix detects if we're already in a running event loop, and if so, runs the subagent in a separate thread with its own isolated event loop to avoid conflicts. Fixes: sub-task cards not appearing in Ultra mode when using async parent agents Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(subagent): harden isolated event loop execution --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -433,6 +433,42 @@ class TestSyncExecutionPath:
|
||||
assert result.status == SubagentStatus.COMPLETED
|
||||
assert result.result == "Thread pool result"
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_execute_in_running_event_loop_uses_isolated_thread(self, classes, base_config, mock_agent, msg):
|
||||
"""Test that execute() uses the isolated-thread path inside a running loop."""
|
||||
SubagentExecutor = classes["SubagentExecutor"]
|
||||
SubagentStatus = classes["SubagentStatus"]
|
||||
|
||||
execution_threads = []
|
||||
final_state = {
|
||||
"messages": [
|
||||
msg.human("Task"),
|
||||
msg.ai("Async loop result", "msg-1"),
|
||||
]
|
||||
}
|
||||
|
||||
async def mock_astream(*args, **kwargs):
|
||||
execution_threads.append(threading.current_thread().name)
|
||||
yield final_state
|
||||
|
||||
mock_agent.astream = mock_astream
|
||||
|
||||
executor = SubagentExecutor(
|
||||
config=base_config,
|
||||
tools=[],
|
||||
thread_id="test-thread",
|
||||
)
|
||||
|
||||
with patch.object(executor, "_create_agent", return_value=mock_agent):
|
||||
with patch.object(executor, "_execute_in_isolated_loop", wraps=executor._execute_in_isolated_loop) as isolated:
|
||||
result = executor.execute("Task")
|
||||
|
||||
assert isolated.call_count == 1
|
||||
assert execution_threads
|
||||
assert all(name.startswith("subagent-isolated-") for name in execution_threads)
|
||||
assert result.status == SubagentStatus.COMPLETED
|
||||
assert result.result == "Async loop result"
|
||||
|
||||
def test_execute_handles_asyncio_run_failure(self, classes, base_config):
|
||||
"""Test handling when asyncio.run() itself fails."""
|
||||
SubagentExecutor = classes["SubagentExecutor"]
|
||||
|
||||
Reference in New Issue
Block a user