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

# OpenAI observability

> Trace every OpenAI API call in TypeScript and Python: prompts, token usage, cost, and latency, all readable in Laminar's transcript view.

## Overview

Laminar automatically instruments the official OpenAI package with a single line of code, allowing you to trace and monitor all your OpenAI API calls without modifying your existing code. This provides complete visibility into your AI application's performance, costs, and behavior.

## Getting Started

<Tabs items={['TypeScript', 'Python']}>
  <Tab title="TypeScript">
    ### 1. Install Laminar and OpenAI

    ```bash theme={null}
    npm install @lmnr-ai/lmnr@latest openai@latest
    ```

    ### 2. Set up your environment variables

    Store your API keys in a `.env` file:

    ```bash theme={null}
    # .env file
    LMNR_PROJECT_API_KEY=your-laminar-project-api-key
    OPENAI_API_KEY=your-openai-api-key
    ```

    Then load them in your application using a package like [dotenv](https://www.npmjs.com/package/dotenv).

    <Note>
      Using OpenAI inside Next.js? The [Vercel AI SDK page](/docs/integrations/vercel-ai-sdk#next-js-setup) covers the Next.js-specific setup (`serverExternalPackages`, `instrumentation.ts`, and `Laminar.patch` for direct SDK clients).
    </Note>

    ### 3. Initialize Laminar

    Just add a single line at the start of your application or file to instrument OpenAI with Laminar.

    ```typescript theme={null}
    import { Laminar } from '@lmnr-ai/lmnr';
    import OpenAI from 'openai';
    import 'dotenv/config'; // Load environment variables

    // This single line instruments all OpenAI API calls
    Laminar.initialize({
      instrumentModules: { OpenAI: OpenAI }
    });

    // Initialize OpenAI client
    const openai = new OpenAI.OpenAI();
    ```

    <Note>
      `import OpenAI from 'openai'` imports the module. Pass it to
      `instrumentModules`, then create the client with `new OpenAI.OpenAI()`.
    </Note>

    ### 4. Use OpenAI as usual

    ```typescript theme={null}
    // Make API calls to OpenAI as you normally would
    const response = await openai.chat.completions.create({
      model: "gpt-5-mini",
      messages: [
        { role: "system", content: "You are a helpful assistant." },
        { role: "user", content: "Hello, how are you?" }
      ],
    });

    console.log(response.choices[0].message.content);
    ```

    All OpenAI API calls are now automatically traced in Laminar.
  </Tab>

  <Tab title="Python">
    ### 1. Install Laminar and OpenAI

    ```bash theme={null}
    pip install -U 'lmnr[all]' openai python-dotenv
    ```

    ### 2. Set up your environment variables

    Store your API keys in a `.env` file:

    ```bash theme={null}
    # .env file
    LMNR_PROJECT_API_KEY=your-laminar-project-api-key
    OPENAI_API_KEY=your-openai-api-key
    ```

    <Note>
      To see an example of how to integrate Laminar within a FastAPI application, check out our [FastAPI integration guide](/docs/guides/fastapi).
    </Note>

    ### 3. Initialize Laminar

    Just add a single line at the start of your application or file to instrument OpenAI with Laminar.

    ```python theme={null}
    from lmnr import Laminar
    from openai import OpenAI
    import os
    from dotenv import load_dotenv

    # Load environment variables from .env file
    load_dotenv()

    # This single line instruments all OpenAI API calls
    Laminar.initialize()

    # Initialize OpenAI client as usual
    client = OpenAI()
    ```

    ### 4. Use OpenAI as usual

    ```python theme={null}
    # Make API calls to OpenAI as you normally would
    response = client.chat.completions.create(
        model="gpt-5-mini",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Hello, how are you?"}
        ]
    )

    print(response.choices[0].message.content)
    ```

    All OpenAI API calls are now automatically traced in Laminar.
  </Tab>
</Tabs>

## Monitoring OpenAI usage

After instrumenting your OpenAI calls with Laminar, you'll be able to:

1. **View detailed traces** of each OpenAI API call, including request and response
2. **Track token usage and cost** across different models
3. **Monitor latency** and performance metrics
4. **Open LLM span in Playground** for prompt engineering
5. **Debug issues** with failed API calls or unexpected model outputs

Visit your Laminar dashboard to view your OpenAI traces and analytics.

## Enrich your OpenAI traces

* Attach sessions, user IDs, metadata, and tags to OpenAI spans via the [SDK reference](/docs/sdk/reference).
* Wrap the functions around your OpenAI calls with `observe` and mark them as [TOOL spans](/docs/tracing/structure/span-types) so they show up in the transcript.
* Images you send to vision-capable OpenAI models are captured automatically ([Tracing Images](/docs/tracing/structure/images)).

## Vision inputs (images)

Laminar automatically traces image inputs (data URLs and external URLs) sent to vision-capable OpenAI models.

<Tabs items={['TypeScript', 'Python']}>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const response = await openai.chat.completions.create({
      model: 'gpt-5-mini',
      messages: [
        {
          role: 'user',
          content: [
            { type: 'text', text: 'Describe this product and its key features.' },
            {
              type: 'image_url',
              image_url: {
                url: 'https://example.com/product-image.jpg',
                detail: 'high',
              },
            },
          ],
        },
      ],
    });

    console.log(response.choices[0].message.content);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = client.chat.completions.create(
        model="gpt-5-mini",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "Describe this product and its key features."},
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": "https://example.com/product-image.jpg",
                            "detail": "high",
                        },
                    },
                ],
            }
        ],
    )

    print(response.choices[0].message.content)
    ```
  </Tab>
</Tabs>

## Decisions API

The [Decisions API](https://developers.openai.com/api/reference/resources/decisions) answers a list of typed questions about a piece of input in one call: predicates (yes or no, with a probability), choices (one value from a fixed list), and scores (a level on a scale). Use it for routing, guardrails, and grading steps in your agent.

In TypeScript, call Decisions through the [Vercel AI SDK](/docs/integrations/vercel-ai-sdk) v7 `experimental_decide` function. Laminar traces it through `LaminarAiSdkTelemetry`, which needs `@lmnr-ai/lmnr` 0.8.50 or later. In Python, Laminar traces every `client.decisions.create` call in the OpenAI SDK, sync and async, with no extra setup. That needs `lmnr` 0.7.65 or later and `openai` 3.26.0 or later.

<Tabs items={['TypeScript', 'Python']}>
  <Tab title="TypeScript">
    ```bash theme={null}
    npm install @lmnr-ai/lmnr@latest ai@latest @ai-sdk/openai@latest
    ```

    ```typescript theme={null}
    import { experimental_decide as decide, registerTelemetry } from 'ai';
    import { openai } from '@ai-sdk/openai';
    import { Laminar, LaminarAiSdkTelemetry } from '@lmnr-ai/lmnr';

    Laminar.initialize();
    registerTelemetry(new LaminarAiSdkTelemetry());

    const { answers } = await decide({
      model: openai.decisionModel('gpt-6-luna'),
      state: 'I was charged twice for my subscription this month. Please fix it today.',
      questions: {
        queue: {
          type: 'choice',
          instructions: 'Which support queue should handle this ticket?',
          criteria: { billing: null, technical: null, account: null },
        },
        urgent: {
          type: 'boolean',
          instructions: 'Does the customer need a response today?',
        },
        frustration: {
          type: 'score',
          instructions: 'How frustrated is the customer?',
          criteria: ['calm', 'annoyed', 'angry'],
        },
      },
      telemetry: {
        functionId: 'triage-ticket',
        includeRuntimeContext: { userId: true },
      },
      runtimeContext: { userId: 'user-123' },
    });

    console.log(answers.queue, answers.urgent, answers.frustration);
    ```
  </Tab>

  <Tab title="Python">
    ```bash theme={null}
    pip install -U lmnr 'openai>=3.26.0'
    ```

    ```python theme={null}
    from lmnr import Laminar
    from openai import OpenAI

    Laminar.initialize()

    client = OpenAI()

    decision = client.decisions.create(
        model="gpt-6-luna",
        input="I was charged twice for my subscription this month. Please fix it today.",
        questions=[
            {
                "type": "choice",
                "name": "queue",
                "instructions": "Which support queue should handle this ticket?",
                "choices": [{"value": "billing"}, {"value": "technical"}, {"value": "account"}],
            },
            {
                "type": "predicate",
                "name": "urgent",
                "instructions": "Does the customer need a response today?",
            },
            {
                "type": "score",
                "name": "frustration",
                "instructions": "How frustrated is the customer?",
                "levels": [{"label": "calm"}, {"label": "annoyed"}, {"label": "angry"}],
            },
        ],
        safety_identifier="user-123",
    )

    for answer in decision.answers:
        print(answer)
    ```
  </Tab>
</Tabs>

Each call becomes an LLM span, named `ai.decide` in TypeScript (`ai.decide triage-ticket` when you set `telemetry.functionId`) and `openai.decision` in Python. The span carries:

* The model, and the input you sent as the span input. A string becomes a user message. Objects, part arrays, and message lists (including image parts) are recorded as you passed them.
* The full question set, with instructions, choices, and levels for every question, in the span's output schema.
* The typed answers as the span output, with probabilities intact.
* Token counts, including cached input tokens.
* The user ID: `runtimeContext.userId` in TypeScript (opted in with `includeRuntimeContext`), `safety_identifier` in Python.

Failed calls, such as an unknown model, are marked as errors with the exception recorded. A Decisions call sits in the transcript next to the LLM calls it routes or guards, so you can see the decision next to the action it triggered. To keep the model and token counts but omit the input, questions, and answers, set `recordInputs: false` and `recordOutputs: false` in the AI SDK `telemetry` options, or `LMNR_TRACE_CONTENT=false` in Python.

## Related integrations

<CardGroup cols={2}>
  <Card title="Anthropic" href="/docs/integrations/anthropic">
    Trace the Anthropic SDK directly in TypeScript and Python.
  </Card>

  <Card title="Gemini" href="/docs/integrations/gemini">
    Trace Gemini calls made with the Google Gen AI SDK.
  </Card>

  <Card title="OpenRouter" href="/docs/integrations/openrouter">
    Trace OpenRouter requests with model and cost attribution.
  </Card>

  <Card title="OpenAI Agents SDK" href="/docs/integrations/openai-agents-sdk">
    Trace agent runs, handoffs, and tool calls.
  </Card>

  <Card title="Vercel AI SDK" href="/docs/integrations/vercel-ai-sdk">
    Trace `generateText` and `streamText` in Next.js and Node.js.
  </Card>

  <Card title="All integrations" href="/docs/integrations">
    Browse every provider, framework, coding agent, and browser integration Laminar supports.
  </Card>
</CardGroup>


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