
Product
PHP and Composer Support Is Now in Beta
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.
@spanlens/sdk
Advanced tools
Spanlens SDK — agent tracing, LLM usage capture, and cost observability for TypeScript.
LLM observability SDK for Spanlens. Record agent traces, LLM calls, tool invocations, and retrievals — with a single line change.
Zero-instrumentation mode — just swap your baseURL to Spanlens proxy and you get request logging + cost tracking automatically. Use this SDK when you also want agent tracing (multi-step workflows, parallel fan-out, nested spans).
💡 Next.js user? Run
npx @spanlens/cli init— the wizard installs this SDK, writes your env var, and auto-rewritesnew OpenAI({...})intocreateOpenAI()for you (30 seconds).
npm install @spanlens/sdk
# or
pnpm add @spanlens/sdk
For the common case — just route your LLM calls through Spanlens for logging + cost tracking — use the pre-configured client helpers. No baseURL to remember:
// Before
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.SPANLENS_API_KEY,
baseURL: 'https://spanlens-server.vercel.app/proxy/openai/v1',
})
// After ⚡
import { createOpenAI } from '@spanlens/sdk/openai'
const openai = createOpenAI() // reads SPANLENS_API_KEY + baseURL automatically
All three providers supported:
import { createOpenAI } from '@spanlens/sdk/openai'
import { createAnthropic } from '@spanlens/sdk/anthropic'
import { createGemini } from '@spanlens/sdk/gemini'
const openai = createOpenAI()
const anthropic = createAnthropic()
const gemini = createGemini()
// gemini.getGenerativeModel() auto-routes through Spanlens proxy
The returned clients are identical to new OpenAI(...) etc — all options (timeout, headers, organization, etc.) forward through. Peer dependencies (openai, @anthropic-ai/sdk, @google/generative-ai) are optional — install only the ones you use.
Link a call to a specific Spanlens Prompts version so it shows up in the A/B metrics table:
import { createOpenAI, withPromptVersion } from '@spanlens/sdk/openai'
const openai = createOpenAI()
const res = await openai.chat.completions.create(
{ model: 'gpt-4o-mini', messages: [...] },
withPromptVersion('chatbot-system@3'), // or '@latest' / raw UUID
)
Same helper on @spanlens/sdk/anthropic. For observeOpenAI/Anthropic/Gemini, pass promptVersion in options.
For multi-step agent tracing (Gantt view, parent/child spans, RAG pipelines), continue to the Quick start below.
import { SpanlensClient, observe } from '@spanlens/sdk'
const client = new SpanlensClient({ apiKey: process.env.SPANLENS_API_KEY! })
const trace = client.startTrace({
name: 'support_chat',
metadata: { user_id: 'u_42', session_id: 'sess_abc' },
})
try {
// Manual span
const retrievalSpan = trace.span({ name: 'kb_search', spanType: 'retrieval' })
const docs = await vectorStore.query('...')
await retrievalSpan.end({ output: { doc_count: docs.length } })
// Auto-end via observe helper — handles errors, always closes the span
const answer = await observe(trace, { name: 'gpt4o_answer', spanType: 'llm' }, async (span) => {
const res = await openai.chat.completions.create({ ... })
span.end({
totalTokens: res.usage!.total_tokens,
costUsd: computeCost(res.usage!),
})
return res.choices[0].message.content
})
await trace.end({ status: 'completed' })
} catch (err) {
await trace.end({ status: 'error', errorMessage: String(err) })
throw err
}
new SpanlensClient(config)| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | required Spanlens API key (sl_live_...). |
baseUrl | string | https://spanlens-server.vercel.app | API base URL. |
timeoutMs | number | 3000 | Request timeout for ingest calls. |
silent | boolean | true | Swallow network errors so instrumentation never crashes user code. |
onError | (err, ctx) => void | — | Called on every ingest failure (even when silent). |
client.startTrace({ name, metadata? }) → TraceHandleStarts a new trace. Returns immediately — the backend ingest POST runs in the background.
TraceHandle.traceId: string — client-generated UUID..span(options) → SpanHandle — create a root span under this trace..end({ status?, errorMessage?, metadata? }) — mark trace complete (idempotent).SpanHandle.spanId: string.child(options) → SpanHandle — nested span (auto-sets parent_span_id)..end({ status?, output?, errorMessage?, promptTokens?, completionTokens?, totalTokens?, costUsd?, requestId?, metadata? }) — idempotent.spanType: 'llm' | 'tool' | 'retrieval' | 'embedding' | 'custom' (default 'custom').
observe(parent, options, fn)Wraps an async function in a span. Auto-ends the span on success or failure (rethrows the error).
const result = await observe(traceOrSpan, { name: 'work' }, async (span) => {
// span is open here
return doWork()
// span automatically closes — .end() is idempotent so you can still
// call span.end({ totalTokens, costUsd }) inside to capture metrics.
})
import OpenAI from 'openai'
import { SpanlensClient, observeOpenAI } from '@spanlens/sdk'
const spanlens = new SpanlensClient({ apiKey: process.env.SPANLENS_API_KEY! })
// Route OpenAI calls through the Spanlens proxy. The SDK injects
// x-trace-id/x-span-id headers so the proxy's request log is linked
// back to your spans.
const openai = new OpenAI({
apiKey: process.env.SPANLENS_API_KEY!,
baseURL: 'https://spanlens-server.vercel.app/proxy/openai/v1',
})
const trace = spanlens.startTrace({ name: 'support_chat' })
const res = await observeOpenAI(trace, 'answer', (headers) =>
openai.chat.completions.create(
{ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'Hi' }] },
{ headers },
),
)
await trace.end({ status: 'completed' })
import Anthropic from '@anthropic-ai/sdk'
import { SpanlensClient, observeAnthropic } from '@spanlens/sdk'
const spanlens = new SpanlensClient({ apiKey: process.env.SPANLENS_API_KEY! })
const anthropic = new Anthropic({
apiKey: process.env.SPANLENS_API_KEY!,
baseURL: 'https://spanlens-server.vercel.app/proxy/anthropic',
})
const trace = spanlens.startTrace({ name: 'agent_run' })
const res = await observeAnthropic(trace, 'reason', (headers) =>
anthropic.messages.create(
{ model: 'claude-haiku-4-5', max_tokens: 1024, messages: [...] },
{ headers },
),
)
await trace.end()
LangChain calls go through the underlying OpenAI/Anthropic client — point that
client at the Spanlens proxy and wrap the chain invocation in observe():
import { ChatOpenAI } from '@langchain/openai'
import { SpanlensClient, observe } from '@spanlens/sdk'
const spanlens = new SpanlensClient({ apiKey: process.env.SPANLENS_API_KEY! })
const llm = new ChatOpenAI({
apiKey: process.env.SPANLENS_API_KEY!,
configuration: {
baseURL: 'https://spanlens-server.vercel.app/proxy/openai/v1',
},
})
const trace = spanlens.startTrace({ name: 'langchain_qa' })
// LangChain's internal fetch won't carry our trace headers, so we group
// the whole chain under one span for dashboard visibility.
const answer = await observe(trace, { name: 'chain.invoke', spanType: 'llm' }, async () => {
return llm.invoke('What is Spanlens?')
})
await trace.end()
import { OpenAI, VectorStoreIndex, SimpleDirectoryReader } from 'llamaindex'
import { SpanlensClient, observe } from '@spanlens/sdk'
const spanlens = new SpanlensClient({ apiKey: process.env.SPANLENS_API_KEY! })
const llm = new OpenAI({
apiKey: process.env.SPANLENS_API_KEY!,
additionalSessionOptions: {
baseURL: 'https://spanlens-server.vercel.app/proxy/openai/v1',
},
})
const trace = spanlens.startTrace({ name: 'rag_query' })
const retrieval = await observe(trace, { name: 'retrieve', spanType: 'retrieval' }, async () => {
const docs = await new SimpleDirectoryReader().loadData({ directoryPath: './docs' })
return VectorStoreIndex.fromDocuments(docs)
})
const answer = await observe(trace, { name: 'generate', spanType: 'llm' }, async () => {
const engine = retrieval.asQueryEngine({ llm })
return engine.query({ query: 'What is Spanlens?' })
})
await trace.end()
startTrace() and trace.span() return synchronously. Network writes run in the background so your hot path never waits on observability.onError hook for visibility.observe() (or wrap via the proxy baseURL + manual span for tracing metadata).MIT
FAQs
Spanlens SDK — agent tracing, LLM usage capture, and cost observability for TypeScript.
The npm package @spanlens/sdk receives a total of 37 weekly downloads. As such, @spanlens/sdk popularity was classified as not popular.
We found that @spanlens/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket’s PHP and Composer support is now in Beta for all customers, with PHP reachability analysis generally available.

Product
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.