Skip to content

fix: handle nested event loop in tool dispatch (fixes #11)#28

Merged
wxai-space merged 1 commit intowanxingai:mainfrom
awanawana:fix/asyncio-nested-event-loop
Mar 31, 2026
Merged

fix: handle nested event loop in tool dispatch (fixes #11)#28
wxai-space merged 1 commit intowanxingai:mainfrom
awanawana:fix/asyncio-nested-event-loop

Conversation

@awanawana
Copy link
Copy Markdown

Summary

  • Fixes RuntimeError when using LightAgent inside FastAPI, Jupyter, or any async framework
  • Adds run_async_safely() helper that detects event loop context and adapts execution strategy
  • Zero breaking changes, backward compatible

Problem

When LightAgent is used inside an existing event loop (e.g., FastAPI), the current code calls asyncio.run() which raises:

RuntimeError: asyncio.run() cannot be called from a running event loop

This happens in two places:

  • _run_non_stream_logic (line 868)
  • _run_stream_logic (line 1054)

Solution

Introduce run_async_safely() that:

  1. Checks if we're already in an event loop via asyncio.get_running_loop()
  2. If not in a loop: use asyncio.run() as before (zero overhead)
  3. If in a loop: run the coroutine in a ThreadPoolExecutor with its own event loop

This approach is:

  • Framework agnostic: Works with FastAPI, Starlette, Quart, aiohttp, etc.
  • Jupyter compatible: Works in notebooks where an event loop is always running
  • Minimal overhead: Only creates a thread when actually needed
  • Type safe: Preserves return type via TypeVar

Test Plan

  • Verified original bug is reproduced with asyncio.run() inside event loop
  • Verified fix works without existing event loop
  • Verified fix works with existing event loop (FastAPI scenario)

Related

Fixes #11

🤖 Generated with Claude Code

Fixes wanxingai#11

When LightAgent is used inside an existing event loop (e.g., FastAPI,
Jupyter notebook, or any async framework), calling `asyncio.run()` raises
`RuntimeError: asyncio.run() cannot be called from a running event loop`.

This commit introduces `run_async_safely()`, a helper function that:
1. Detects if we're already inside an event loop
2. If not, uses `asyncio.run()` as before (no overhead)
3. If yes, runs the coroutine in a separate thread with its own event loop

This approach:
- Is backward compatible (no API changes)
- Works with all async frameworks (FastAPI, Starlette, Quart, etc.)
- Works in Jupyter notebooks
- Has minimal overhead when no event loop is running

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@wxai-space wxai-space merged commit 335882e into wanxingai:main Mar 31, 2026
@wxai-space
Copy link
Copy Markdown
Collaborator

Thank you for your suggestion. The PR you submitted has been merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

在event loop中执行会报错:RuntimeError: asyncio.run() cannot be called from a running event loop

2 participants