Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.bitfab.ai/llms.txt

Use this file to discover all available pages before exploring further.

Bitfab integrates with the Claude Agent SDK via a handler that automatically captures LLM turns, tool invocations, and subagent execution as traced spans. The handler instruments the SDK’s hook system and wraps the response stream to capture all execution data. Canonical signatures: TypeScript reference · Python reference

Supported Languages

LanguageMethodStatus
TypeScriptgetClaudeAgentHandler()✅ Supported
Pythonget_claude_agent_handler()✅ Supported
RubyNot yet supported
GoNot yet supported

Quick Start

import { Bitfab } from "bitfab"

const bitfab = new Bitfab({ apiKey: process.env.BITFAB_API_KEY })
const handler = bitfab.getClaudeAgentHandler("my-agent")

// Instrument the SDK options with Bitfab hooks
const options = handler.instrumentOptions({
  model: "claude-sonnet-4-5-20250514",
})

const client = new ClaudeSDKClient(options)
await client.connect()
await client.query("What's the weather?")

// Wrap the response stream to capture LLM turns
for await (const message of handler.wrapResponse(client.receiveResponse())) {
  // Process messages as normal
}

What Gets Captured

The handler captures three types of spans:
EventSpan TypeCaptured Data
LLM turnsllmFull conversation history, assistant response content, model name, token usage (input, output, cache read, cache creation)
Tool invocationsfunctionTool input, tool response or error
Subagent executionagentAgent type, start/stop lifecycle

Token Usage

For LLM spans, token usage is extracted from the message stream:
  • inputTokens — Input tokens
  • outputTokens — Output tokens
  • cacheReadTokens — Cache read tokens (if applicable)
  • cacheCreationTokens — Cache creation tokens (if applicable)
  • model — Model name

TypeScript

Installation

npm install bitfab

Method Signature

bitfab.getClaudeAgentHandler(traceFunctionKey: string): BitfabClaudeAgentHandler
Parameters:
  • traceFunctionKey (string, required) — Groups all traces from this handler under one key in Bitfab
Returns: A BitfabClaudeAgentHandler instance with methods for instrumenting options and wrapping streams.

Handler Methods

instrumentOptions(options)

Injects Bitfab hooks into the SDK options object. Mutates the options in-place and returns them.
const options = handler.instrumentOptions({
  model: "claude-sonnet-4-5-20250514",
  // your other options...
})
The injected hooks capture:
  • PreToolUse → Creates a function span with the tool input
  • PostToolUse → Completes the span with the tool response
  • PostToolUseFailure → Completes the span with an error
  • SubagentStart → Creates an agent span
  • SubagentStop → Completes the agent span

wrapResponse(stream)

Wraps the receiveResponse() async iterable to capture LLM turns from the message stream. Messages are yielded unchanged.
for await (const message of handler.wrapResponse(client.receiveResponse())) {
  // Messages pass through unchanged
  console.log(message)
}
Each LLM turn creates an llm span containing:
  • Input: Full conversation history snapshot up to this turn
  • Output: Assistant message content blocks
  • Context: Model name, token usage

wrapQuery(stream)

Simpler alternative for the query() API (no tool support):
for await (const message of handler.wrapQuery(client.query("Hello"))) {
  console.log(message)
}

Usage

import { Bitfab } from "bitfab"

const bitfab = new Bitfab({ apiKey: process.env.BITFAB_API_KEY })
const handler = bitfab.getClaudeAgentHandler("my-agent")

const options = handler.instrumentOptions({
  model: "claude-sonnet-4-5-20250514",
  tools: [/* your tools */],
})

const client = new ClaudeSDKClient(options)
await client.connect()
await client.query("Analyze the latest sales data")

for await (const message of handler.wrapResponse(client.receiveResponse())) {
  // Tool calls, subagent execution, and LLM turns
  // are all automatically traced
}

Nesting with Core Tracing

const pipeline = bitfab.getFunction("my-pipeline")

const tracedRun = pipeline.withSpan(
  { name: "RunClaudeAgent", type: "agent" },
  async (query: string) => {
    const handler = bitfab.getClaudeAgentHandler("my-pipeline")
    const options = handler.instrumentOptions({ model: "claude-sonnet-4-5-20250514" })
    const client = new ClaudeSDKClient(options)
    await client.connect()
    await client.query(query)

    const messages = []
    for await (const msg of handler.wrapResponse(client.receiveResponse())) {
      messages.push(msg)
    }
    return messages
  },
)

await tracedRun("What's the weather?")
// Claude Agent spans appear nested under the "RunClaudeAgent" span

Error Handling

  • All hook callbacks are wrapped in try/catch — errors are silently ignored and return an empty object.
  • Stream processing continues even if individual message capture fails.
  • The handler never throws or affects the SDK’s execution.

Python

Installation

pip install bitfab-py

Method Signature

bitfab.get_claude_agent_handler(trace_function_key: str) -> BitfabClaudeAgentHandler
Parameters:
  • trace_function_key (str, required) — Groups all traces from this handler under one key in Bitfab
Returns: A BitfabClaudeAgentHandler instance with methods for instrumenting options and wrapping streams.

Handler Methods

instrument_options(options)

Injects Bitfab hooks into the SDK options object. Returns the modified options.
options = handler.instrument_options(
    ClaudeAgentOptions(model="claude-sonnet-4-5-20250514")
)

wrap_response(stream)

Wraps the receive_response() async iterator to capture LLM turns:
async for message in handler.wrap_response(client.receive_response()):
    # Messages pass through unchanged
    print(message)

wrap_query(stream)

Simpler alternative for the query() API:
async for message in handler.wrap_query(client.query("Hello")):
    print(message)

Usage

import os
from bitfab import Bitfab

bitfab = Bitfab(api_key=os.environ["BITFAB_API_KEY"])
handler = bitfab.get_claude_agent_handler("my-agent")

options = handler.instrument_options(
    ClaudeAgentOptions(
        model="claude-sonnet-4-5-20250514",
        tools=[...],
    )
)

async with ClaudeSDKClient(options=options) as client:
    await client.query("Analyze the latest sales data")

    async for message in handler.wrap_response(client.receive_response()):
        # Tool calls, subagent execution, and LLM turns
        # are all automatically traced
        pass

Nesting with Core Tracing

@bitfab.span("my-pipeline", type="agent")
async def run_claude_agent(query: str):
    handler = bitfab.get_claude_agent_handler("my-pipeline")
    options = handler.instrument_options(
        ClaudeAgentOptions(model="claude-sonnet-4-5-20250514")
    )

    async with ClaudeSDKClient(options=options) as client:
        await client.query(query)
        async for message in handler.wrap_response(client.receive_response()):
            pass

await run_claude_agent("What's the weather?")
# Claude Agent spans appear nested under the "my-pipeline" span

Error Handling

  • All hook callbacks are wrapped in try/except — errors are logged at DEBUG level and return an empty dict.
  • Stream processing continues even if individual message capture fails.
  • The handler never raises or affects the SDK’s execution.