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

# TypeSafe AI

> Trace TypeSafe AI systemOne calls with the OpenInference instrumentor and send spans to Arize AX for LLM observability.

[TypeSafe AI](https://typesafe.ai) answers typed questions about a piece of state — yes/no, scores, and labeled choices — through its `systemOne` API, returning answers whose TypeScript types are inferred from the questions you ask. Arize AX captures every call through the [`@arizeai/openinference-instrumentation-typesafe`](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-typesafe) instrumentor, which emits one OpenInference LLM span per `TypeSafeClient.systemOne` call with the request and answers as JSON, the resolved model, and token usage.

<Note>
  This is the TypeScript / JavaScript guide. For the Python instrumentor, see [TypeSafe AI](/docs/ax/integrations/python-agent-frameworks/typesafe/typesafe-tracing).
</Note>

<Note>
  `client.models.list()` is not instrumented — only `systemOne` produces spans. SDK retries are folded into one span rather than emitting a span per attempt.
</Note>

## Prerequisites

* Node.js 20+
* `@typesafe-ai/sdk` 0.6.0 or newer — the range the instrumentor patches
* An Arize AX account ([sign up](https://arize.com/sign-up/))
* A `TYPESAFE_API_KEY` from [TypeSafe AI](https://docs.typesafe.ai/)

## Launch Arize AX

1. Sign in to your [Arize AX account](https://app.arize.com/).
2. From **Space Settings**, copy your **Space ID** and **API Key**. You will set them as `ARIZE_SPACE_ID` and `ARIZE_API_KEY` below.

## Install

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
npm install @typesafe-ai/sdk tsx typescript \
  @arizeai/openinference-instrumentation-typesafe \
  @arizeai/openinference-semantic-conventions \
  @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/resources \
  @opentelemetry/sdk-trace-base \
  @opentelemetry/sdk-trace-node \
  @opentelemetry/semantic-conventions
```

## Configure credentials

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export ARIZE_SPACE_ID="<your-space-id>"
export ARIZE_API_KEY="<your-api-key>"
export ARIZE_PROJECT_NAME="typesafe-tracing-example"
export TYPESAFE_API_KEY="<your-typesafe-api-key>"
```

## Setup tracing

Register a `NodeTracerProvider` that ships spans to Arize AX, then hand it to `TypeSafeInstrumentation` and patch the SDK with `manuallyInstrument`.

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
// instrumentation.ts
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 * as TypeSafe from "@typesafe-ai/sdk";
import {
  TypeSafeInstrumentation,
} from "@arizeai/openinference-instrumentation-typesafe";
import {
  SEMRESATTRS_PROJECT_NAME,
} from "@arizeai/openinference-semantic-conventions";

const projectName =
  process.env.ARIZE_PROJECT_NAME ?? "typesafe-tracing-example";

export const provider = new NodeTracerProvider({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: projectName,
    [SEMRESATTRS_PROJECT_NAME]: projectName,
  }),
  spanProcessors: [
    new SimpleSpanProcessor(
      new OTLPTraceExporter({
        url: "https://otlp.arize.com/v1/traces",
        headers: {
          "arize-space-id": process.env.ARIZE_SPACE_ID ?? "",
          "arize-api-key": process.env.ARIZE_API_KEY ?? "",
        },
      }),
    ),
  ],
});

provider.register();

const instrumentation = new TypeSafeInstrumentation({
  tracerProvider: provider,
});

instrumentation.manuallyInstrument(TypeSafe);

console.log("Arize AX tracing initialized for TypeSafe AI.");
```

<Note>
  `manuallyInstrument(TypeSafe)` patches `TypeSafeClient.prototype`, so it covers ESM, bundlers, and modules imported before the instrumentor was enabled — import `instrumentation.ts` first and the rest of your code can keep importing `TypeSafeClient` normally. On CommonJS you can instead let Node's `require` hook patch the SDK, by installing `@opentelemetry/instrumentation` and passing the instrumentor to its `registerInstrumentations({ instrumentations: [instrumentation] })` in place of the `manuallyInstrument` call. That path only works when `instrumentation.ts` runs before the SDK is required, which is why the snippet above uses `manuallyInstrument`.
</Note>

## Run TypeSafe AI

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
// example.ts

// Importing instrumentation first ensures tracing is set up before the
// TypeSafe client is used.
import { provider } from "./instrumentation";

import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

// The client reads TYPESAFE_API_KEY from the environment and defaults to
// the jev-latest model.
const client = new TypeSafeClient();

const { data } = 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.choice, data.answers.category.confidence);

// Flush any pending spans before the process exits.
await provider.forceFlush();
```

Run the example with `npx tsx example.ts`. The instrumented `systemOne` still returns an SDK `APIPromise`, so `await`, `withResponse()`, and `map()` behave exactly as they do without tracing.

### Expected output

```text wrap theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
Arize AX tracing initialized for TypeSafe AI.
billing 1
```

## Verify in Arize AX

1. Open your Arize AX space and select project **`typesafe-tracing-example`**.
2. You should see a new trace within \~30 seconds containing a single `TypeSafeClient.systemOne` LLM span. Its input is the JSON request (`state` plus `questions`), its output is the JSON result (`model`, `answers`, `usage`), and it carries `llm.request.model_name` (`jev-latest`, which this instrumentor fills in from the client default when the call omits `model`), `llm.response.model_name` (the resolved version, for example `jev-1.13.0`), and prompt, completion, and total token counts.
3. If no traces appear, see [Troubleshooting](#troubleshooting).

### Check from the skill, CLI, or SDK

Confirm spans are actually reaching your Arize AX project. Use whichever fits your workflow — the skill and CLI work for any framework; the SDK check is shown for each language.

<Tabs>
  <Tab title="Arize skill (agent)">
    Install the [Arize Skills](https://github.com/Arize-ai/arize-skills) plugin and let your coding agent check for you:

    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
    npx skills add Arize-ai/arize-skills
    ```

    Then prompt your agent:

    > Use the `arize-trace` skill to export and analyze recent traces from my project. Confirm spans are arriving, and summarize any errors or latency issues.
  </Tab>

  <Tab title="AX CLI">
    Export recent spans for your project — any rows mean traces are landing:

    ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
    ax spans export "$ARIZE_PROJECT_NAME" --space "$ARIZE_SPACE_ID" \
      --limit 5 --stdout | jq 'length'
    ```

    A non-zero count confirms spans reached Arize AX. Run `ax auth login` first if you have not authenticated. See the [`ax spans` reference](/docs/api-clients/cli/spans).
  </Tab>

  <Tab title="SDK">
    Query the project's spans and check that at least one came back.

    <CodeGroup>
      ```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
      import os
      from arize import ArizeClient

      client = ArizeClient(api_key=os.environ["ARIZE_API_KEY"])
      resp = client.spans.list(
          project=os.environ["ARIZE_PROJECT_NAME"],
          space=os.environ["ARIZE_SPACE_ID"],
          limit=5,
      )
      count = len(resp.spans)
      print(
          f"{count} span(s) found" if count else "No spans yet — recheck setup"
      )
      ```

      ```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
      // Reads ARIZE_API_KEY from the environment.
      import { listSpans } from "@arizeai/ax-client";

      const { data: spans } = await listSpans({
        project: process.env.ARIZE_PROJECT_NAME!,
        space: process.env.ARIZE_SPACE_ID!,
        limit: 5,
      });
      const count = spans.length;
      console.log(
        count ? `${count} span(s) found` : "No spans yet — recheck setup",
      );
      ```

      ```go Go theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
      client, err := arize.NewClient(
          arize.Config{APIKey: os.Getenv("ARIZE_API_KEY")},
      )
      if err != nil {
          log.Fatal(err)
      }
      resp, err := client.Spans.List(ctx, spans.ListRequest{
          Project: os.Getenv("ARIZE_PROJECT_NAME"),
          Space:   os.Getenv("ARIZE_SPACE_ID"),
          Limit:   5,
      })
      if err != nil {
          log.Fatal(err)
      }
      fmt.Printf("%d span(s) found\n", len(resp.Spans))
      ```
    </CodeGroup>

    SDK span references: [Python](/docs/api-clients/python/version-8/client-resources/spans) · [TypeScript](/docs/api-clients/typescript/version-1/client-resources/spans) · [Go](/docs/api-clients/go/version-2/client-resources/spans).
  </Tab>
</Tabs>

## Mask sensitive payloads

The request `state` is the document you are asking about, so it often carries customer data. Pass `traceConfig` to drop inputs, outputs, or both before the span leaves your process:

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

Model name and token counts are still recorded, so cost and latency dashboards keep working with payloads masked. See the [instrumentor README](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-typesafe) for the full list of masking options.

## Troubleshooting

* **No traces in Arize AX.** Confirm `ARIZE_SPACE_ID` and `ARIZE_API_KEY` are set in the same shell that runs `example.ts`. Enable OpenTelemetry debug logs with `export OTEL_LOG_LEVEL=debug` and re-run.
* **Spans missing but `systemOne` returns answers.** `manuallyInstrument(TypeSafe)` must run before the first `systemOne` call, so `instrumentation.ts` has to be imported before any code that calls the SDK.
* **No span for `models.list()`.** Expected — the instrumentor only wraps `systemOne`.
* **`401` from TypeSafe.** Verify `TYPESAFE_API_KEY` is set and valid; the client reads it from the environment unless you pass `apiKey` explicitly.
* **SDK fails to install or import.** The TypeSafe SDK requires Node.js 20+. Check with `node --version`.
* **Process exits before spans flush.** Spans are exported asynchronously; always `await provider.forceFlush()` (or `provider.shutdown()`) before the process exits.

## Resources

<CardGroup>
  <Card icon="book-open" href="https://docs.typesafe.ai/" title="TypeSafe AI Documentation" horizontal />

  <Card icon="terminal" href="https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-typesafe" title="OpenInference TypeSafe Instrumentor (JS/TS)" horizontal />

  <Card icon="code" href="https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-typesafe/examples" title="Runnable TypeSafe Tracing Examples" horizontal />

  <Card icon="npm" href="https://www.npmjs.com/package/@typesafe-ai/sdk" title="TypeSafe AI SDK on npm" horizontal />

  <Card icon="book-open" href="/docs/ax/integrations/python-agent-frameworks/typesafe/typesafe-tracing" title="TypeSafe AI (Python) tracing" horizontal />
</CardGroup>
