litellm/tests/test_litellm/integrations/otel
yucheng-berri 3c2fa5fafb
fix(otel/v2): detach credential-routed tenant spans into their own trace (#38847)
* fix(otel/v2): detach credential-routed tenant spans into their own trace

Multi-tenant OTel v2 routes a team or key's LLM-call span to that tenant's
own vendor account (New Relic, Arize, Langfuse, Weave) via dynamic OTLP
credential headers, while the request-root, auth, and db spans stay on the
operator's default backend. The span was still parented into the request
trace, so the tenant account received a child whose parent it never got,
and New Relic rendered it as a fragmented trace with a missing parent.

Detach a credential-routed span the same way a project-routed (Phoenix)
span already detaches: root a fresh trace in the tenant account and link
back to the request trace for correlation. Service-name routing keeps
parenting, since it only relabels service.name on the same operator
backend where the parent is present.

Guard the detach on the callback actually owning an OTLP exporter the
credentials can reach: a callback owning only a console or in_memory
exporter has nowhere to stamp them, so the span would export to the
default backend unchanged and detaching would orphan it on the very
backend that holds its parent. In that case warn once and keep the
default tracer.

* fix(otel/v2): derive tenant-route routability from resolved exporter transport

A denylist classified an owned exporter as routable whenever its kind was
not console/in_memory, so a typo'd or unavailable kind (e.g. "otlp",
"grcp") passed the check while _exporter_from_spec falls it back to a
header-ignoring console exporter. Detaching such a span would root a fresh
trace that only ever reaches the operator console, never the tenant
backend, orphaning it on both sides.

Route on a shared exporter_transport() predicate that resolves the kind the
same way _exporter_from_spec builds it (registered factories + otlp_http
aliases -> http, otlp_grpc aliases -> grpc, else headerless), so an
unresolvable kind is headerless and stays parented. Fixes the same latent
gap in project routability.
2026-08-29 17:41:25 -07:00
..
test_db_endpoint.py feat(otel): attribute Prisma database spans to PostgreSQL instead of localhost (#36595) 2026-08-18 20:08:43 -07:00
test_otel_v2_baggage.py feat(otel): allowlist team_metadata sub-keys promoted to baggage (#29442) 2026-06-01 14:02:23 -07:00
test_otel_v2_components.py feat(guardrails): track Azure Prompt Shield usage and cost with spend isolation (#38387) 2026-08-26 17:42:17 -07:00
test_otel_v2_config_baggage_parenting_guardrails.py feat(otel): typed semconv-aligned OpenTelemetry instrumentation (#28909) 2026-05-29 23:15:27 -07:00
test_otel_v2_dynamic.py fix(otel/v2): detach credential-routed tenant spans into their own trace (#38847) 2026-08-29 17:41:25 -07:00
test_otel_v2_emitter.py fix(otel): cap tool-definition attributes so they cannot evict gen_ai.* from the LLM span (#34828) 2026-07-30 12:01:10 -07:00
test_otel_v2_logger.py fix(otel): anchor MCP tool-call spans to the gateway's own trace, link the client's context (#38317) 2026-08-27 13:41:30 -07:00
test_otel_v2_metrics.py fix(logging): stop billing and logging response reads as LLM calls (#36890) 2026-08-26 18:34:17 -07:00
test_otel_v2_mount.py test: drop the cwd-relative sys.path.insert calls from the test suite (#37802) 2026-08-22 09:25:58 -07:00
test_otel_v2_multibackend.py feat(otel): typed semconv-aligned OpenTelemetry instrumentation (#28909) 2026-05-29 23:15:27 -07:00
test_otel_v2_presets.py feat(newrelic): per-team New Relic trace routing via team callbacks (#37603) 2026-08-22 19:13:48 -07:00
test_otel_v2_sources_of_truth.py fix(otel): anchor MCP tool-call spans to the gateway's own trace, link the client's context (#38317) 2026-08-27 13:41:30 -07:00
test_otel_v2_vendor_mappers.py feat(otel): typed semconv-aligned OpenTelemetry instrumentation (#28909) 2026-05-29 23:15:27 -07:00
test_runtime.py perf(otel): memoize per-request lazy import of otel runtime hooks (#31707) 2026-06-30 10:26:20 -07:00