fix(mosaico): pin pydantic-settings so Langfuse tracing actually loads#2560
Conversation
Langfuse observability is dead in the shipped mosaico_agent image. The MOSAICO
agent card advertises the mosaico-observability extension with required:true,
and not a single LLM call is traced.
litellm 1.93.0 imports pydantic-settings unconditionally from
litellm/integrations/otel/__init__.py, but declares it only under its optional
'proxy' extra. requirements.txt never pinned it, so it is absent from the image.
litellm's callback factory swallows the resulting ImportError and returns None,
which is why nothing surfaced: the failure is silent by construction.
pr_agent/mosaico/env_bridge.py registers 'langfuse_otel' precisely to route
around the legacy 'langfuse' callback raising a sdk_integration TypeError under
langfuse 3.x. With pydantic-settings missing, langfuse_otel could not load
either, so the workaround was defeated and both paths were dead.
In the shipped 0.40.0-mosaico_agent image:
_init_custom_logger_compatible_class('langfuse_otel', ...) -> None
_init_custom_logger_compatible_class('langfuse', ...) -> None
After the pin, langfuse_otel returns a LangfuseOtelLogger. The legacy callback
still returns None, which is the unchanged 3.x incompatibility env_bridge exists
to avoid, and is expected.
Traces were confirmed to flow, not merely to initialise: against a loopback
receiver the rebuilt image emitted POST /api/public/otel/v1/traces carrying a
2335-byte protobuf with service.name=litellm and a litellm_request span. The
"Proxy Server is not installed" warning that remains is benign - it comes from
_init_otel_logger_on_litellm_proxy(), which runs after _init_tracing() and only
registers the logger into the proxy server's own callback list.
The regression test asserts the environment rather than pr-agent logic, so it
lives in its own file. It is meaningful in CI because build-and-test.yaml runs
the suite inside the Docker test target, whose dependencies come from
requirements.txt via pyproject - the same base layer the mosaico_agent image is
built from. Verified red against the pre-fix image and green after.
PR Summary by QodoFix MOSAICO Langfuse OTEL tracing by pinning pydantic-settings
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
Reaching into litellm privates is unavoidable here: there is no public API that reports a callback litellm declined to build, and the swallowed ImportError is exactly what this test exists to surface. But two details were brittle without adding signal, so an upstream refactor could fail CI over nothing. The logger-registry fixture now tolerates the attribute being absent and skips its cleanup instead of erroring. The type assertion matches on "langfuse" rather than the exact class name, so a rename survives; `is not None` above it remains the assertion that catches the real defect. Still verified red against the pre-fix image and green after.
|
Code review by qodo was updated up to the latest commit 8a09ca0 |
The module docstring justified required=True by pointing at docstring-agent, which no longer sets it. Following that pointer now argues for the opposite of what the code does, and every live peer reinforces the wrong conclusion: docstring-agent advertises required=False, ip-solution-agent and mini-swe-agent omit the flag, and even the reference agent's own card omits it. The demonstrator's docs/agent-requirements.md states "Required: true" for this extension, so pr-agent is correct and the peers are out of spec. Cite that instead, and say plainly not to relax the flag to match them - the comment as written invited exactly that change. No behaviour change.
|
Code review by qodo was updated up to the latest commit e095b97 |
Verification reproduced, Qodo finding evaluatedIndependently reproduced both directions on top of Red (pre-fix image). Mounting the test into Green. 3 passed. Qodo finding 1, "Brittle LiteLLM internal test": partially accepted, partially rejectedAccepted, and already applied in 8a09ca0:
Rejected: replacing the private construction call Qodo suggests routing through "a more stable/public surface". litellm does expose an apparently public
A test built on it would report the same result in the fixed environment as in the broken one, so it cannot distinguish them. The private factory is the only surface that returns a logger when tracing works and |
The bundle pinned 0.40.0, an image built before every MOSAICO fix merged since: pydantic-settings (#2560), the forwarded-conversation router fix (#2561), and failure fidelity (#2563). Version tags are immutable, so the pin has to move for the consortium to get them. Verified against the published image before opening: digest sha256:6cf24cd58c0c78bfcaa458e576eb9fb4a1fecb79cadfbfe92d9307486b89015f, dispatch.py carries _split_turns and propagate_tool_errors, pydantic_settings imports at 2.14.2, the langfuse_otel callback constructs as LangfuseOtelLogger where 0.40.0 returned None, and get_version() reports 0.41.0. The README upgrade example moves to 0.41.0 -> 0.42.0; left alone it would tell a reader to upgrade to the version they are already running. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Langfuse observability is dead in the shipped
mosaico_agentimage. The MOSAICO agent card advertises themosaico-observabilityextension withrequired: true, and not a single LLM call is traced.Why it was silent
litellm 1.93.0 imports
pydantic-settingsunconditionally fromlitellm/integrations/otel/__init__.py, but declares it only under its optionalproxyextra.requirements.txtnever pinned it, so it is absent from the image — and litellm's callback factory swallows the resultingImportErrorand returnsNone. The failure is silent by construction, which is why a fully green test suite sat alongside a dead deliverable.pr_agent/mosaico/env_bridge.pyregisterslangfuse_otelprecisely to route around the legacylangfusecallback raising asdk_integrationTypeError under langfuse 3.x. Withpydantic-settingsmissing,langfuse_otelcould not load either — so the workaround was defeated and both paths were dead.After the pin,
langfuse_otelreturns aLangfuseOtelLogger. The legacy callback still returnsNone; that is the unchanged 3.x incompatibilityenv_bridgeexists to avoid, and is expected.Traces confirmed flowing, not just initialising
Against a loopback receiver, the rebuilt image emitted:
The
Proxy Server is not installedwarning that remains is benign — it comes from_init_otel_logger_on_litellm_proxy(), which runs after_init_tracing()and only registers the logger into the proxy server's own callback list.Not verified: acceptance by a live Langfuse instance. The receiver was a local stub.
The regression test
It asserts the environment rather than pr-agent logic, so it lives in its own file. It is meaningful in CI because
build-and-test.yamlruns the suite inside the Dockertesttarget, whose dependencies come fromrequirements.txtvia pyproject — the same base layer the shippedmosaico_agentimage is built from.Verified both directions:
0.40.0-mosaico_agentwithModuleNotFoundError: No module named 'pydantic_settings'testimageSuite goes 1485 → 1488 passed.