fix: GeneratorExit handling in streaming generators + Langfuse native sessions#300
fix: GeneratorExit handling in streaming generators + Langfuse native sessions#300sheepdestroyer wants to merge 6 commits into
Conversation
- Add _end_parent_obs() and _end_child_span() helpers (v4: update() then end()) - Replace all .end(output=, metadata=) calls with helpers - Add _flush_langfuse_async() for async flush after each request - Add session_id, user_id, environment metadata to traces - Fix parent_obs.end() was never called (traces were incomplete) - Use await for non-streaming flush paths, asyncio.create_task for generators
…ardening - Dynamic kwargs for _end_parent_obs / _end_child_span update() (Gemini: avoid overwriting None) - Fix _end_child_span docstring (CodeRabbit: 'logged' → 'never propagated') - Register all fire-and-forget flush tasks in _background_tasks set (Gemini+CodeRabbit: RUF006) - Flush Langfuse after error-path _end_parent_obs calls (Gemini: missing flush on stream errors) - End litellm_span_obj before parent on LiteLLM stream error (CodeRabbit: missing finalization) - Remove redundant if <obj>: + try/except wrappers around helper calls (CodeRabbit: dead code) - Add trace finalization to non-200 and exception error paths (CodeRabbit: pre-existing gaps) - Add trace finalization to streaming non-200 initial response (CodeRabbit: raised without ending traces)
…dcoded wrong port) - Moved LLAMA_SERVER_URL from hardcoded os.getenv to config.yaml os.environ/LLAMA_SERVER_URL - Added LLAMA_SERVER_URL=https://x570.vendeuvre.lan/llm-routing/llama to .env + .env.dev - Added get_llama_client() with verify=False for self-signed TLS (mirrors get_classifier_client) - Added _check_llama_health() using dedicated llama client - Fixes 'Failed to fetch llama.cpp metrics' warning (default was port 8080, actual is 8083/HAProxy) Closes #298
…ributes for Langfuse sessions GeneratorExit (BaseException, client disconnect) bypasses except Exception in async generators, leaving Langfuse traces open indefinitely. Fix: - native_agy_stream_generator: add finalized flag + finally block - agy_stream_generator (simulated): wrap in try/finally with finalized flag - stream_generator (LiteLLM): extend existing finally to handle cancellation Also fix Langfuse sessions: replace metadata-only session_id/user_id with propagate_attributes() context manager (native session mechanism in v4 SDK). Verified: 193/193 tests pass, 24/24 canonical endpoints pass, Langfuse API confirms sessionId propagation to child observations.
There was a problem hiding this comment.
Sorry @sheepdestroyer, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe router adds environment-driven Llama server connectivity, a dedicated HTTP client and health checks, plus Langfuse v4-compatible trace propagation, finalization, and flushing across AGY and LiteLLM success, error, streaming, and cancellation paths. ChangesLlama routing and Langfuse lifecycle
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces robust Langfuse tracing integration, including session and user ID propagation, helper functions for safely finalizing parent observations and child spans, and asynchronous event flushing. It also refactors the llama-server configuration to resolve URLs dynamically and introduces a dedicated get_llama_client singleton with configurable TLS verification. The feedback highlights a critical context leak in router/main.py where the context manager _prop_ctx is entered manually but never exited on successful execution paths or during certain exceptions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if propagate_attributes and (_trace_session_id or _trace_user_id): | ||
| _prop_ctx = propagate_attributes( | ||
| session_id=_trace_session_id or None, | ||
| user_id=_trace_user_id or None, | ||
| tags=[os.getenv("ENVIRONMENT", "production"), "llm-routing"], | ||
| ) | ||
| _prop_ctx.__enter__() |
There was a problem hiding this comment.
The context manager _prop_ctx (returned by propagate_attributes) is entered manually using _prop_ctx.__enter__() but is never exited on the successful execution path of the chat_completions endpoint, nor is it exited when raising an HTTPException (e.g., for unknown models). This causes a context leak where the session_id, user_id, and tags of one request can persist and affect subsequent requests processed by the same worker thread or asyncio task context.
To fix this cleanly without having to indent the entire 900-line function, you can extract the core logic of chat_completions into a helper function (e.g., _chat_completions_impl) and wrap the call in a with block:
if propagate_attributes and (_trace_session_id or _trace_user_id):
with propagate_attributes(
session_id=_trace_session_id or None,
user_id=_trace_user_id or None,
tags=[os.getenv("ENVIRONMENT", "production"), "llm-routing"],
):
return await _chat_completions_impl(request, body, ...)
else:
return await _chat_completions_impl(request, body, ...)…ssist review) - Add _close_prop_ctx() idempotent helper for safe context cleanup - Add _close_prop_ctx(_prop_ctx) at ALL 14 exit points: 6 non-streaming (unknown model, agy success, litellm failures/returns, proxy exception) 8 streaming generator (3 generators x success/error/cancelled finalization blocks) - Convert init-error handler to use helper for consistency - Fixes context leak where propagate_attributes session/user/tags could persist across requests processed by the same worker thread
|
Closing in favor of a fresh PR with Gemini Code Assist review fix applied (propagate_attributes context leak). See new PR. |
What
Addresses all review comments on PR #297 and fixes additional issues discovered during review.
Changes
GeneratorExit handling (Gemini Code Assist review)
All three streaming generators now handle early client disconnect (GeneratorExit, a BaseException that bypasses except Exception):
finalizedflag +finallyblocktry/finallywithfinalizedflagfinallyto handle non-finalized cancellationLangfuse native sessions (propagate_attributes)
session_id/user_id/tagswith Langfuse's nativepropagate_attributes()context managerpropagate_attributes(session_id=..., user_id=..., tags=...)is called BEFOREstart_observation()so all child observations inherit the session contextclient_modelandenvironment(dimensions, not session attributes)Verification
sessionId=e2e-pr297-sessionanduserId=pr297-testvisible on child observations via Langfuse APICloses #297
Summary by CodeRabbit
Bug Fixes
Improvements