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

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 non-streaming `chat.completions.create()` call produces its own trace.

| 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`                    | 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`                                                          |
| `error`                        | Exception message if the API call raises                                                                       |

<Note>
  Calls made with `stream=True` are passed through untraced. Safely wrapping a
  chunk iterator without disrupting the caller's own consumption of it needs
  different handling than a single request/response call, so streaming isn't
  covered by this integration yet.
</Note>

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 on the current thread, the call is attached to it as an `"LLM Call N"` step; otherwise it sends its own trace as usual.

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