@inference/tracing
v0.0.12
Published
First-party OpenInference-shaped tracing for TypeScript LLM and agent applications on Catalyst by Inference.net.
Downloads
1,062
Readme
@inference/tracing
First-party OpenInference-shaped tracing for TypeScript LLM and agent applications running on Catalyst by Inference.net.
@inference/tracing instruments common model SDKs, agent frameworks, and custom
agent workflows. It emits OpenTelemetry spans with OpenInference-compatible
attributes over OTLP/HTTP so Catalyst can display model calls, tool calls,
prompts, responses, token usage, and parent-child agent flows.
This package is currently in beta. APIs may change before 1.0, but the package
name and import paths are intended to remain stable.
Install
npm install @inference/tracingor:
bun add @inference/tracing
pnpm add @inference/tracing
yarn add @inference/tracingInstall the model or framework SDKs you use separately. They are optional peer dependencies so production installs stay narrow.
Quick Start
Set your Catalyst endpoint and token:
export CATALYST_OTLP_ENDPOINT="https://your-catalyst-otlp-endpoint"
export CATALYST_OTLP_TOKEN="your-token"
export CATALYST_SERVICE_NAME="checkout-agent"Initialize tracing before creating SDK clients:
import OpenAI from "openai";
import { setup } from "@inference/tracing";
const tracing = await setup({ modules: { openai: OpenAI } });
const client = new OpenAI();
await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Summarize this order." }],
});
await tracing.shutdown();The OpenAI call is captured as an OpenInference-shaped LLM span and exported to Catalyst through OTLP/HTTP.
What It Instruments
| Integration | Package | What is captured |
|---|---|---|
| OpenAI | openai | Chat Completions, Responses, sync flows, and streaming flows |
| Anthropic | @anthropic-ai/sdk | Messages API calls and streaming flows |
| LangChain | @langchain/core | Callback-manager driven chain, model, tool, and retriever spans |
| LangGraph | @langchain/langgraph | Graph and node spans through the LangChain callback path |
| LangSmith | langsmith | LangSmith OpenTelemetry spans bridged into the Catalyst provider |
| OpenAI Agents | @openai/agents | Agent runs plus nested OpenAI model spans |
| Claude Agent SDK | @anthropic-ai/claude-agent-sdk | Wrapped query() calls and yielded agent messages |
| ElevenLabs Agents | @elevenlabs/client | Conversation sessions, user/agent messages, and clientTools calls |
| Cursor SDK | @cursor/sdk | Cursor agent runs, streamed messages, wait results, and tool calls |
| Vercel AI SDK | ai | Native AI SDK OpenTelemetry spans for generateText, streamText, ToolLoopAgent, tool calls, structured output, and usage |
| LiveKit Agents | @livekit/agents | Native LiveKit OTel spans bridged into Catalyst and enriched as AGENT, LLM, TOOL, and CHAIN spans |
Public API
Most applications start with setup():
import { setup } from "@inference/tracing";
const tracing = await setup({
serviceName: "support-agent",
serviceVersion: "0.4.0",
});setup() returns a CatalystTracing handle with:
| Attribute | Purpose |
|---|---|
| provider | OpenTelemetry tracer provider configured for Catalyst export |
| tracer | Tracer for manual spans |
| installResults | Per-integration install results |
| shutdown() | Flush and close tracing before process exit |
You can also import integration helpers directly:
import OpenAI from "openai";
import { setup } from "@inference/tracing";
import { instrumentOpenAI } from "@inference/tracing/openai";
const tracing = await setup({ autoInstrument: false });
instrumentOpenAI(OpenAI, tracing);Available entry-point modules:
| Import | Export |
|---|---|
| @inference/tracing | setup, agentSpan, wrapClaudeAgentSdkQuery, errors, constants, and types |
| @inference/tracing/openai | instrumentOpenAI, installOpenAIInstrumentation |
| @inference/tracing/anthropic | instrumentAnthropic, installAnthropicInstrumentation |
| @inference/tracing/langchain | instrumentLangChain, installLangChainInstrumentation |
| @inference/tracing/langgraph | instrumentLangGraph, installLangChainInstrumentation |
| @inference/tracing/langsmith | installLangSmith |
| @inference/tracing/openai-agents | instrumentOpenAIAgents |
| @inference/tracing/claude-agent-sdk | wrapClaudeAgentSdkQuery |
| @inference/tracing/elevenlabs | instrumentElevenLabs, installElevenLabsInstrumentation |
| @inference/tracing/cursor-sdk | instrumentCursorSdk, installCursorSdkInstrumentation |
| @inference/tracing/ai-sdk | instrumentAISdk, createAISdkTelemetrySettings, installAISdkInstrumentation |
| @inference/tracing/livekit-agents | instrumentLiveKitAgents, installLiveKitAgentsInstrumentation |
| @inference/tracing/semconv | Attr, SpanKindValues |
Vercel AI SDK
The AI SDK emits its own OpenTelemetry spans when per-call telemetry is enabled. Use the helper to attach the Catalyst tracer while keeping the native AI SDK span names and attributes:
import * as ai from "ai";
import { generateText } from "ai";
import { setup } from "@inference/tracing";
import { createAISdkTelemetrySettings } from "@inference/tracing/ai-sdk";
const tracing = await setup({ modules: { aiSdk: ai } });
await generateText({
model,
prompt: "Summarize this ticket.",
experimental_telemetry: createAISdkTelemetrySettings(tracing.tracer, {
functionId: "support-summary",
}),
});
await tracing.shutdown();The same telemetry settings work for AI SDK agents:
import { ToolLoopAgent, stepCountIs, tool } from "ai";
import { z } from "zod";
const agent = new ToolLoopAgent({
model,
instructions: "Use tools when they are relevant.",
stopWhen: stepCountIs(2),
tools: {
weather: tool({
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, temperatureC: 21 }),
}),
},
experimental_telemetry: createAISdkTelemetrySettings(tracing.tracer, {
functionId: "support-agent",
}),
});
await agent.generate({ prompt: "What is the weather in Paris?" });
await tracing.shutdown();Manual Agent Spans
Use agentSpan() when work does not go through a supported SDK, such as a CLI
subprocess, custom router, planner, evaluator, or tool executor.
import { agentSpan, setup } from "@inference/tracing";
const tracing = await setup();
await agentSpan(
tracing.tracer,
{ name: "RefundReviewAgent", system: "internal" },
async (span) => {
span.setInput("Review refund request #1842");
const decision = await runRefundReview();
span.setOutput(decision.summary);
span.recordTokens({ prompt: 820, completion: 160 });
},
);
await tracing.shutdown();Any child spans created inside the callback automatically parent under the agent span through standard OpenTelemetry context propagation.
Configuration
You can configure tracing with options or environment variables.
| Option | Environment variable | Default |
|---|---|---|
| endpoint | CATALYST_OTLP_ENDPOINT | http://localhost:8799 |
| token | CATALYST_OTLP_TOKEN | unset |
| serviceName | CATALYST_SERVICE_NAME | generated catalyst-app-* name |
| serviceVersion | CATALYST_SERVICE_VERSION | 0.0.3 |
| debug | CATALYST_DEBUG | false |
| batching | none | "batch" |
Legacy OTLP_ENDPOINT, OTLP_INGEST_TOKEN, and SERVICE_NAME variables are
also accepted for compatibility.
Span Shape
Spans use OpenInference-style semantic attributes so LLM-aware viewers can understand them without custom adapters:
| Attribute family | Examples |
|---|---|
| Span kind | openinference.span.kind |
| Inputs and outputs | input.value, output.value |
| Messages | llm.input_messages.*, llm.output_messages.* |
| Model metadata | llm.model_name, llm.invocation_parameters |
| Token counts | llm.token_count.prompt, llm.token_count.completion, llm.token_count.total |
| Provider/system | gen_ai.system |
Constants are exported for custom spans:
import { Attr, SpanKindValues } from "@inference/tracing";
span.setAttribute(Attr.SPAN_KIND, SpanKindValues.LLM);
span.setAttribute(Attr.MODEL_NAME, "gpt-4o-mini");Error Handling
Integration helpers throw typed errors with stable code values when called
with wrong-shape arguments:
import {
CatalystTracingError,
InvalidSdkInputError,
InvalidTracerInputError,
instrumentOpenAI,
} from "@inference/tracing";
try {
instrumentOpenAI(maybeOpenAI, tracing);
} catch (err) {
if (err instanceof InvalidSdkInputError) {
console.error(err.code);
}
if (err instanceof InvalidTracerInputError) {
console.error(err.code);
}
if (err instanceof CatalystTracingError) {
throw err;
}
}Missing optional SDKs are not treated as errors. They are reported through install results so one package can support many integrations without forcing all SDKs into every application.
Runtime Notes
The package is ESM-only and publishes compiled JavaScript plus TypeScript declarations. It is intended for modern Node.js and Bun applications.
