> ## Documentation Index
> Fetch the complete documentation index at: https://developers.agentx.so/llms.txt
> Use this file to discover all available pages before exploring further.

# LiteLLM

> Auto-trace every LiteLLM completion call with AgentXLiteLLMLogger

Install the integration extra:

```bash theme={null}
pip install "agentx-python[litellm]"
```

## Usage

Register `AgentXLiteLLMLogger` via `litellm.callbacks` once at startup. Every subsequent `litellm.completion()` / `litellm.acompletion()` call - sync, async, or streaming, across any of the 100+ providers LiteLLM supports - is traced automatically with no per-call changes.

```python theme={null}
from agentx import AgentX
from agentx.integrations.litellm import AgentXLiteLLMLogger
import litellm

agentx = AgentX.from_env()

litellm.callbacks = [
    AgentXLiteLLMLogger(
        tracer=agentx.tracer,
        name="support-agent",
        metadata={"env": "production"},
    )
]

response = litellm.completion(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "How do I cancel my subscription?"}],
)
print(response.choices[0].message.content)

agentx.tracer.flush(timeout=10)
```

Async and streaming calls work the same way, no extra setup:

```python theme={null}
response = await litellm.acompletion(
    model="claude-haiku-4-5-20251001",
    messages=[{"role": "user", "content": "How do I cancel my subscription?"}],
)

for chunk in litellm.completion(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "What is your refund policy?"}],
    stream=True,
):
    print(chunk.choices[0].delta.content or "", end="", flush=True)
```

<Note>
  `litellm.callbacks` is process-global - set it once at startup, not per
  request. It affects every LiteLLM call made afterward, regardless of which
  provider or model each call targets.
</Note>

## What gets traced

By default, each `completion()` / `acompletion()` call produces its own trace. For a streamed call, LiteLLM reassembles the full response internally before invoking the logger, so streaming is traced the same way as a regular call - one trace with the complete output, not one per chunk.

| Field                          | Source                                                                                              |
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
| `input`                        | `messages` kwarg                                                                                    |
| `output`                       | `response.choices[0].message.content` (or a tool-call description if the reply is a pure tool call) |
| `latencyMs`                    | `end_time - start_time` from LiteLLM's own logging callback                                         |
| `model`                        | `model` kwarg                                                                                       |
| `inputTokens` / `outputTokens` | `response.usage.prompt_tokens` / `.completion_tokens`                                               |
| `error`                        | The underlying exception LiteLLM captured, from `kwargs["exception"]`                               |

The raw LiteLLM client has no built-in concept of "tool call" or "retrieval"; those only exist as plain Python code around your `completion()` calls, so the logger can't see them on its own. Use `tracer.trace_tool_call()` and `tracer.trace_retrieval()` to record them manually so they show up in the trace's performance summary - see the [Anthropic integration's tool-use example](/sdk/integrations/anthropic#tool-use) for the same pattern.

## Multi-call agentic loops

Wrap a multi-call loop in `with tracer.trace(...)` to collapse every `completion()`/`acompletion()` call made inside it into **one** trace instead of one trace per call:

```python theme={null}
with agentx.tracer.trace("support-agent", framework="litellm") as span:
    span.input = question
    # ... call litellm.completion() as many times as needed ...
    span.output = final_answer
```

This works because `AgentXLiteLLMLogger` checks `tracer.current_span` on every callback: if a span is active on the current thread, the call is attached to it as an `"LLM Call N"` step; otherwise it sends its own trace as usual.

## `AgentXLiteLLMLogger` reference

```python theme={null}
AgentXLiteLLMLogger(
    tracer: Tracer,
    name: str = "litellm-agent",
    metadata: dict | None = None,
    session_id: str | None = None,
)
```

| Parameter    | Description                                       |
| ------------ | ------------------------------------------------- |
| `tracer`     | `agentx.tracer` from your `AgentX` instance       |
| `name`       | Label shown in the AgentX UI for every trace      |
| `metadata`   | Static key-value metadata attached to every trace |
| `session_id` | Links traces from the same conversation thread    |

`AgentXLiteLLMLogger` is a real `litellm.integrations.custom_logger.CustomLogger` - it can be combined with other LiteLLM callbacks in the same `litellm.callbacks` list without conflict.

<Note>
  Call `tracer.flush()` before your process exits in scripts or one-shot jobs.
  In long-running servers it is not required: traces drain automatically in the
  background.
</Note>
