> ## Documentation Index
> Fetch the complete documentation index at: https://arizeai-433a7140.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeSafe AI Tracing (TypeScript)

> Instrument TypeSafe AI SDK calls in TypeScript/Node.js

> [!NOTE]
>
> Looking for Python? See the [Python guide](/docs/phoenix/integrations/llm-providers/typesafe/typesafe-python.md).

[![NPM Version](https://img.shields.io/npm/v/@arizeai%2Fopeninference-instrumentation-typesafe.svg)](https://www.npmjs.com/package/@arizeai/openinference-instrumentation-typesafe)

This module provides [OpenInference](https://github.com/Arize-ai/openinference) instrumentation for the [TypeSafe AI Node.js SDK](https://www.npmjs.com/package/@typesafe-ai/sdk) (`@typesafe-ai/sdk`). Each `TypeSafeClient.systemOne` call is captured as an OpenInference [`DECISION` span](/docs/phoenix/tracing/concepts-tracing/otel-openinference/span-kinds#decision) with JSON `input.value` / `output.value`, and with the model and token usage recorded under `decision.*`.

Requires Node.js 20+ and `@arizeai/openinference-instrumentation-typesafe >= 0.4.1`. `client.models.list()` is not instrumented.

## Install

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
npm install --save @arizeai/phoenix-otel @arizeai/openinference-instrumentation-typesafe @typesafe-ai/sdk
```

## Setup

Use the `register` function from `@arizeai/phoenix-otel` to connect to Phoenix, then register the TypeSafe instrumentation.

Create the `instrumentation.ts` file:

```typescript expandable theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { register } from "@arizeai/phoenix-otel";
import { TypeSafeInstrumentation } from "@arizeai/openinference-instrumentation-typesafe";

// Initialize Phoenix tracing
export const provider = register({
  projectName: "typesafe-app",
  // If Phoenix is running elsewhere:
  // url: "https://your-phoenix.example.com",
  // apiKey: process.env.PHOENIX_API_KEY,
  // If using self-hosted Phoenix:
  // url: "http://localhost:6006",
});

// Set up TypeSafe SDK instrumentation
const instrumentation = new TypeSafeInstrumentation({
  tracerProvider: provider,
});
```

> [!NOTE]
>
> Registering the instrumentation this way patches `@typesafe-ai/sdk` at load time, which requires `instrumentation.ts` to run **before** the SDK is imported (CommonJS only). For ESM, bundlers, or when the SDK is imported first, call `manuallyInstrument` instead:
>
> ```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
> import * as TypeSafe from "@typesafe-ai/sdk";
> import { TypeSafeInstrumentation } from "@arizeai/openinference-instrumentation-typesafe";
>
> const instrumentation = new TypeSafeInstrumentation({ tracerProvider: provider });
> instrumentation.manuallyInstrument(TypeSafe);
> ```

## Usage

```typescript expandable theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import "./instrumentation.js";
import { TypeSafeClient, choice } from "@typesafe-ai/sdk";

const client = new TypeSafeClient();
const { data, requestId } = await client
  .systemOne({
    state: { document: "I was charged twice. Please fix this ASAP." },
    questions: {
      category: choice("What is this ticket about?", {
        billing: null,
        technical: null,
        other: null,
      }),
    },
  })
  .withResponse();

console.log(data.answers.category, requestId);
```

> [!NOTE]
>
> Spans are exported in batches. In short-lived scripts, call `await provider.forceFlush()` before the process exits so all spans are delivered to Phoenix.

## Observe

With instrumentation enabled, each `systemOne` call shows up in Phoenix as a **DECISION span** containing:

* The full `state` and `questions` sent to TypeSafe as `input.value` (JSON)
* The typed `answers` returned as `output.value` (JSON)
* `decision.system` and `decision.provider`: both `typesafe`
* `decision.request.model_name`, `decision.response.model_name` (the resolved model), and `decision.model_name`
* `decision.token_count.input` and `decision.token_count.output`, when reported by the SDK

Phoenix names the decision model in the span's input card. The model and its usage follow the OpenInference [decision span conventions](https://github.com/Arize-ai/openinference/blob/main/spec/decision_spans.md) and live under `decision.*` rather than `llm.*`, so decision-model calls stay out of Phoenix's LLM token and cost reporting. Earlier releases recorded them under `llm.*`, and releases before 0.3.0 recorded them as LLM spans.

![TypeSafe AI traces in Phoenix](https://storage.googleapis.com/arize-phoenix-assets/assets/images/typesafe.png)

## Configuration

Pass `traceConfig` to mask sensitive request/response payloads before they leave your process:

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
const instrumentation = new TypeSafeInstrumentation({
  tracerProvider: provider,
  traceConfig: {
    hideInputs: true,
    hideOutputs: true,
  },
});
```

See the [package README](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-typesafe) for the full list of `traceConfig` masking options and the SDK compatibility table.

## Resources

* [NPM Package](https://www.npmjs.com/package/@arizeai/openinference-instrumentation-typesafe)

* [Runnable examples](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-typesafe/examples)

* [TypeSafe AI SDK on npm](https://www.npmjs.com/package/@typesafe-ai/sdk)

* [TypeSafe AI Documentation](https://docs.typesafe.ai/)
