/docs/sdks/node
Node SDK
TypeScript SDK for tracing AI agents, model calls, tool calls, metadata, media, metrics, and trace reads.
Installation
npm install @theta-lab/obsrvConfiguration
API keys are project-scoped on the server. THETA_PROJECTis optional and only needed when you want explicit project filtering.
export THETA_API_KEY="tk_live_…"
export THETA_BASE_URL="https://api.obsrv.tech" # optional
export THETA_PROJECT="proj_…" # optionalTracing
await client.trace({ name: "checkout", runType: "prod" }, async (t) => {
t.setMetadata({ user_id: "u_a72c", release: "v3.2.1" });
await t.step({ name: "plan", type: "llm", model: "claude-sonnet-4-6" }, (s) => {
s.logMessage({ role: "user", text: "Buy milk" });
s.logMessage({ role: "assistant", text: "On it." });
s.setTokenUsage({ input: 900, output: 120 });
s.setCost(0.014);
});
await t.step({ name: "inventory.lookup", type: "tool" }, (s) => {
s.logToolCall({
name: "inventory.lookup",
arguments: { sku: "milk" },
result: { in_stock: true },
latencyMs: 83,
});
});
await t.attachImage("screenshot.png");
});
await client.flush();Agent wrapping
const agent = client.wrapAgent("support-agent", async (ctx, query: string) => {
const result = await callLlm(query);
ctx.onComplete(result);
return result;
});
const { result, runId } = await agent("Help me cancel my order");
await client.recordMetric("task_adherence", runId, { passed: true });Multimodal attachments
await t.attachImage("./screenshot.png");
await t.attachAudio(buffer, { filename: "audio.wav", mime: "audio/wav" });
await t.attachVideo("https://cdn.example.com/recording.mp4");
await t.attachFile("./report.pdf");
await t.attachSensor(frameBuffer, { modality: "depth-camera" });Provider integrations
import OpenAI from "openai";
import { wrapOpenAI } from "@theta-lab/obsrv/openai";
const oai = wrapOpenAI(new OpenAI(), { client });
await oai.chat.completions.create({ /* ... */ });Reading data back
const traces = await client.listTraces({
status: ["error"],
metadata: [{ key: "release", value: "v3.2.1" }],
limit: 50,
});
for (const trace of traces.data) {
console.log(trace.trace_id, trace.status, trace.latency_ms);
}
const detail = await client.getTrace("tr_…");
console.log(detail?.name, detail?.steps.length);Reference
new TraceClient({ apiKey, project?, baseUrl?, flushInterval?, maxBatch? })client.trace({ name, runType, userId, metadata }, async (t) => {...})client.wrapAgent(name, fn)t.step({ name, type, model, metadata }, async (s) => {...})s.logMessage({ role, text, images?, audio?, video?, attachments?, toolCalls? })s.logToolCall({ name, arguments, result?, error?, latencyMs? })s.setTokenUsage({ input, output, total? })t.setMetadata(object)andt.setMetadataPath("dot.path", value)client.recordMetric(name, traceId, { passed | score | label })client.listTraces(filters)andclient.getTrace(traceId)client.flush()andclient.shutdown()