> ## 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 Node.js SDK

> Instrument and observe OpenAI calls

This module provides automatic instrumentation for the [OpenAI Node.js SDK](https://github.com/openai/openai-node). which may be used in conjunction with [@opentelemetry/sdk-trace-node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-node).

## Install

```bash
npm install --save @arizeai/openinference-instrumentation-openai openai

npm install --save @opentelemetry/api @opentelemetry/sdk-trace-node \
  @opentelemetry/sdk-trace-base \
  @opentelemetry/resources \
  @opentelemetry/semantic-conventions \
  @opentelemetry/instrumentation \
  @opentelemetry/exporter-trace-otlp-proto \
  @arizeai/openinference-semantic-conventions
```

## Setup

To instrument your application, import and enable `OpenAIInstrumentation`

Create the `instrumentation.js` file:

```javascript expandable
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { SEMRESATTRS_PROJECT_NAME } from "@arizeai/openinference-semantic-conventions";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
// OpenAI instrumentation
import OpenAI from "openai";
import { OpenAIInstrumentation } from "@arizeai/openinference-instrumentation-openai";

const COLLECTOR_ENDPOINT = "your-phoenix-collector-endpoint";
const SERVICE_NAME = "openai-app";

const provider = new NodeTracerProvider({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: SERVICE_NAME,
    [SEMRESATTRS_PROJECT_NAME]: SERVICE_NAME,
  }),
  spanProcessors: [
    new SimpleSpanProcessor(
      new OTLPTraceExporter({
        url: `${COLLECTOR_ENDPOINT}/v1/traces`,
        // (optional) if connecting to Phoenix with Authentication enabled
        headers: { Authorization: `Bearer ${process.env.PHOENIX_API_KEY}` },
      })
    ),
  ],
});

provider.register();
console.log("Provider registered");

const instrumentation = new OpenAIInstrumentation();
instrumentation.manuallyInstrument(OpenAI);

registerInstrumentations({
  instrumentations: [instrumentation],
});

console.log("OpenAI instrumentation registered");
```

## Run OpenAI

Import the `instrumentation.js` file first, then use OpenAI as usual.

```javascript expandable
import "./instrumentation.js";
import OpenAI from "openai";

// set OPENAI_API_KEY in environment, or pass it in arguments
const openai = new OpenAI({
    apiKey: 'your-openai-api-key'
});

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

## Observe

After setting up instrumentation and running your OpenAI application, traces will appear in the Phoenix UI for visualization and analysis. Chat completions, completions, the Responses API, embeddings, and decisions are instrumented.

## Decisions API

The [OpenAI Decisions API](https://developers.openai.com/api/docs/guides/decisions) (`openai.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 >= 7.30.0` and `@arizeai/openinference-instrumentation-openai >= 4.4.0`. Calls are traced with the setup above, with no extra configuration:

```javascript expandable
import "./instrumentation.js";
import OpenAI from "openai";

const openai = new OpenAI();

const decision = await openai.decisions.create({
  model: "gpt-6-luna",
  input: "I was charged twice for my order.",
  questions: [
    {
      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." },
      ],
    },
  ],
});

console.log(decision.answers);
```

Each call shows up in Phoenix as a **[DECISION span](/docs/phoenix/tracing/concepts-tracing/otel-openinference/span-kinds#decision)** named `OpenAI Decisions`, 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` and `decision.provider`: both `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 the `hideInputs` and `hideOutputs` trace config options redact them completely.

## Custom Tracer Provider

You can specify a custom tracer provider for OpenAI instrumentation in multiple ways:

### Method 1: Pass tracerProvider on instantiation

```javascript expandable
const instrumentation = new OpenAIInstrumentation({
  tracerProvider: customTracerProvider,
});
instrumentation.manuallyInstrument(OpenAI);
```

### Method 2: Set tracerProvider after instantiation

```javascript
const instrumentation = new OpenAIInstrumentation();
instrumentation.setTracerProvider(customTracerProvider);
instrumentation.manuallyInstrument(OpenAI);
```

### Method 3: Pass tracerProvider to registerInstrumentations

```typescript
const instrumentation = new OpenAIInstrumentation();
instrumentation.manuallyInstrument(OpenAI);

registerInstrumentations({
  instrumentations: [instrumentation],
  tracerProvider: customTracerProvider,
});
```

## Resources

* [Example project](https://github.com/Arize-ai/openinference/tree/main/js/examples/openai)

* [OpenInference package for OpenAI Node.js SDK](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-openai)
