Spect
Quickstart

JavaScript

Get started with Spect tracking in your AI application.

Installation

npm install @spect-tools/track
# or
pnpm add @spect-tools/track

Peer Dependencies

# For Vercel AI SDK
npm install ai

# For OpenTelemetry export
npm install @opentelemetry/core @opentelemetry/sdk-trace-base

# For Claude Agent SDK
npm install @anthropic-ai/claude-agent-sdk

API key

You can get your API key from the Spect settings page.

AI SDK integration

Wrap your language model to enable automatic trace collection:

import { wrap } from '@spect-tools/track/ai-sdk-middleware';
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';

const wrappedModel = wrap(openai('gpt-4o'), {
  organizationId: 'your-org-id',
  apiKey: 'your-spect-api-key',
});

const result = await generateText({
  model: wrappedModel,
  prompt: 'Hello!',
});

Claude Agent SDK integration

Track Claude Agent sessions:

import { query } from '@anthropic-ai/claude-agent-sdk';
import { wrapQuery } from '@spect-tools/track';

const spectQuery = wrapQuery(query, {
  organizationId: 'your-org-id',
  apiKey: 'your-spect-api-key',
});

const session = spectQuery({
  prompt: 'Build a hello world app',
  options: {
    model: 'claude-sonnet-4-6',
  },
});

for await (const message of session) {
  console.log(message);
}

Customizing Spect

Operation names

Set the trace operation name with providerOptions.spect.name:

await generateText({
  model: wrappedModel,
  prompt: 'Hello!',
  providerOptions: {
    spect: {
      name: 'my-operation',
    },
  },
});

Spect generates trace IDs automatically so multi-step calls from the same wrapped model stay correlated.

Manual observer API

Use the generic observer when you are building your own SDK adapter. Record transcript items on spans with messages; use the tool overlay for tool-call/tool-result correlation.

import { createObserver } from '@spect-tools/track';

const observer = createObserver({
  organizationId: 'your-org-id',
  apiKey: 'your-spect-api-key',
});

const session = observer.startSession({
  name: 'custom.agent',
  model: { modelId: 'my-model', provider: 'custom' },
});

session.startSpan({
  name: 'user',
  kind: 'user',
  status: 'ok',
  messages: [{ id: crypto.randomUUID(), role: 'user', content: 'Find acute asthma protocols.' }],
});

session.startSpan({
  name: 'evidence_search',
  kind: 'tool-call',
  status: 'ok',
  tool: {
    toolCallId: 'call_1',
    toolName: 'evidence_search',
    parameters: { query: 'acute asthma protocols' },
  },
  messages: [
    {
      id: crypto.randomUUID(),
      role: 'assistant',
      content: [
        {
          type: 'tool-call',
          toolCallId: 'call_1',
          toolName: 'evidence_search',
          input: { query: 'acute asthma protocols' },
        },
      ],
    },
  ],
});

session.startSpan({
  name: 'evidence_search.result',
  kind: 'tool-result',
  status: 'ok',
  tool: {
    toolCallId: 'call_1',
    toolName: 'evidence_search',
    result: { matches: 3 },
  },
  messages: [
    {
      id: crypto.randomUUID(),
      role: 'tool',
      content: [
        {
          type: 'tool-result',
          toolCallId: 'call_1',
          toolName: 'evidence_search',
          output: { type: 'json', value: { matches: 3 } },
        },
      ],
    },
  ],
});

await session.send();

Configuration Options

These top-level options are accepted by wrap(), wrapQuery(), and createObserver().

OptionTypeRequiredDescription
organizationIdstringYesYour organization identifier
apiKeystringYes, unless local: trueSpect API key

All other options have sensible defaults and are typically not needed:

OptionTypeDefaultDescription
collectorUrlstringhttps://collect.spect.toolsCollector endpoint
providerstringauto-detectedFallback provider name when the wrapped model or session model does not supply one
operationNamestringOverride trace name for all calls; takes precedence over per-call name. Falls back to generate / stream / custom when unset
localbooleanfalseLocal-only mode — skip sending to collector
conversationIdstringStable ID that groups a conversation's traces into one session
samplingSamplingOptionsSticky rate sampling, promotion rules, and rule-based overrides
onTrace(payload) => voidCallback when a trace is collected
sendFailuresToConsolebooleantrueLog collector send failures
headersHeadersInitAdditional collector request headers
fetchImpltypeof fetchglobalThis.fetchCustom fetch implementation

AI SDK calls also accept per-call providerOptions.spect values:

OptionTypeDescription
namestringOperation name for that call; ignored when top-level operationName is set
conversationIdstringGroups this call's trace into a session; overrides the top-level value
metadataRecord<string, unknown>Metadata attached to the trace request
samplingSamplingOptionsPer-call sampling override

On this page