Skip to content

Commit 7c287fe

Browse files
MkDev11fengju0213a7m-1stWendong-Fanclaude
authored
refactor: extract event_loop_utils module and improve agent response handling (eigent-ai#999)
Co-authored-by: mkdev11 <MkDev11@users.noreply.github.com> Co-authored-by: Tao Sun <168447269+fengju0213@users.noreply.github.com> Co-authored-by: a7m-1st <Ahmed.jimi.awelkeir500@gmail.com> Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 42ce1d9 commit 7c287fe

15 files changed

Lines changed: 464 additions & 221 deletions

File tree

backend/app/agent/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# limitations under the License.
1313
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
1414

15-
from app.agent.agent_model import agent_model, set_main_event_loop
15+
from app.agent.agent_model import agent_model
1616
from app.agent.factory import (
1717
browser_agent,
1818
developer_agent,
@@ -31,7 +31,6 @@
3131
"agent_model",
3232
"get_mcp_tools",
3333
"get_toolkits",
34-
"set_main_event_loop",
3534
"browser_agent",
3635
"developer_agent",
3736
"document_agent",

backend/app/agent/agent_model.py

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -12,80 +12,20 @@
1212
# limitations under the License.
1313
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
1414

15-
import asyncio
16-
import contextvars
1715
import logging
1816
import uuid
19-
from threading import Lock
2017
from typing import Any, Callable
2118

2219
from camel.messages import BaseMessage
2320
from camel.models import ModelFactory
2421
from camel.toolkits import FunctionTool, RegisteredAgentToolkit
2522
from camel.types import ModelPlatformType
2623

24+
from app.utils.event_loop_utils import _schedule_async_task
2725
from app.agent.listen_chat_agent import ListenChatAgent, logger
2826
from app.model.chat import AgentModelConfig, Chat
2927
from app.service.task import ActionCreateAgentData, Agents, get_task_lock
3028

31-
# Thread-safe reference to main event loop using contextvars
32-
# This ensures each request has its own event loop reference,
33-
# avoiding race conditions
34-
_main_event_loop_var: contextvars.ContextVar[asyncio.AbstractEventLoop
35-
| None] = contextvars.ContextVar(
36-
"_main_event_loop",
37-
default=None
38-
)
39-
40-
# Global fallback for main event loop reference
41-
# Used when contextvars don't propagate to worker threads
42-
# (e.g., asyncio.to_thread)
43-
_GLOBAL_MAIN_LOOP: asyncio.AbstractEventLoop | None = None
44-
_GLOBAL_MAIN_LOOP_LOCK = Lock()
45-
46-
47-
def set_main_event_loop(loop: asyncio.AbstractEventLoop | None):
48-
"""Set the main event loop reference for thread-safe task scheduling.
49-
50-
This should be called from the main async context before spawning threads
51-
that need to schedule async tasks. Uses both contextvars (for request
52-
isolation) and a global fallback (for thread pool workers where
53-
contextvars may not propagate).
54-
"""
55-
global _GLOBAL_MAIN_LOOP
56-
_main_event_loop_var.set(loop)
57-
with _GLOBAL_MAIN_LOOP_LOCK:
58-
_GLOBAL_MAIN_LOOP = loop
59-
60-
61-
def _schedule_async_task(coro):
62-
"""Schedule an async coroutine as a task, thread-safe.
63-
64-
This function handles scheduling from both the main event loop thread
65-
and from worker threads (e.g., when using asyncio.to_thread).
66-
"""
67-
try:
68-
# Try to get the running loop (works in main event loop thread)
69-
loop = asyncio.get_running_loop()
70-
loop.create_task(coro)
71-
except RuntimeError:
72-
# No running loop in this thread (we're in a worker thread)
73-
# First try contextvars, then fallback to global reference
74-
main_loop = _main_event_loop_var.get()
75-
if main_loop is None:
76-
with _GLOBAL_MAIN_LOOP_LOCK:
77-
main_loop = _GLOBAL_MAIN_LOOP
78-
if main_loop is not None and main_loop.is_running():
79-
asyncio.run_coroutine_threadsafe(coro, main_loop)
80-
else:
81-
# This should not happen in normal operation - log error and skip
82-
logging.error(
83-
"No event loop available for async task "
84-
"scheduling, task skipped. Ensure "
85-
"set_main_event_loop() is called "
86-
"before parallel agent creation."
87-
)
88-
8929

9030
def agent_model(
9131
agent_name: str,

0 commit comments

Comments
 (0)