fix(langsmith): avoid no running event loop during sync init#23727
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes a Key observations:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| litellm/integrations/langsmith.py | Core fix: replaces unconditional asyncio.create_task in __init__ with a guarded _start_periodic_flush_task helper, adds lazy _ensure_periodic_flush_task called on every async log event. Logic is sound; log_success_event (sync path) intentionally does not call _ensure_periodic_flush_task since sync logging relies on batch-size-based flushing via _send_batch(). |
| tests/test_litellm/integrations/test_langsmith_init.py | Stale @patch("asyncio.create_task") decorators correctly removed; five new tests cover no-loop init, loop-present init, and lazy startup for both async log paths. One subtle gap: test_langsmith_init_starts_periodic_flush_with_running_loop asserts mock_loop.create_task.assert_called_once() but does not verify the coroutine passed is periodic_flush; however the test does close the scheduled coroutine to avoid ResourceWarning. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[LangsmithLogger.__init__] --> B[self._flush_task = _start_periodic_flush_task]
B --> C{asyncio.get_running_loop?}
C -- RuntimeError: No Loop --> D[return None\n_flush_task = None]
C -- Loop exists --> E[loop.create_task periodic_flush\n_flush_task = Task]
F[async_log_success_event] --> G[_ensure_periodic_flush_task]
H[async_log_failure_event] --> G
G --> I{_flush_task is None\nor .done?}
I -- Yes --> J[_start_periodic_flush_task]
J --> C
I -- No --> K[Task already running - skip]
E --> L[periodic_flush loop\n while True: sleep → flush_queue]
D -.->|lazy start on first async call| F
D -.->|lazy start on first async call| H
Last reviewed commit: 59caea4
|
Addressed the review feedback in the latest push. What changed:
I also checked branch state against Local validation run:
|
|
Quick note: the LangSmith sync-init fix and regression coverage are updated per review and passing locally. The remaining red checks appear unrelated to this change:
I did not change the proxy startup path touched by the |
e9291a9
into
BerriAI:litellm_oss_staging_03_17_2026
Summary
Fixes #23733.
Fix the LangSmith logger init path for synchronous callers.
Today
LangsmithLogger.__init__()always callsasyncio.create_task(self.periodic_flush()). When LiteLLM is initialized from a synchronous path, there is no running event loop yet, so logger startup raisesRuntimeError: no running event loop.This change starts the periodic flush task only when an event loop is already running. In sync contexts, logger initialization now stays safe and non-blocking instead of raising during setup.
Root cause
asyncio.create_task()requires an active running loop in the current thread.LangsmithLoggerwas calling it unconditionally during object construction.Fix
_start_periodic_flush_task()helperasyncio.get_running_loop()to detect whether task startup is safeTest plan
async_log_success_eventasync_log_failure_eventuv run pytest tests/test_litellm/integrations/test_langsmith_init.py -quv run pytest tests/logging_callback_tests/test_langsmith_unit_test.py -qContext
This is related to earlier reports around LangSmith sync initialization and event-loop handling: