From 28b4de25cc8ca7bcc157f4f322f93e44643bbd80 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 6 Nov 2026 08:14:50 +0000 Subject: [PATCH] feat(lens): always read Lens traces from Postgres spend logs Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/proxy_server.py | 11 +++++------ litellm/proxy/tracing_runtime.py | 3 +-- litellm/tracing/AGENTS.md | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 86f6a142753..09881dab265 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1961,12 +1961,11 @@ async def _proxy_lifespan(app: FastAPI, resources: AsyncExitStack) -> AsyncGener TypeAdapter(dict[str, object] | None).validate_python(general_settings.get("tracing")), ) tracing_enabled: Final = is_lens_tracing_enabled(tracing_settings) - async with manage_tracing( - enabled=tracing_enabled, - settings=tracing_settings, - fallback_factory=lambda: _spend_log_trace_receiver(database), - ) as receiver: - state: Final[ProxyLifespanState] = {"tracing_receiver": receiver, "database": database} + async with manage_tracing(enabled=tracing_enabled, settings=tracing_settings): + state: Final[ProxyLifespanState] = { + "tracing_receiver": _spend_log_trace_receiver(database), + "database": database, + } from litellm.proxy.admin_mcp import admin_mcp_lifespan telemetry_settings: Final = load_telemetry_settings() diff --git a/litellm/proxy/tracing_runtime.py b/litellm/proxy/tracing_runtime.py index d24a9a0ed11..57f76365d0f 100644 --- a/litellm/proxy/tracing_runtime.py +++ b/litellm/proxy/tracing_runtime.py @@ -40,10 +40,9 @@ async def manage_tracing( receiver_factory: Callable[[], TraceReceiver] | None = None, settings: Mapping[str, object] | None = None, client_factory: Callable[[LensConnection], httpx.AsyncClient] = LensConnection.lifespan_client, - fallback_factory: Callable[[], TraceReceiver | None] = lambda: None, ) -> AsyncGenerator[TraceReceiver | None, None]: if not enabled: - yield fallback_factory() + yield None return try: connection: Final = LensConnection.from_env() diff --git a/litellm/tracing/AGENTS.md b/litellm/tracing/AGENTS.md index 397252be7df..0017a5bb24f 100644 --- a/litellm/tracing/AGENTS.md +++ b/litellm/tracing/AGENTS.md @@ -1,7 +1,7 @@ # Lens gateway boundary - Lens owns trace normalization, schemas, storage, graph assembly and querying in `BerriAI/lens` -- This package owns the HTTP client, gateway response validation and asynchronous gateway spend export. Keep the trace reader remote-only +- This package owns the HTTP client, gateway response validation, asynchronous gateway spend export and the trace reader. Trace reads always come from Postgres spend logs in `spend_logs.py`, even when a Lens service is configured, so traces have one source of truth - Gateway endpoints preserve authenticated user/team scope, trace references, pagination and public error categories - `generated/` comes from `scripts/generate_trace_types.py` using the Lens schemas pinned in `scripts/lens_assets/source.json`. Update the canonical Lens Rust contracts and import their schema outputs before regenerating. Never edit generated Python - Keep the pinned schema and fixture checksums current. CI verifies assets and regenerated Python without a Rust trace implementation in this repository