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

# LLM auto-capture

> Wrap synchronous or asynchronous OpenAI and Anthropic clients to emit token metadata.

The wrappers patch the supplied provider client's create method and preserve its normal return value. They support synchronous calls, asynchronous calls, synchronous streams, and asynchronous streams.

Only model, token counts, latency, and an optional feature tag are sent. Prompt and completion content are not captured.

## OpenAI

```bash theme={null}
pip install "nozle-sdk[openai]"
```

```python theme={null}
from openai import OpenAI

from nozle import Nozle, wrap_openai

nozle = Nozle(
    api_key="sk_example",
    base_url="https://api.nozle.app",
    events_url="https://core.nozle.app",
)

openai = wrap_openai(
    OpenAI(),
    nozle,
    customer_id="workspace_123",
    metric_code="llm_tokens",
    feature="assistant_reply",
)

response = openai.chat.completions.create(
    model="example-chat-model",
    messages=[{"role": "user", "content": "Hello"}],
)
```

### OpenAI streaming

Request usage metadata so the final stream chunk contains token counts:

```python theme={null}
stream = openai.chat.completions.create(
    model="example-chat-model",
    messages=[{"role": "user", "content": "Explain vector search"}],
    stream=True,
    stream_options={"include_usage": True},
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

## Anthropic

```bash theme={null}
pip install "nozle-sdk[anthropic]"
```

```python theme={null}
from anthropic import Anthropic

from nozle import Nozle, wrap_anthropic

anthropic = wrap_anthropic(
    Anthropic(),
    nozle,
    customer_id="workspace_123",
    metric_code="llm_tokens",
    feature="assistant_reply",
)

message = anthropic.messages.create(
    model="example-anthropic-model",
    max_tokens=1_024,
    messages=[{"role": "user", "content": "Hello"}],
)
```

Anthropic streams read input usage from `message_start` and output usage from `message_delta` events.

## Wrapper arguments

| Argument      | Required | Default      | Description                          |
| ------------- | -------- | ------------ | ------------------------------------ |
| `client`      | yes      | —            | OpenAI or Anthropic client instance. |
| `nozle`       | yes      | —            | Configured `Nozle` client.           |
| `customer_id` | yes      | —            | Customer passed to `track()`.        |
| `metric_code` | no       | `llm_tokens` | Event code.                          |
| `feature`     | no       | `None`       | Optional event property.             |

## Tracked properties

```json theme={null}
{
  "model": "example-chat-model",
  "input_tokens": 320,
  "output_tokens": 84,
  "latency_ms": 742,
  "feature": "assistant_reply"
}
```

Cost calculation remains server-side through your Feature and cost-model configuration.

## Failure behavior

The wrappers attempt tracking after a successful provider result. Tracking failures do not replace that result; the SDK emits `NozleTrackingWarning`.

For async provider calls, synchronous Nozle tracking runs in a worker thread through `asyncio.to_thread()`.

The wrapper does not expose `subscription_id` or `transaction_id`, so `track()` resolves the customer's single active subscription and creates a transaction UUID. Use manual queued tracking when you require durable delivery or deterministic IDs.

```python theme={null}
response = provider_call()

nozle.track(
    "workspace_123",
    "llm_tokens",
    metadata={
        "model": response.model,
        "input_tokens": response.input_tokens,
        "output_tokens": response.output_tokens,
    },
    subscription_id="workspace_123_subscription",
    transaction_id=f"llm:{response.id}:usage",
)
```
