@zespan/sdk
v1.5.1
Published
Agent observability for Node.js. Trace every AI call — latency, tokens, cost, errors — with one `init()`.
Readme
@zespan/sdk
Agent observability for Node.js. Trace every AI call — latency, tokens, cost, errors — with one init().
For full documentation visit docs.zespan.com
Install
npm install @zespan/sdk
# or
pnpm add @zespan/sdkQuick start
import { init } from "@zespan/sdk";
init({
apiKey: "zsp_your_key_here",
projectId: "your_project_id",
});Get your API key and project ID from app.zespan.com → Project Settings → API Keys.
After init(), all calls to OpenAI, Anthropic, and Google GenAI are automatically traced. No further changes needed.
Auto-instrumentation
By default, init() patches OpenAI, Anthropic, and Google GenAI as soon as they are imported. You do not need to wrap anything.
import { init } from "@zespan/sdk";
import OpenAI from "openai";
init({ apiKey: "zsp_...", projectId: "proj_..." });
const openai = new OpenAI();
// This call is automatically traced
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});To disable autopatch and wrap manually:
init({ apiKey: "zsp_...", autopatch: false });Manual wrappers
When you need explicit control, or when using providers beyond OpenAI/Anthropic/Google:
import { init, wrapOpenAI, wrapAnthropic, wrapGoogle } from "@zespan/sdk";
import OpenAI from "openai";
import Anthropic from "@anthropic-ai/sdk";
import { GoogleGenerativeAI } from "@google/generative-ai";
init({ apiKey: "zsp_...", autopatch: false });
const openai = wrapOpenAI(new OpenAI());
const anthropic = wrapAnthropic(new Anthropic());
const google = wrapGoogle(new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!));All available wrappers:
| Function | Provider |
| ------------------------ | -------------------- |
| wrapOpenAI(client) | OpenAI |
| wrapAnthropic(client) | Anthropic |
| wrapGoogle(client) | Google Generative AI |
| wrapOpenRouter(client) | OpenRouter |
| wrapBedrock(client) | AWS Bedrock |
| wrapMistral(client) | Mistral |
| wrapGroq(client) | Groq |
| wrapLiteLLM(client) | LiteLLM |
Configuration
init({
apiKey: "zsp_...", // required
projectId: "proj_...", // required — links traces to a project
environment: "production", // default: "production"
storePrompts: false, // store prompt/completion text (default: false)
redactKeys: ["password", "secret", "token", "api_key"], // keys to redact
sampleRate: 1.0, // 0.0–1.0, fraction of events to send
debug: false, // log SDK activity to console
batchSize: 50, // events per batch flush
flushInterval: 2000, // ms between batch flushes
autopatch: true, // auto-instrument OpenAI/Anthropic/Google
});User and session context
Tag traces with user ID, session ID, or custom metadata:
import { withZespanContext } from "@zespan/sdk";
await withZespanContext(
{ userId: "user_123", sessionId: "sess_abc", tags: { feature: "chat" } },
async () => {
// All AI calls inside here are tagged with this context
await openai.chat.completions.create({ ... });
}
);Agent tracing
Group multi-step agent workflows into a single trace:
import { withAgent } from "@zespan/sdk";
await withAgent(
{
name: "SupportAgent",
role: "specialist",
framework: "custom",
tools: [{ name: "lookup_order", description: "Look up order by ID" }],
},
async (agent) => {
agent.logPlan(["Look up order", "Check policy", "Draft reply"]);
const result = await agent.traceTool(
"lookup_order",
{ order_id: "123" },
async () => fetchOrder("123")
);
agent.delegateTo("RefundAgent", "refund requested");
// Wrapped LLM calls inside here inherit agent context
await openai.chat.completions.create({ ... });
}
);Manual spans
Trace custom operations that aren't LLM calls:
import { startSpan } from "@zespan/sdk";
const { span, run } = startSpan({
name: "document-retrieval",
span_kind: "retrieval",
});
await run(async () => {
const docs = await vectorStore.search(query);
await span.end({ status: "success" });
return docs;
});Framework integrations
LangChain
import { ZespanCallbackHandler } from "@zespan/sdk";
const handler = new ZespanCallbackHandler();
const chain = new LLMChain({ llm, prompt, callbacks: [handler] });Vercel AI SDK
import { instrumentVercelAI, getZespanVercelTelemetry } from "@zespan/sdk";
instrumentVercelAI();
// Or per-call:
const result = await generateText({
model: openai("gpt-4o"),
prompt: "Hello",
experimental_telemetry: getZespanVercelTelemetry({ traceId: "my-trace" }),
});Google ADK
import { instrumentADK } from "@zespan/sdk";
instrumentADK(); // patches all ADK agents globallyOr wrap a specific agent:
import { wrapADKAgent } from "@zespan/sdk";
const traced = wrapADKAgent(myAgent, { name: "MyAgent" });LlamaIndex
import { ZespanLlamaIndexHandler } from "@zespan/sdk";
const handler = new ZespanLlamaIndexHandler();
Settings.callbackManager.addEventHandler(handler);CrewAI / AutoGen / PydanticAI
These frameworks use OTEL-based instrumentation. See exports:
import {
CREWAI_OTEL_ENV,
getCrewAIInstrumentationGuide,
} from "@zespan/sdk";
import { injectAutoGenContext } from "@zespan/sdk";
import { getPydanticAIConfig } from "@zespan/sdk";Prompt management
Fetch versioned prompts from Zespan with 5-minute client-side cache:
import { getZespanClient } from "@zespan/sdk";
const client = getZespanClient();
// Latest version
const prompt = await client.prompts.get("support-reply");
// Specific version or label
const prompt = await client.prompts.get("support-reply", { version: 3 });
const prompt = await client.prompts.get("support-reply", {
label: "production",
});
// Compile variables
const text = client.prompts.compile(prompt, {
user_name: "Alice",
product: "Pro",
});Guardrails
Check content against your configured guardrail rules before or after LLM calls:
import { getZespanClient, GuardrailBlockedError } from "@zespan/sdk";
const client = getZespanClient();
try {
const check = await client.checkGuardrails({
text: userMessage,
phase: "pre",
});
if (!check.allowed) {
return "Request blocked by content policy.";
}
const response = await openai.chat.completions.create({ ... });
const postCheck = await client.checkGuardrails({
text: response.choices[0].message.content!,
phase: "post",
});
return postCheck.modifiedText ?? response.choices[0].message.content;
} catch (err) {
if (err instanceof GuardrailBlockedError) {
return "Response blocked by content policy.";
}
throw err;
}Guardrails can also be applied automatically via wrapper options:
const openai = wrapOpenAI(new OpenAI(), { guardrails: true });
// or fine-grained:
const openai = wrapOpenAI(new OpenAI(), {
guardrails: { pre: true, post: true, failClosed: false },
});Flush on exit
The SDK flushes automatically on process.beforeExit. For serverless functions or short-lived scripts, flush manually:
import { getZespanClient } from "@zespan/sdk";
await getZespanClient().flush();OpenTelemetry
Enable native OTEL export alongside Zespan:
init({
apiKey: "zsp_...",
enableOTel: true,
otelEndpoint: "http://localhost:4318",
otelServiceName: "my-service",
});TypeScript
The SDK is fully typed. All wrapper functions preserve the original client's type signature.
