Skip to main content

TypeScript SDK

The Seer TypeScript SDK provides a lightweight client for logging retrieval events from Node.js and browser environments.

Private Beta

Seer is currently in private beta. Email ben@seersearch.com to request access and receive SDK installation instructions.

Installation

Once you have beta access, you'll receive installation instructions. The SDK supports both CommonJS and ES modules:

# Install via URL provided in beta access email
npm install <sdk-url>

# With OpenTelemetry support (optional)
npm install @opentelemetry/api

Quick Start

import { SeerClient } from '@seer/sdk';

const client = new SeerClient(); // reads SEER_API_KEY from env

// No await needed! Fire-and-forget by default
client.log({
task: "How do I reset my password?",
context: [
{ text: "To reset your password, go to Settings...", score: 0.89 }
],
});

// Events auto-flush on process exit

Configuration

Environment Variables

VariableDescriptionDefault
SEER_API_KEYAPI key (required)
SEER_INGESTION_URLIngestion endpointhttps://api.seersearch.com/v1/log

Constructor Options

const client = new SeerClient({
apiKey: "seer_live_...", // or from SEER_API_KEY env
fireAndForget: true, // async (default) or sync
maxQueueSize: 10_000, // max events before dropping
flushInterval: 500, // worker flush interval (ms)
timeout: 5000, // HTTP timeout (ms)
});

SeerClient API

log()

Log a retrieval event.

await client.log({
// Required
task: string, // The user query
context: Array<string | Passage>, // Retrieved passages

// Metadata
metadata?: Record<string, unknown>, // Free-form metadata

// OpenTelemetry (auto-detected by default)
trace_id?: string, // OTEL trace ID (32 hex)
span_id?: string, // OTEL span ID (16 hex)
parent_span_id?: string, // OTEL parent span ID
span_name?: string, // Operation type
useOtelTrace?: boolean, // Auto-detect OTEL context (default: true)

// Multi-hop / Agentic
is_final_context?: boolean, // Mark as final evidence
subquery?: string, // Decomposed sub-question

// Accuracy testing
ground_truth?: GroundTruth, // For comparing against expected

// Other options
created_at?: string, // ISO8601 timestamp override
sample_rate?: number, // 0.0-1.0 sampling rate
});

Returns:

  • fireAndForget=true (default): void — no await needed, event is queued
  • fireAndForget=false: Promise<string | null> — await to get record_id

flush()

Wait for all queued events to be sent.

await client.flush(timeout?: number);

stats()

Get client statistics.

const stats = client.stats();
console.log(`Sent: ${stats.eventsSent}, Failed: ${stats.eventsFailed}`);

Returns a ClientStats object:

interface ClientStats {
eventsEnqueued: number;
eventsSent: number;
eventsDropped: number;
eventsFailed: number;
bytesSent: number;
lastError?: string;
}

close()

Shutdown client gracefully (flushes remaining events).

await client.close();

Context Format

Passages can be simple strings or objects with metadata:

// Simple strings
const context = ["Passage one...", "Passage two..."];

// Passage objects (recommended)
const context = [
{
text: "The main content...", // Required
score: 0.95, // Optional: retrieval score
id: "doc-123", // Optional: document ID
source: "wiki", // Optional: source name
metadata: { author: "..." }, // Optional: custom metadata
}
];

Function Wrapper

Use wrapWithSeerTrace to automatically log function calls:

import { wrapWithSeerTrace, SeerClient } from '@seer/sdk';

const client = new SeerClient();

const tracedRetrieve = wrapWithSeerTrace(
async (query: string) => {
// Your retrieval logic
return [{ text: "Result...", score: 0.9 }];
},
{
taskArgIndex: 0, // Index of the query argument
contextFromReturn: true, // Return value is the context
metadata: { service: "help-bot" },
sampleRate: 0.10,
client,
}
);

// Automatically logged when called
const results = await tracedRetrieve("How do I reset my password?");

Wrapper Parameters

ParameterTypeDescription
taskArgIndexnumberIndex of argument containing the query
contextArgIndexnumberIndex of argument containing context
contextFromReturnbooleanUse return value as context
metadataRecord<string, unknown>Static metadata to attach
sampleRatenumberSampling rate (0.0-1.0)
useOtelTracebooleanUse OTEL trace context (default: true)
clientSeerClientClient instance

Fire-and-Forget vs Synchronous

Fire-and-Forget (Default)

Events are queued and sent asynchronously in the background. No await needed — just call log() and continue.

const client = new SeerClient();  // fireAndForget: true (default)

// Just call and continue - no await!
client.log({ task: "...", context: [...] });

// Events are batched and sent automatically
// Auto-flushes on process exit

Note: Auto-flush happens on normal exit. Call flush() explicitly before process.exit() or in worker threads.

Synchronous (Get Record ID)

If you need the record_id back, use synchronous mode. The log() call returns a Promise that resolves to the record ID.

const client = new SeerClient({ fireAndForget: false });

// Await to get the record_id
const recordId = await client.log({ task: "...", context: [...] });
console.log(`Created: ${recordId}`);
When to use which mode?
  • Fire-and-forget (99% of cases): For logging where you don't need the ID back. No performance impact on your application.
  • Synchronous: When you need to correlate the record_id with other systems or ensure the event was received before continuing.

OpenTelemetry Integration

The SDK automatically captures OTEL trace context when available:

import { trace } from '@opentelemetry/api';
import { SeerClient } from '@seer/sdk';

const tracer = trace.getTracer('my-app');
const client = new SeerClient();

await tracer.startActiveSpan('retrieval', async (span) => {
// trace_id, span_id, span_name are captured automatically
// No await needed in fire-and-forget mode
client.log({ task: "...", context: [...] });
span.end();
});

Manual Trace IDs

// No await needed in fire-and-forget mode
client.log({
task: "...",
context: [...],
trace_id: "0af7651916cd43dd8448eb211c80319c",
span_id: "b7ad6b7169203331",
span_name: "retrieval",
useOtelTrace: false, // disable auto-detection
});

Span Name Patterns

Span names are used for filtering in the UI:

PatternUI Color
retrieval, retrieval_hop_NBlue
rerank, rerankerPurple
llm_call, llmAmber
synthesis, answerEmerald

Ground Truth (Accuracy Testing)

Include expected results for accuracy measurement:

// No await needed in fire-and-forget mode
client.log({
task: "What is machine learning?",
context: [
{ text: "ML is a subset of AI...", id: "doc-ml-intro" },
{ text: "Neural networks learn from data...", id: "doc-nn-basics" },
],
ground_truth: {
// Document IDs that are relevant (matched against passage.id)
gold_doc_ids: ["doc-ml-intro", "doc-nn-basics"],
answer: "ML is a type of AI that learns from data", // optional
},
});

Convenience Function

For simple cases, use the global seerLog:

import { seerLog } from '@seer/sdk';

// No await needed - fire-and-forget by default
seerLog({
task: "Quick question",
context: [{ text: "Answer..." }],
});

Creates a global SeerClient on first call (fire-and-forget mode).


Error Handling

The SDK is designed to be non-blocking and fail gracefully:

  • Queue overflow: Events are dropped (logged to console)
  • Network errors: Retried with exponential backoff, then dropped
  • Invalid API key: Warning logged, event sent (server rejects)

Check client.stats() for failure counts.


TypeScript Types

The SDK exports all types for use in your application:

import type {
Passage,
GroundTruth,
LogEvent,
LogOptions,
ClientStats,
SeerClientOptions,
} from '@seer/sdk';

Examples

See the SDK examples directory for complete usage patterns:

  • Basic usage — All common patterns
  • OpenTelemetry integration — Distributed tracing
  • Express middleware — Web application integration