@bitfab/sdk. Dual ESM/CJS. Node.js ≥ 18 and modern browsers.
Module Exports
Constants
class Bitfab
new Bitfab(config: BitfabConfig)
withSpan
- If
enabled === false, returns the original function unchanged - Span
namedefaults tofn.name || traceFunctionKey - Span
typedefaults to"custom" - Input arguments are serialized via superjson (type-preserving)
- Return value (sync or the resolved Promise value) is serialized as output
SpanOptions.finalize?: (result) => unknown | Promise<unknown>records a serializable view of a non-serializable result (a live stream). The raw result is returned to the caller unchanged;await finalize(result)is recorded as the span output. Runs in the background; a throwingfinalizerecords an error instead of crashing. Ignored for async-generator results. Pair with the exportedfinalizers.aiSdk(Vercel AI SDK) orfinalizers.readableStream- Async-generator spans remain open until iteration completes, returns early, or throws. Nested spans created inside the generator body inherit that span. To include spans created by the consumer in the same trace, wrap the controller that owns the iteration in an outer root span.
- Thrown errors are recorded (
erroranderror_source: "code"fields) and re-thrown - Spans nest automatically via
AsyncLocalStorage(Node) or a module-level stack (browser fallback) - Browser fallback does not isolate concurrent async chains (
Promise.allwith independentwithSpancalls may see the wrong parent)
getFunction
BitfabFunction bound to traceFunctionKey.
wrapBAML
Framework integration → see BAML framework guide for examples.
WrappedBamlFn — an async function with a .collector property set after each call.
Throws:
BitfabErrorif form 1 is used withoutbamlClientin constructorBitfabErrorif the method has no.name
- If
@boundaryml/bamlis not installed, the method is called directly;.collectorisnull - Otherwise: creates a BAML
Collector, calls the method through a tracked client, then:- Calls
getCurrentSpan().setPrompt(...)with the rendered messages as JSON - Calls
getCurrentSpan().addContext({ model, provider, inputTokens, outputTokens, durationMs })
- Calls
onCollectorcallback fires after each invocation; errors in the callback are swallowed
getTrace
DetachedTrace handle for annotating a trace after its root span has closed, from any process or thread.
Throws: BitfabError if traceId is not a canonical Bitfab trace ID.
Semantics:
- All methods on the returned handle are fire-and-forget (return
Promise<unknown>) - When
enabled === false, methods returnPromise.resolve()immediately - Pending requests are tracked so
flushTraces()waits for them - Server returns 404 if no trace exists with that ID; failure is logged as a warning
getTraceSpan
traceId is the canonical Bitfab trace ID, and lookup is { id } using the span’s Bitfab ID or { name, occurrence? }; occurrence is "first" | "last" | number and defaults to "last". Numeric occurrences are zero-based in start-time order. Returns null when no trace or span matches.
replay
Returns:
{ items, testRunId, testRunUrl }. See ReplayResult.
Notes:
- A trace replays only when its root span has serializable inputs, or it was instrumented through a framework handler (whose recorded root input is serializable). If the original inputs were stubbed as non-serializable at capture time, the trace cannot be replayed.
fnmay be an already-withSpan-wrapped function (carries its trace function key, used as-is) or a plain callable (replay()wraps it under the key automatically); either way new spans link to the test run via async context. Don’t wrap an already-wrapped function in a fresh closure: the closure has no trace function key, soreplay()wraps the closure as the root and the inner wrapped function records a second span, nesting a duplicate.- Inputs are deserialized from historical spans and passed positionally
call
envVars.
Throws: BitfabError on lookup failure or execution error.
Framework Integrations
Handlers returned by these methods are framework-native adapters — they plug into each framework’s own callback/processor/hook surface and emit Bitfab spans automatically. For usage examples and semantics, see the per-framework guides; signatures here are canonical.getLangGraphCallbackHandler
config.callbacks when invoking a graph/chain. Root framework invocations are registered immediately as pending external traces and completed when the root callback ends. The handler-created root is replayable from the framework input, so a separate withSpan root is only needed for meaningful surrounding application work. See LangGraph framework guide.
Aliased as getLangChainCallbackHandler(traceFunctionKey) for plain LangChain projects; the returned handler and behavior are identical. The handler class is also exported as BitfabLangChainCallbackHandler.
getOpenAiTracingProcessor
@openai/agents’ addTraceProcessor (which keeps the SDK’s default OpenAI exporter; setTraceProcessors replaces it). Captures agent internals; pair it with getOpenAiAgentHandler for a replayable root. See OpenAI Agents framework guide.
getOpenAiAgentHandler
wrapRun(agent, input, options?) is a drop-in for @openai/agents’ run() that records a keyed, replayable root span carrying the run input (the tracing processor’s spans nest underneath). See OpenAI Agents framework guide.
getClaudeAgentHandler
instrumentOptions(options), wrapResponse(stream, opts?), and wrapQuery(stream, opts?) for the Claude Agent SDK. Pass { input: prompt } to the wrap call to record a replayable root span. See Claude Agent SDK framework guide.
getVercelAiMiddleware
wrapLanguageModel, then use the wrapped model with generateText / streamText / generateObject / streamObject. Every call is captured as a keyed llm span carrying the call parameters as input; streaming is captured without disturbing the live stream. See Vercel AI SDK framework guide.
wrapBAML
See the BAML framework guide for examples; full signature under wrapBAML above.
class BitfabFunction
Fluent wrapper binding a traceFunctionKey. Obtained via client.getFunction(key).
withSpan
client.withSpan(boundKey, optionsOrFn, maybeFn).
getVercelAiMiddleware
client.getVercelAiMiddleware(boundKey), reusing the bound key so a withSpan root and the middleware share it. See Nesting with core tracing.
getClaudeAgentHandler
client.getClaudeAgentHandler(boundKey), reusing the bound key so a withSpan root and the handler share it. See Nesting with core tracing.
getLangGraphCallbackHandler
client.getLangGraphCallbackHandler(boundKey), reusing the bound key so a withSpan root and the handler share it. See Nesting with core tracing.
getLangChainCallbackHandler
Alias of getLangGraphCallbackHandler: LangChain and LangGraph share one callback system.
wrapBAML
Identical signature and semantics to Bitfab#wrapBAML. Unlike the getXHandler() methods above, it does not use the bound key; it opens no span and enriches the current span, so call it inside a function wrapped by this handle’s withSpan.
class BitfabError
Extends Error.
withSpan paths — those are swallowed.
class BitfabLangGraphCallbackHandler
Duck-types LangChain’s callback handler interface without importing @langchain/core. Obtained via client.getLangGraphCallbackHandler(key). No direct instantiation needed for normal use. It records chain, LLM, tool, and retriever roots as pending traces on start, then completes them on root end. Full callback surface documented in LangGraph framework guide.
class BitfabOpenAITracingProcessor
Implements the OpenAI Agents SDK TracingProcessor interface. Obtained via client.getOpenAiTracingProcessor(). No direct instantiation needed for normal use. See OpenAI Agents framework guide.
class BitfabOpenAIAgentHandler
Run wrapper for the OpenAI Agents SDK. Obtained via client.getOpenAiAgentHandler(key). Exposes wrapRun(agent, input, options?), a drop-in for run() that records a keyed, replayable root span. See OpenAI Agents framework guide.
class BitfabClaudeAgentHandler
Handler for the Claude Agent SDK. Obtained via client.getClaudeAgentHandler(key). Exposes instrumentOptions, wrapResponse, and wrapQuery. See Claude Agent SDK framework guide for method signatures and usage.
Functions
getCurrentSpan()
traceId is "".
getCurrentTrace()
flushTraces(timeoutMs?: number)
timeoutMs: 5000. Use before process.exit() in short-lived scripts. Resolves after timeoutMs or when all pending sends finish, whichever comes first.
Interfaces
BitfabConfig
See constructor table.
SpanOptions
SpanType
CurrentSpan
CurrentTrace
DetachedTrace
client.getTrace(traceId), where traceId is the canonical Bitfab trace ID. Methods have the same semantics as CurrentTrace but send to the server immediately (fire-and-forget). When enabled === false, all methods return Promise.resolve().
WrapBAMLOptions
WrappedBamlFn<TArgs, TReturn>
collector is null before the first call or when @boundaryml/baml is unavailable. After each successful call, it holds the BAML Collector instance from that invocation.
ReplayOptions
onProgress fires once per item as it settles (completion order, not input order), so you can render live progress and consume the settled item’s input/output before the whole replay finishes. Replay doesn’t know pass/fail yet (verdicts are assigned later), so the totals only split ran-ok vs errored. A throwing callback never crashes the run. The SDK exports a ready-made reporter, reportReplayProgress, that you can pass straight in (onProgress: reportReplayProgress): it writes the running totals plus settled item payload to stderr, which the Bitfab plugin polls to report live progress and write per-item result files while the replay runs in the background, so scripts never hand-format the protocol.
ReplayEnvironment is read inside the replayed function: environment.databaseUrl, environment.expiresAt, environment.providerConsoleUrl, environment.readOnly, environment.traceId, and environment.active (false when no per-trace branch was resolved). Reading databaseUrl outside a replay item throws.
ReplayResult<T>
AllowedEnvVars
OPENAI_API_KEY is currently whitelisted.
ActiveSpanContext
Error Behavior Summary
Module Resolution
- Node.js ESM:
dist/index.js - Node.js CJS:
dist/index.cjs - Browser: works, but
AsyncLocalStorage-dependent features degrade (seewithSpansemantics)