Skip to main content
Bitfab integrates with LangGraph and LangChain via a callback handler that automatically captures graph node execution, LLM calls, tool invocations, and retriever queries as traced spans, with no manual withSpan or @span decorators needed on your graph nodes. Canonical signatures: TypeScript reference · Python reference

Supported Languages

Working with plain LangChain (no LangGraph)? The same handler serves both, and the SDK exposes it under a LangChain name too: getLangChainCallbackHandler() / get_langchain_callback_handler() and the BitfabLangChainCallbackHandler class are aliases of their LangGraph counterparts. Use whichever name matches your stack; the behavior is identical.

Quick Start

Plain LangChain Chains

The handler works the same way on LangChain chains and runnables; pass it in callbacks when invoking:

What Gets Captured

The callback handler hooks into LangChain’s callback system and creates spans automatically:

Trace Lifecycle

The first callback in a framework invocation becomes the trace root. For full LangGraph runs this is usually a chain/graph node; for plain LangChain usage it can also be a direct chat model, LLM, tool, or retriever call. The handler registers that root immediately as a pending external trace, then completes it when the root callback ends, so long-running invocations can appear as in progress before their final output is available. If the handler runs inside an active Bitfab withSpan / @span root, its spans attach to that outer trace instead. In that case the handler still records the framework span tree, but the outer Bitfab root owns final trace completion. You do not need an outer withSpan / @span root when the workflow is just the LangGraph or LangChain invocation. Add one only when there is meaningful application work around invoke() that should be part of the same trace, such as input preparation, retrieval outside LangChain, post-processing, persistence, or downstream service calls. When you do add an outer root, use the same trace function key for the outer span and the callback handler.

LangGraph Metadata

LangGraph-specific metadata is automatically extracted and stored as span context:
  • langgraph_step: Current step number
  • langgraph_node: Current node name
  • langgraph_triggers: What triggered this node
  • langgraph_path: Execution path
  • langgraph_checkpoint_ns: Checkpoint namespace

Token Usage

For LLM spans, token usage is captured from the LLM result. The handler prefers LangChain’s standardized usage_metadata on each generation’s message (which is also how usage arrives on the final aggregated chunk of streaming runs), then falls back to provider-native response_metadata shapes (OpenAI, Anthropic, Google Gemini / Vertex), and finally the legacy llm_output.token_usage location:
  • inputTokens: Prompt tokens. For Anthropic, cache reads and cache creation are added back so the value reflects the true prompt size.
  • outputTokens: Completion tokens
  • totalTokens: Total tokens
  • cachedInputTokens: Cached prompt tokens, from usage_metadata.input_token_details.cache_read, OpenAI prompt_tokens_details.cached_tokens, or Anthropic cache_read_input_tokens
  • model: Model name (extracted from serialized config or metadata)
When a result has multiple generations, usage is summed across them. Only provider-reported numbers are recorded: if the provider reports nothing (for example OpenAI streaming without stream_usage: true / stream_options: {"include_usage": true}), the fields are left unset rather than estimated.

TypeScript

Installation

Method Signature

Parameters:
  • traceFunctionKey (string, required): Groups all traces from this handler under one key in Bitfab
Returns: A BitfabLangGraphCallbackHandler that implements the LangChain callback handler interface (duck-typed, no @langchain/core dependency required).

Usage

Callback Hooks

LangChain invokes these callback hooks on the handler:

Nesting with Core Tracing

The handler integrates with Bitfab’s span stack. If the application has meaningful work around the graph or chain invocation, create a same-key withSpan wrapper around that outer workflow and the LangGraph spans nest as children. Handler-only instrumentation is enough for a plain LangGraph / LangChain call:
Use the same trace function key in both places. Both bitfab.getFunction(...) and bitfab.getLangGraphCallbackHandler(...) take a key; pass the same key ("my-pipeline" above) to both. If you use two different keys here, the same flow will register as two separate overlapping trace functions in the dashboard, an anti-pattern to avoid.

Error Handling

  • GraphBubbleUp: LangGraph’s internal interrupt mechanism. Detected automatically and completed silently (no error recorded).
  • All other errors: Error message captured in the span. The handler never throws; all callbacks are wrapped in try/catch.
  • Missing serialized callback data: Some framework versions can pass nullish serialized payloads. The handler still records the span using fallback names such as chain, llm, tool, or retriever.
  • Reusability: The handler resets after each root span completes and can be reused across multiple invocations.

Python

Installation

Method Signature

Parameters:
  • trace_function_key (str, required): Groups all traces from this handler under one key in Bitfab
Returns: A BitfabLangGraphCallbackHandler instance that extends BaseCallbackHandler from langchain-core.

Usage

Callback Methods

The handler implements these LangChain callback methods:

Nesting with Core Tracing

Bind the key once with get_function so the root and the handler share it (no repeated string to keep in sync). Use this only when the application has meaningful work around the graph or chain invocation; handler-only instrumentation is enough for a plain LangGraph / LangChain call:
Use the same trace function key for the root and the handler. Binding via get_function does this for you. The plain @bitfab.span("my-pipeline") / bitfab.get_langgraph_callback_handler("my-pipeline") forms work too, but you must pass the same key to both, otherwise the same flow registers as two separate overlapping trace functions in the dashboard, an anti-pattern to avoid.

Error Handling

  • GraphBubbleUp: LangGraph’s internal interrupt mechanism. Detected automatically and completed silently (no error recorded).
  • All other errors: repr(error) captured in the span. The handler never raises; all callbacks are wrapped in try/except.
  • Missing serialized callback data: Some framework versions can pass nullish serialized payloads. The handler still records the span using fallback names such as chain, llm, tool, or retriever.
  • Reusability: The handler resets after each root span completes and can be reused across multiple invocations.

Replay

Handler-instrumented graphs are fully replayable, even though no @span / withSpan root exists in the application code. The handler records the graph invocation as the root span, with the initial graph state as the recorded input. To replay, pass the handler’s key plus a plain callable that re-invokes the graph (the SDK wraps it internally):
The key is the only link between the handler-recorded production traces and the replay callable. Rebuild any runtime environment inside the callable (config["configurable"] values, dependency objects, API keys); the trace records only the graph state. Use no-op substitutes for side-effectful wiring (billing callbacks, notification senders). On SDKs that predate explicit-key replay, wrap the callable under the same key yourself (Python: @bitfab.span("weather-agent") with a (**state) signature; TypeScript: getFunction("weather-agent").withSpan(...)). Full details: Replaying handler-instrumented functions in the Python SDK and TypeScript SDK pages.