* build: migrate packaging metadata to uv * ci: move automation and local tooling to uv * docker: migrate image builds and runtime setup to uv * docs: update install and deployment guidance for uv * chore: align auxiliary scripts and tests with uv * test: harden test_litellm isolation * fix: keep release and health check images self-contained * build: pin uv tooling and health check deps * test: isolate bedrock image request formatting from suite state * test: cover sandbox executor requirements flow * ci: fix circleci no-op command steps * ci: fix circleci publish workflow parsing * fix: stabilize remaining uv migration CI checks * ci: increase matrix test timeout headroom * fix: restore published docker and license coverage * fix: restore proxy runtime build parity * fix: restore proxy extras parity and venv migrations * ci: persist uv path across circleci steps * fix: keep psycopg binary in default test env * docker: preserve prisma cache across stages * test: run local proxy checks through uv python * build: restore runtime deps moved into ci * build: refresh uv lock after upstream merge * fix: restore module import in test_check_migration after merge The conflict resolution imported only the function but the test body references check_migration as a module throughout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert dependency promotions, remove nodejs-wheel-binaries, fix Docker layer caching - Move google-generativeai, Pillow, tenacity back to ci group (they are lazily imported and bloat the base SDK install needlessly) - Remove nodejs-wheel-binaries from extra_proxy and proxy-dev (redundant in Docker where system Node.js is already installed via apk) - Remove all nodejs-wheel node replacement and venv npm patching blocks from Dockerfiles since the wheel is no longer installed - Add --no-default-groups to CodSpeed benchmark workflow so the benchmark environment matches the old minimal pip install footprint - Apply standard uv two-phase Docker pattern: copy metadata first, install deps (cached layer), then copy source and install project - Replace CircleCI enterprise no-op with proper uv sync command Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate uv.lock after removing nodejs-wheel-binaries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use cache/restore instead of cache to prevent cache poisoning The old workflow used actions/cache/restore (read-only). The uv migration changed it to actions/cache (read-write), which zizmor flags as a cache poisoning risk. Restore the safer read-only variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): disable setup-uv built-in cache to silence cache-poisoning alert The setup-uv action enables caching by default, which zizmor flags as a cache poisoning risk. Disable it since we already use a read-only cache/restore step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): disable setup-uv cache in publish workflow Silences zizmor cache-poisoning alert. Publishing workflow runs infrequently on protected branches so caching adds no real benefit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): remove duplicate verbose_logger mock in test_check_migration The logger was patched twice — first via mocker.patch() then via mocker.patch.object(autospec=True). The second call fails because autospec cannot inspect an already-mocked attribute. Remove the redundant first patch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): free disk space before Docker build in test-server-root-path The Dockerfile.non_root build ran out of disk on the CI runner. Remove Android SDK, .NET, Boost, and GHC toolchains (~12GB) to free space. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.8 KiB
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
import Image from '@theme/IdealImage';
🪢 Langfuse OpenTelemetry Integration
The Langfuse OpenTelemetry integration allows you to send LiteLLM traces and observability data to Langfuse using the OpenTelemetry protocol. This provides a standardized way to collect and analyze your LLM usage data.
<Image img={require('../../img/langfuse_otel.png')} />
Features
- Automatic trace collection for all LiteLLM requests
- Support for Langfuse Cloud (EU and US regions)
- Support for self-hosted Langfuse instances
- Custom endpoint configuration
- Secure authentication using Basic Auth
- Consistent attribute mapping with other OTEL integrations
Prerequisites
- Langfuse Account: Sign up at Langfuse Cloud or set up a self-hosted instance
- API Keys: Get your public and secret keys from your Langfuse project settings
- Dependencies: Install required packages:
uv add litellm opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
Configuration
Environment Variables
| Variable | Required | Description | Example |
|---|---|---|---|
LANGFUSE_PUBLIC_KEY |
Yes | Your Langfuse public key | pk-lf-... |
LANGFUSE_SECRET_KEY |
Yes | Your Langfuse secret key | sk-lf-... |
LANGFUSE_OTEL_HOST |
No | OTEL endpoint host | https://otel.my-langfuse.com |
Endpoint Resolution
The integration automatically constructs the OTEL endpoint from LANGFUSE_OTEL_HOST
- Default (US):
https://us.cloud.langfuse.com/api/public/otel - EU Region:
https://cloud.langfuse.com/api/public/otel - Self-hosted:
{LANGFUSE_OTEL_HOST}/api/public/otel
Usage
Basic Setup
import os
import litellm
# Set your Langfuse credentials
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
# Enable Langfuse OTEL integration
litellm.callbacks = ["langfuse_otel"]
# Make LLM requests as usual
response = litellm.completion(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello!"}]
)
Advanced Configuration
import os
import litellm
# Set your Langfuse credentials
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
# Use EU region
os.environ["LANGFUSE_OTEL_HOST"] = "https://cloud.langfuse.com" # EU region
# os.environ["LANGFUSE_OTEL_HOST"] = "https://otel.my-langfuse.company.com" # custom OTEL endpoint
# Or use self-hosted instance
# os.environ["LANGFUSE_OTEL_HOST"] = "https://my-langfuse.company.com"
# Optional: Ignore otel context propagation to prevent parent-child relationships with spans from other providers
# os.environ["OTEL_IGNORE_CONTEXT_PROPAGATION"] = "true"
litellm.callbacks = ["langfuse_otel"]
Manual OTEL Configuration
If you need direct control over the OpenTelemetry configuration:
import os
import base64
import litellm
# Get keys for your project from the project settings page: https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_OTEL_HOST"] = "https://cloud.langfuse.com" # EU region
# os.environ["LANGFUSE_OTEL_HOST"] = "https://us.cloud.langfuse.com" # US region
# os.environ["LANGFUSE_OTEL_HOST"] = "https://otel.my-langfuse.company.com" # custom OTEL endpoint
LANGFUSE_AUTH = base64.b64encode(
f"{os.environ.get('LANGFUSE_PUBLIC_KEY')}:{os.environ.get('LANGFUSE_SECRET_KEY')}".encode()
).decode()
host = os.environ.get("LANGFUSE_OTEL_HOST")
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = host + "/api/public/otel"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
litellm.callbacks = ["langfuse_otel"]
With LiteLLM Proxy
Add the integration to your proxy configuration:
- Add the credentials to your environment variables
export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_OTEL_HOST="https://us.cloud.langfuse.com" # Default US region
# export LANGFUSE_OTEL_HOST="https://otel.my-langfuse.company.com" # custom OTEL endpoint
# Optional: Ignore otel context propagation to prevent parent-child relationships with spans from other providers
# export OTEL_IGNORE_CONTEXT_PROPAGATION="true"
- Setup config.yaml
# config.yaml
litellm_settings:
callbacks: ["langfuse_otel"]
- Run the proxy
litellm --config /path/to/config.yaml
Data Collected
The integration automatically collects the following data:
- Request Details: Model, messages, parameters (temperature, max_tokens, etc.)
- Response Details: Generated content, token usage, finish reason
- Timing Information: Request duration, time to first token
- Metadata: User ID, session ID, custom tags (if provided)
- Error Information: Exception details and stack traces (if errors occur)
Metadata Support
All metadata fields available in the vanilla Langfuse integration are now fully supported when you use the OTEL integration.
- Any key you pass in the
metadatadictionary (generation_name,trace_id,session_id,tags, and the rest) is exported as an OpenTelemetry span attribute. - Attribute names are prefixed with
langfuse.so you can filter or search for them easily in your observability backend. Examples:langfuse.generation.name,langfuse.trace.id,langfuse.trace.session_id.
Passing Metadata – Example
response = litellm.completion(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello!"}],
metadata={
"generation_name": "welcome-message",
"trace_id": "trace-123",
"session_id": "sess-42",
"tags": ["prod", "beta-user"]
}
)
The resulting span will contain attributes similar to:
langfuse.generation.name = "welcome-message"
langfuse.trace.id = "trace-123"
langfuse.trace.session_id = "sess-42"
langfuse.trace.tags = ["prod", "beta-user"]
Use the Langfuse UI (Traces tab) to search, filter and analyse spans that contain the langfuse.* attributes.
The OTEL exporter in this integration sends data directly to Langfuse’s OTLP HTTP endpoint; it is not intended for Grafana, Honeycomb, Datadog, or other generic OTEL back-ends.
Authentication
The integration uses HTTP Basic Authentication with your Langfuse public and secret keys:
Authorization: Basic <base64(public_key:secret_key)>
This is automatically handled by the integration - you just need to provide the keys via environment variables.
Troubleshooting
Common Issues
-
Missing Credentials Error
ValueError: LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY must be setSolution: Ensure both environment variables are set with valid keys.
-
Connection Issues
- Check your internet connection
- Verify the endpoint URL is correct
- For self-hosted instances, ensure the
/api/public/otelendpoint is accessible
-
Authentication Errors
- Verify your public and secret keys are correct
- Check that the keys belong to the same Langfuse project
- Ensure the keys have the necessary permissions
Debug Mode
Enable verbose logging to see detailed information:
import litellm
litellm._turn_on_debug()
export LITELLM_LOG="DEBUG"
This will show:
- Endpoint resolution logic
- Authentication header creation
- OTEL trace submission details