> ## 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.

# OpenAI

> Auto-trace raw OpenAI SDK calls with patch_openai_client

For agents built on the higher-level OpenAI Agents SDK instead of the plain client, see [OpenAI Agents SDK](/sdk/integrations/openai-agents). For an OpenAI-compatible endpoint served by NVIDIA NIM, use [`patch_nim_client`](/sdk/integrations/nvidia-nim) instead - same machinery, but traces are attributed to `nvidia-nim` rather than `openai`.

Install the integration extra:

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

## Usage

Call `patch_openai_client()` once after creating your OpenAI client. All subsequent `client.chat.completions.create()` calls are traced automatically. No changes to individual API calls are needed.

Works with both `openai.OpenAI` and `openai.AsyncOpenAI`.

```python theme={null}
from agentx import AgentX
from agentx.integrations.openai import patch_openai_client
import openai

agentx = AgentX.from_env()
client = openai.OpenAI()

patch_openai_client(
    client,
    tracer=agentx.tracer,
    name="support-agent",
    metadata={"env": "production"},
    session_id="session-xyz-789",
)

response = client.chat.completions.create(
    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 client:

```python theme={null}
patch_openai_client(async_client, tracer=agentx.tracer, name="support-agent")

response = await async_client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "How do I cancel my subscription?"}],
)
```

## What gets traced

By default, each `chat.completions.create()` call produces its own trace.

| Field | Source |
| - | - |
| `input` | `messages` kwarg |
| `output` | The choices' message content (or a tool-call description if the reply is a pure tool call) |
| `latencyMs` | Wall-clock time of the API call - measured after the real response comes back, for both sync and async clients |
| `model` | `model` kwarg |
| `inputTokens` / `outputTokens` | `response.usage.prompt_tokens` / `.completion_tokens` |
| `cacheReadTokens` | `response.usage.prompt_tokens_details.cached_tokens` - the cached subset of `inputTokens`, priced at the cache rate. OpenAI has no cache-write concept to report |
| `framework` | Stamped `openai` automatically - the [platform label](/trace/platform-detection) on the framework filter and Monitor's Platforms chart |
| `metadata.tools` | The request's `tools=[...]` definitions, captured for the unregistered-tool listing (Tools & MCPs) |
| `error` | Exception message if the API call raises |

## Streaming

Calls made with `stream=True` are traced too. The patch returns a transparent proxy over the provider's stream: iterating it, using it as a context manager, calling `close()`, and reading its attributes all pass straight through to the real stream, and the trace is assembled from the chunks your code actually consumes.

```python theme={null}
with client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Summarize this ticket."}],
    stream=True,
    stream_options={"include_usage": True},  # asks OpenAI to send token usage on the last chunk
) as stream:
    for chunk in stream:
        if chunk.choices:
            print(chunk.choices[0].delta.content or "", end="")
```

What a streamed trace carries:

| Field | Source |
| - | - |
| `output` | Text deltas concatenated; tool-call deltas merged by index into a `[tool call] name(arguments)` description when the reply is a pure tool call |
| `inputTokens` / `outputTokens` | The `usage` block on the final chunk - only present when you pass `stream_options={"include_usage": True}`; without it the counts stay unset rather than reading as 0 |
| `latencyMs` | Measured to the **last chunk** - the response as your user experienced it, not the moment you closed or dropped the stream |
| `metadata.streaming` / `metadata.timeToFirstTokenMs` | Marks the call as streamed and records the time to the first chunk, on the trace (standalone call) or the `LLM Call N` child span (inside an active span) |

If your code stops reading early (`break`) or an error interrupts the stream, the trace records what was streamed up to that point, and an error is recorded as the trace's `error`. A stream that is dropped without being closed still records what it saw when it is garbage-collected.

The raw OpenAI SDK has no built-in concept of "tool call" or "retrieval"; those only exist as plain Python code around your `chat.completions.create()` calls, so the patch 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 (identical API, different client).

## Multi-call agentic loops

Like the Anthropic integration, wrap a multi-call tool-use loop in `with tracer.trace(...)` to collapse every `chat.completions.create()` call made inside it into **one** trace instead of one trace per call:

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

This works because `patch_openai_client()` checks `tracer.current_span` on every call: if a span is active in the current context (a `ContextVar`, inherited by asyncio tasks), the call is attached to it as an `"LLM Call N"` step; otherwise it sends its own trace as usual.

A patched call made outside any active span opens its own root trace, stamped with [span kind](/trace/span-kinds) `llm` - it is a bare model call.

## `patch_openai_client()` reference

```python theme={null}
patch_openai_client(
    client: openai.OpenAI | openai.AsyncOpenAI,
    tracer: Tracer,
    name: str = "openai-agent",
    metadata: dict | None = None,
    session_id: str | None = None,
) -> None
```

| Parameter | Description |
| - | - |
| `client` | The `openai.OpenAI()` or `openai.AsyncOpenAI()` instance to patch |
| `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 |

Calling `patch_openai_client()` on an already-patched client is a no-op; it is safe to call multiple times.

<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>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.