telemetry-dev-litellm
LiteLLM integration for the telemetry.dev Python SDK. It instruments litellm.completion, litellm.acompletion, litellm.embedding, litellm.aembedding, streaming CustomStreamWrapper responses, and litellm.Router calls by emitting spans through the public telemetry_dev.start_span API.
Install
pip install telemetry-dev telemetry-dev-litellm
# or
uv add telemetry-dev telemetry-dev-litellm
Requires Python >=3.10,<3.14.
Initialize telemetry.dev
import telemetry_dev
from telemetry_dev_litellm import instrument_litellm
telemetry_dev.init() # reads TELEMETRY_DEV_API_KEY and OTLP settings
instrument_litellm()
The core SDK fails open: without an API key, LiteLLM calls still run and telemetry is a no-op.
Per-call drop-ins
Use the exported functions as drop-in replacements when you do not want global monkey-patching:
from telemetry_dev_litellm import completion
response = completion(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Say hi"}],
)
Available drop-ins: completion, acompletion, embedding, aembedding. Each resolves the current litellm.<function> at call time. If global instrumentation is already active, it delegates to the globally wrapped function so the call produces exactly one span.
Global instrumentation
import litellm
from telemetry_dev_litellm import instrument_litellm, uninstrument_litellm
instrument_litellm()
response = litellm.completion(model="gpt-4o-mini", messages=[...])
uninstrument_litellm()
instrument_litellm() patches exactly four package attributes:
litellm.completionlitellm.acompletionlitellm.embeddinglitellm.aembedding
It is thread-safe and idempotent. uninstrument_litellm() restores the original objects and is also idempotent.
Import-order limitation: references captured before instrumentation, such as from litellm import completion, keep pointing at the original function and are not traced. Instrument at process startup or use the telemetry_dev_litellm drop-ins.
Router wrapping
Global instrumentation traces Router deployment attempts because LiteLLM Router dispatches through the patched package attributes at call time:
import litellm
from telemetry_dev_litellm import instrument_litellm
instrument_litellm()
router = litellm.Router(model_list=[...])
router.completion(model="my-group", messages=[...]) # one span per deployment attempt
Use wrap_router(router) for an enclosing Router-level span:
import litellm
from telemetry_dev_litellm import instrument_litellm, wrap_router
instrument_litellm()
router = wrap_router(litellm.Router(model_list=[...]))
router.completion(model="my-group", messages=[...])
With both wrap_router() and instrument_litellm() active, the Router span is the parent and each deployment attempt is a child span. Router-injected LiteLLM metadata such as model_group and deployment is forwarded as td.metadata.* on inner attempt spans when LiteLLM provides it.
Internal LiteLLM num_retries retries happen inside a single wrapped call, so they are represented as one span whose duration covers all attempts. Router retries and fallbacks are separate deployment calls and can produce separate child spans under global instrumentation.
Captured fields
Chat spans use type="generation" and emit gen_ai.operation.name="chat":
- request model, provider, messages, sampling parameters (including
top_k), stop sequences, seed, frequency and presence penalties response_formatasgen_ai.output.type(jsonforjson_object/json_schema/pydantic models)- response model, response id, output messages, finish reasons, usage, cost when available
metadata={...}passed to LiteLLM astd.metadata.*- errors via the core SDK's
error.typeand exception event mapping
Embedding spans use type="embedding" and emit gen_ai.operation.name="embeddings":
- request model, provider, input, metadata
- response model, usage, cost when available
- no embedding vectors as output
Usage keys follow the core SDK contract: input_tokens, output_tokens, total_tokens, cache_read_input_tokens, cache_creation_input_tokens, and reasoning_output_tokens.
Streaming
stream=True chat calls return a proxy around LiteLLM's CustomStreamWrapper that supports both sync and async iteration, context managers, close(), aclose(), and attribute delegation to the wrapped stream.
The integration never mutates request arguments. It does not inject stream_options.include_usage. On stream completion, early close, or mid-stream error, it rebuilds the best available response with litellm.stream_chunk_builder(...) and ends the span once. time_to_first_chunk_ms is recorded on the first chunk. Stream usage is best-effort: LiteLLM aggregates usage chunks when present, otherwise it estimates usage from chunks and request messages.
Provider attribution
LiteLLM provider names are mapped to OpenTelemetry well-known gen_ai.provider.name values where one exists:
| LiteLLM provider | Emitted provider |
|---|---|
openai |
openai |
azure, azure_text |
azure.ai.openai |
azure_ai |
azure.ai.inference |
anthropic, anthropic_text |
anthropic |
bedrock |
aws.bedrock |
vertex_ai, vertex_ai_beta |
gcp.vertex_ai |
gemini |
gcp.gemini |
mistral |
mistral_ai |
groq |
groq |
deepseek |
deepseek |
xai |
x_ai |
cohere, cohere_chat |
cohere |
perplexity |
perplexity |
watsonx, watsonx_text |
ibm.watsonx.ai |
Unlisted providers pass through unchanged, for example ollama, openrouter, together_ai, and fireworks_ai.
Cost
LiteLLM's synchronous response metadata is used when available: _hidden_params.response_cost maps to gen_ai.usage.cost. If that field is absent, the integration tries litellm.completion_cost(completion_response=...) and silently omits cost if LiteLLM cannot price the model.
Limitations
- Instrumented surfaces are limited to
completion,acompletion,embedding,aembedding, and the same four methods onRouterviawrap_router.text_completion,litellm.responses, image/audio/rerank/batch APIs, and provider-specific APIs are out of scope for this package version. - References imported from LiteLLM before
instrument_litellm()are not patched. - Internal
num_retriesretries are not separate spans; Router deployment attempts are. - OTel's built-in-tools scenario is not portable across LiteLLM providers and is not covered by the examples.
- Multimodal output is not covered; LiteLLM chat returns text/tool calls, while image and audio generation use APIs outside this integration.
Development
uv sync
uv run pytest
uv run ruff format .
uv run ruff check .
uv run pyright
Repository-level commands run this package together with the core Python SDK:
pnpm run py:sync
pnpm run py:check
pnpm run py:test
Metadata
Release files for telemetry-dev-litellm 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| telemetry_dev_litellm-0.1.1.tar.gz | 10.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| telemetry_dev_litellm-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.6 kB
Release files / telemetry_dev_litellm-0.1.1.tar.gz
| Download URL | telemetry_dev_litellm-0.1.1.tar.gz |
|---|---|
| Size | 10.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aba1e5dba2950d2b3771c3c65560f83219c142b6b80a5e5e4c703f7d795d9aee
|
|
BLAKE2b-256 checksum How to use checksums |
bfb494ec414b98266914a9596e7ccf4e02f57679341c6f6417b60631d109e7f8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / telemetry_dev_litellm-0.1.1-py3-none-any.whl
| Download URL | telemetry_dev_litellm-0.1.1-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
14848f76115e7e291e635955a14da688e98cbf4c49fff5bc0298f03578c34f9d
|
|
BLAKE2b-256 checksum How to use checksums |
901960833249efadc6edfefcb2521c910a499c3dc4905711c3d65e20b86cd888
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|