Skip to main content
Bitfab integrates with BAML to automatically capture rendered prompts and LLM metadata, with no manual setPrompt or addContext calls. Under an opt-out trace root, BAML calls are recorded on their own (see below). With opt-in spans, wrap a BAML method with wrapBAML / wrap_baml and Bitfab extracts everything onto the current span. Canonical signatures: TypeScript wrapBAML · Python wrap_baml
Client capture off applies to BAML. With captureEnabled: false in TypeScript or capture_enabled=False in Python, wrapped BAML functions continue to run but send no span, and bitfab.call() sends no internal trace for its local execution. Replay items and seedTrace / seed_trace still record when explicitly invoked.

Under a trace root (opt-out tracing)

Beneath a withTrace() / trace() root, BAML calls are recorded without wrapBAML / wrap_baml. Each call on your generated BAML client (b.ClassifyText(...), sync or async) becomes its own llm span named after the client class and function, such as BamlAsyncClient.ClassifyText, carrying the same prompt, model, provider, token counts, and duration listed under What Gets Captured.
  • TypeScript needs the SDK’s build transform in the server build, the same requirement as the rest of opt-out tracing. The transform recognizes files BAML generated, records only the client’s function calls from them, and skips the rest of the generated code (withOptions, b.stream, parsers, request builders).
  • Python needs Python 3.12+, as opt-out tracing does. The generated client must already be imported when the root starts, which a module-level from baml_client import b guarantees. It is recorded whether or not baml_client sits inside the root’s package.
  • Your own collectors keep working. Bitfab adds its collector beside any you pass in the call options or set with b.withOptions({ collector }) / b.with_options(collector=...).
  • exclude matches the function name (ClassifyText). Excluding it runs the call without recording it. Replay mocking follows the root’s mockOnReplay / mock_on_replay default.
  • Existing wrapBAML / wrap_baml calls are safe. Under a root, the prompt and usage land on the BAML call’s span, never also on the function that called it.
  • withNode / node() on a BAML call configures that call’s one span. Pass the bound method or a wrapBAML / wrap_baml wrapper, and the node’s name, replay mocking, and other options apply to the BAML call’s span, which keeps its prompt and usage. Its type stays llm unless the node sets one. No second span is opened beneath it.
  • A node on your own function stays a separate span. When the node wraps a function of yours that calls BAML, the trace shows your function’s span with the BAML call’s llm span beneath it, and the prompt and usage appear only on the BAML span.
  • Outside a trace root, BAML calls run exactly as before and record nothing.
The streaming limitation below still applies: b.stream.* calls are not recorded as llm spans.

Supported Languages

Quick Start

What Gets Captured

wrapBAML / wrap_baml creates a BAML Collector, runs the method through a tracked client, then extracts: If @boundaryml/baml (TypeScript) or baml-py (Python) is not installed, the BAML method is called directly without instrumentation.

Limitations

  • Streaming functions are not auto-instrumented. wrapBAML / wrap_baml awaits a single result, so streaming calls (b.stream.ClassifyText, which return a stream rather than a final value) are not captured. Instrument the non-streaming form (b.ClassifyText), or capture the call manually with setPrompt / addContext inside a withSpan / @span root after draining the stream.
  • Functions that make more than one LLM call capture only one of them. When a BAML function issues multiple HTTP calls (retry policies, fallback clients, round-robin clients), the captured prompt, model, and provider come from the selected call (or the first one if none is marked selected). Token usage is taken from the Collector’s aggregate, so on a fallback chain the recorded model / provider may not correspond to the call that produced the tokens.

TypeScript

Installation

If your server code is bundled (Next.js with Turbopack, for example), add this import once in server code that loads before your BAML calls, such as the module that creates your Bitfab client:
Bundlers such as Turbopack rename external packages, so the SDK cannot find @boundaryml/baml by name at runtime. This import lets the bundler resolve it, and the SDK uses the module it loads. Without it, wrapBAML runs untraced and logs a one-time [bitfab] warning.

Method Signature

Parameters:
  • method (Function, required) - The BAML method to wrap (e.g., b.ClassifyText)
  • bamlClient (unknown, optional) - The BAML client instance. Required if not passed in the Bitfab constructor
  • options (WrapBAMLOptions, optional) - Configuration options
WrapBAMLOptions:
  • onCollector?: (collector: unknown) => void - Callback fired after each invocation with the BAML Collector instance
Returns: A WrappedBamlFn - an async function with the same signature as the original, plus a .collector property.

Usage

Explicit Client

Accessing the BAML Collector

The wrapped function exposes a .collector property containing the BAML Collector from the most recent call:
The .collector is null before the first call or if @boundaryml/baml is not installed.

onCollector Callback

For more control, pass an onCollector callback:
With the explicit client form, options go in the third argument:
If the callback throws, the error is silently caught and never crashes your application.

Error Handling

If @boundaryml/baml cannot be loaded (not installed, or bundled without import "@bitfab/sdk/baml"), wrapBAML calls the method directly on the BAML client without instrumentation, does not call onCollector, and logs a one-time [bitfab] warning. Metadata extraction errors are silently caught.

Python

Installation

Method Signature

Parameters:
  • method (Callable, required) - The BAML method to wrap (e.g., b.ClassifyText)
  • baml_client (Any, optional) - The BAML client instance. Required if not passed in the Bitfab constructor
  • on_collector (Callable, optional, keyword-only): Callback fired after each invocation with the BAML Collector instance
Returns: An async wrapper with the same signature as the original method, plus a .collector attribute holding the most recent call’s Collector (None before the first call or if baml-py is not installed).

Usage

Explicit Client

Accessing the BAML Collector

The wrapper exposes a .collector attribute with the BAML Collector from the most recent call, and accepts an on_collector callback fired after each invocation:
If the callback throws, the error is silently caught and never crashes your application.

Error Handling

If baml-py is not installed, wrap_baml falls back to calling the method directly on the BAML client without instrumentation. Metadata extraction errors are silently caught.