> ## Documentation index
> Fetch the complete documentation index at: https://arize.com/llms.txt
> Ask it with GET https://arize.com/ask?q=<keywords or question>. Short keywords rank best.
> Use these to discover all available pages before exploring further.

# OpenAI Tracing

> Phoenix provides auto-instrumentation for the [OpenAI Python Library](https://github.com/openai/openai-python).

<Info>
**Note***: This instrumentation also works with Azure OpenAI

</Info>

## Install

```bash
pip install openinference-instrumentation-openai openai
```

## Setup

Add your OpenAI API key as an environment variable:

```shell
export OPENAI_API_KEY=[your_key_here]
```

Connect your application to Phoenix with the `register` function:

<CodeBlock language="python">
{`from phoenix.otel import register

# configure the Phoenix tracer
tracer_provider = register(
  project_name="$my-llm-app", # Default is 'default'
  auto_instrument=True # Auto-instrument your app based on installed OI dependencies
)`}
</CodeBlock>

## Run OpenAI

```python
import openai

client = openai.OpenAI()
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Write a haiku."}],
)
print(response.choices[0].message.content)
```

## Observe

Now that you have tracing setup, all invocations of OpenAI (chat completions, completions, the Responses API, embeddings, images, and decisions) will be streamed to your running Phoenix for observability and evaluation.

## Decisions API

The [OpenAI Decisions API](https://developers.openai.com/api/docs/guides/decisions) (`client.decisions.create`) asks a decision model a fixed set of typed questions about text or images. It returns one typed, probabilistic answer per question instead of generated text, which makes it a good fit for routing, classification, and scoring inside your application.

Requires `openai >= 3.26.0` and `openinference-instrumentation-openai >= 0.1.64`. Calls are traced with the setup above, with no extra configuration:

```python expandable
import openai

client = openai.OpenAI()
decision = client.decisions.create(
    model="gpt-6-luna",
    input="I was charged twice for my order.",
    questions=[
        {
            "type": "predicate",
            "name": "is_refund",
            "instructions": "Is this a refund request?",
        },
        {
            "type": "choice",
            "name": "department",
            "instructions": "Which department should handle this complaint?",
            "choices": [
                {"value": "billing", "description": "Payments, invoices, and refunds."},
                {"value": "technical", "description": "Problems using the product."},
                {"value": "shipping", "description": "Delivery and tracking."},
            ],
        },
        {
            "type": "score",
            "name": "urgency",
            "instructions": "How urgent is this message?",
            "levels": [
                {"label": "low", "description": "Can wait a week."},
                {"label": "medium"},
                {"label": "high", "description": "Needs a reply today."},
            ],
        },
    ],
)
for answer in decision.answers:
    print(answer.model_dump_json())
```

Each call shows up in Phoenix as a **[DECISION span](/docs/phoenix/tracing/concepts-tracing/otel-openinference/span-kinds#decision)** rather than an LLM span, containing:

- `input.value`: the request body (`model`, `input`, `questions`) as JSON
- `output.value`: the response body (`answers`, `model`, `usage`) as JSON
- `decision.system`: `openai`
- `decision.provider`: inferred from the client's host, so `openai`, or `azure` for Azure OpenAI
- `decision.request.model_name`, `decision.response.model_name`, and `decision.model_name`
- `decision.token_count.input` and `decision.token_count.output`, from the response's `usage`

Phoenix names the decision model in the span's input card. The model and its usage are recorded under `decision.*` rather than `llm.*`, so decision-model calls stay out of Phoenix's LLM token and cost reporting.

The questions and answers live only in `input.value` and `output.value`, so `TraceConfig(hide_inputs=True)` and `hide_outputs=True` redact them completely. `hide_input_images` applies to inline images in the input.

## Resources

* [Example notebook](https://github.com/Arize-ai/phoenix/blob/main/tutorials/tracing/openai_tracing_tutorial.ipynb)

* [OpenInference package](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai)

* [Working examples](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai/examples)
