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

# OpenAI

> Follow the LangWatch OpenAI TypeScript integration guide to trace LLM calls and support agent testing workflows.

<div className="not-prose" style={{display: "flex", gap: "8px", padding: "0"}}>
  <div>
    <a href="https://github.com/langwatch/langwatch/tree/main/typescript-sdk" target="_blank">
      <img src="https://img.shields.io/badge/repo-langwatch-blue?style=flat&logo=Github" noZoom alt="LangWatch TypeScript Repo" />
    </a>
  </div>

  <div>
    <a href="https://www.npmjs.com/package/langwatch" target="_blank">
      <img src="https://img.shields.io/npm/v/langwatch?color=007EC6" noZoom alt="LangWatch TypeScript SDK version" />
    </a>
  </div>
</div>

LangWatch library is the easiest way to integrate your TypeScript application with LangWatch, the messages are synced on the background so it doesn't intercept or block your LLM calls.

<Note>Protip: wanna to get started even faster? Copy our <a href="/llms.txt" target="_blank">llms.txt</a> and ask an AI to do this integration</Note>

#### Prerequisites

* Obtain your `LANGWATCH_API_KEY` from the [LangWatch dashboard](https://app.langwatch.ai/).

#### Installation

```sh theme={null}
npm install langwatch
```

#### Configuration

Ensure `LANGWATCH_API_KEY` is set:

<Tabs>
  <Tab title="Environment variable">
    ```bash .env theme={null}
    LANGWATCH_API_KEY='your_api_key_here'
    ```
  </Tab>

  <Tab title="Client parameters">
    ```typescript theme={null}
    import { LangWatch } from 'langwatch';

    const langwatch = new LangWatch({
      apiKey: 'your_api_key_here',
    });
    ```
  </Tab>
</Tabs>

## Basic Concepts

* Each message triggering your LLM pipeline as a whole is captured with a [Trace](/concepts#traces).
* A [Trace](/concepts#traces) contains multiple [Spans](/concepts#spans), which are the steps inside your pipeline.
  * A span can be an LLM call, a database query for a RAG retrieval, or a simple function transformation.
  * Different types of [Spans](/concepts#spans) capture different parameters.
  * [Spans](/concepts#spans) can be nested to capture the pipeline structure.
* [Traces](/concepts#traces) can be grouped together on LangWatch Dashboard by having the same [`thread_id`](/concepts#threads) in their metadata, making the individual messages become part of a conversation.
  * It is also recommended to provide the [`user_id`](/concepts#user-id) metadata to track user analytics.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm i langwatch openai
  ```

  ```bash pnpm theme={null}
  pnpm add langwatch openai
  ```

  ```bash yarn theme={null}
  yarn add langwatch openai
  ```

  ```bash bun theme={null}
  bun add langwatch openai
  ```
</CodeGroup>

## Usage

<Info>
  The LangWatch API key is configured by default via the `LANGWATCH_API_KEY` environment variable.
</Info>

Set up observability and use `withActiveSpan` to capture your OpenAI calls:

```typescript theme={null}
import { setupObservability } from "langwatch/observability/node";
import { getLangWatchTracer } from "langwatch";
import { OpenAI } from "openai";

setupObservability({ serviceName: "<project_name>" });

const tracer = getLangWatchTracer("<project_name>");

async function main(message: string): Promise<string> {
  const openai = new OpenAI();

  return await tracer.withActiveSpan("main", async span => {
    span.setInput(message);

    const response = await openai.chat.completions.create({
      model: "gpt-5",
      messages: [{ role: "user", content: message }],
    });

    const text = response.choices[0].message.content as string;
    span.setOutput(text);
    return text;
  });
}

console.log(await main("Hey, tell me a joke"));
```

The `withActiveSpan` method automatically creates the span, handles errors, and ends the span when the function completes.

<Note>
  On short-live environments like Lambdas or Serverless Functions, be sure to call <br /> `await trace.sendSpans();` to wait for all pending requests to be sent before the runtime is destroyed.
</Note>

## Capture a RAG Span

Appart from LLM spans, another very used type of span is the RAG span. This is used to capture the retrieved contexts from a RAG that will be used by the LLM, and enables a whole new set of RAG-based features evaluations for RAG quality on LangWatch.

To capture a RAG, you can simply start a RAG span inside the trace, giving it the input query being used:

```typescript theme={null}
const ragSpan = trace.startRAGSpan({
  name: "my-vectordb-retrieval", // optional
  input: { type: "text", value: "search query" },
});

// proceed to do the retrieval normally
```

Then, after doing the retrieval, you can end the RAG span with the contexts that were retrieved and will be used by the LLM:

```typescript theme={null}
ragSpan.end({
  contexts: [
    {
      documentId: "doc1",
      content: "document chunk 1",
    },
    {
      documentId: "doc2",
      content: "document chunk 2",
    },
  ],
});
```

<Note>
  On LangChain.js, RAG spans are captured automatically by the LangWatch callback when using LangChain Retrievers, with `source` as the documentId.
</Note>

## Capture an arbritary Span

You can also use generic spans to capture any type of operation, its inputs and outputs, for example for a function call:

```typescript theme={null}
// before the function starts
const span = trace.startSpan({
  name: "weather_function",
  input: {
    type: "json",
    value: {
      city: "Tokyo",
    },
  },
});

// ...after the function ends
span.end({
  output: {
    type: "json",
    value: {
      weather: "sunny",
    },
  },
});
```

You can also nest spans one inside the other, capturing your pipeline structure, for example:

```typescript theme={null}
const span = trace.startSpan({
  name: "pipeline",
});

const nestedSpan = span.startSpan({
  name: "nested_pipeline",
});

nestedSpan.end()

span.end()
```

Both LLM and RAG spans can also be nested like any arbritary span.

## Capturing Exceptions

To capture also when your code throws an exception, you can simply wrap your code around a try/catch, and update or end the span with the exception:

```typescript theme={null}
try {
  throw new Error("unexpected error");
} catch (error) {
  span.end({
    error: error,
  });
}
```

## Capturing custom evaluation results

[LangWatch Evaluators](/evaluations/evaluators/list) can run automatically on your traces, but if you have an in-house custom evaluator, you can also capture the evaluation
results of your custom evaluator on the current trace or span by using the `.addEvaluation` method:

```typescript theme={null}
import { type LangWatchTrace } from "langwatch";

async function llmStep({ message, trace }: { message: string, trace: LangWatchTrace }): Promise<string> {
    const span = trace.startLLMSpan({ name: "llmStep" });

    // ... your existing code

    span.addEvaluation({
        name: "custom evaluation",
        passed: true,
        score: 0.5,
        label: "category_detected",
        details: "explanation of the evaluation results",
    });
}
```

The evaluation `name` is required and must be a string. The other fields are optional, but at least one of `passed`, `score` or `label` must be provided.

## Related

* [Capturing RAG](/integration/typescript/tutorials/capturing-rag) - Learn how to capture RAG data from retrievers and tools
* [Capturing Metadata and Attributes](/integration/typescript/tutorials/capturing-metadata) - Add custom metadata and attributes to your traces and spans
* [Capturing Evaluations & Guardrails](/integration/python/tutorials/capturing-evaluations-guardrails) - Log evaluations and implement guardrails in your OpenAI applications
