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

# Capture from an OpenAI app

> Log OpenAI Chat Completions and Responses calls into a task dataset with one wrapper. Your calls stay direct and capture never breaks your app.

If your app uses the OpenAI Python SDK, wrap its client once to build a dataset from real traffic. The same client still calls OpenAI directly. Apprentice only records the completed call.

The wrapper covers `create` and `parse` on both `chat.completions` and `responses`. `parse` is the structured-output helper, and it posts on its own rather than going through `create`, so it needs wrapping in its own right.

## Install

```bash theme={null}
pip install openai runapprentice
```

## Capture Responses API calls

```python theme={null}
import os
from openai import OpenAI
from runapprentice import Apprentice
from runapprentice.openai import wrap

apprentice = Apprentice(api_key=os.environ["APPRENTICE_API_KEY"])
client = wrap(OpenAI(), apprentice, task="support-bot")

response = client.responses.create(
    model="gpt-5-mini",
    input="Summarize this support ticket.",
)
print(response.output_text)
```

## Capture Chat Completions calls

```python theme={null}
import os
from openai import OpenAI
from runapprentice import Apprentice
from runapprentice.openai import wrap

apprentice = Apprentice(api_key=os.environ["APPRENTICE_API_KEY"])
client = wrap(OpenAI(), apprentice, task="support-bot")

response = client.chat.completions.create(
    model="gpt-5-mini",
    messages=[{"role": "user", "content": "Summarize this support ticket."}],
)
print(response.choices[0].message.content)
```

Both sync `OpenAI` and async `AsyncOpenAI` clients work. Each completed call logs its input, text output, model name, latency, and any token counts OpenAI returns to the `support-bot` task.

## It cannot break your app

The capture path is fail-open by contract. If the Apprentice backend is slow, down, or unreachable, your OpenAI call still returns unchanged. The only loss is that one trace. OpenAI errors still reach your code unchanged.

Captured traces arrive as **raw** rows: live, unverified. Verify them to gold, or upload curated rows as silver, before you rely on them for optimization.

## Streaming passes through uncaptured

Calls with `stream=True` return an iterator that Apprentice must not consume. They pass through uncaptured and unchanged. The OpenAI SDK's `responses.stream(...)` and `chat.completions.stream(...)` helpers are left untouched and uncaptured for the same reason.

## Redact before anything leaves your process

If your inputs contain sensitive data, pass a `redact` function. It runs in your process, before the trace is sent:

```python theme={null}
client = wrap(
    OpenAI(),
    apprentice,
    task="support-bot",
    redact=lambda text: my_scrubber(text),
)
```

## Next

Once you have traces, optimize the prompt with the [JSON extraction guide](/how-to/json-extraction), or read the [capture reference](/reference/python-sdk#capture) for the manual path.
