Generate evaluations and iterate on your AI applications
The Bitfab Go SDK captures your AI function calls to automatically generate evaluations. Re-run your prompts with different models, parameters, and inputs to iterate faster.
Framework-native adapters (LangGraph, OpenAI Agents, BAML, Claude Agent SDK) are not yet available for Go. See Frameworks overview for current coverage. Instrument Go code manually via client.Span or client.Start.
Copy this prompt into your coding agent (tested with Cursor and Claude Code using Sonnet 4.5):
Modify existing Go code to add Bitfab tracing.Do NOT browse or web search. Use ONLY the API described below.Bitfab Go SDK (authoritative excerpt):- Install: `go get github.com/Project-White-Rabbit/bitfab-go`- Init: import bitfab "github.com/Project-White-Rabbit/bitfab-go" client := bitfab.NewClient(os.Getenv("BITFAB_API_KEY"))- Start/End style (PREFERRED for existing functions): func myFunc(ctx context.Context, arg1 string) (Result, error) { ctx, span := client.Start(ctx, "<trace_function_key>", "<SpanName>", bitfab.WithType("function")) defer span.End() span.SetInput(arg1) result, err := doWork(ctx, arg1) if err != nil { span.SetError(err) return Result{}, err } span.SetOutput(result) return result, nil }- Closure style (for inline code): result, err := client.Span(ctx, "<trace_function_key>", func(ctx context.Context) (any, error) { return doWork(ctx), nil }, bitfab.WithName("<SpanName>"), bitfab.WithType("function"), bitfab.WithInput(args...))- Fluent API: fn := client.GetFunction("<trace_function_key>") ctx, span := fn.Start(ctx, "<SpanName>", bitfab.WithType("function")) defer span.End()- Span types: "llm", "agent", "function", "guardrail", "handoff", "custom"- Always pass ctx from Start or Span callback into nested calls for parent-child linking.- Always call client.FlushTraces(5 * time.Second) before program exit.Task:1) Ensure the bitfab-go module is added (`go get github.com/Project-White-Rabbit/bitfab-go`).2) Ensure a client is initialized with the API key.3) Read the codebase and identify ALL AI workflows (LLM calls, agent runs, AI-driven decisions).4) Present me with a numbered list of workflows you found. For each, describe: - What it does - Why it's worth instrumenting - what visibility tracing gives you into each step5) After I choose which workflow(s) to instrument: - Add Start/End at the top of each function (preferred) or wrap in Span closure - Use SetInput/SetOutput/SetError to capture data - Instrument intermediate steps (not just the final output) so each trace has enough context to diagnose issues - Pass `ctx` through for nested span support6) Do not change function signature, behavior, or return value. Minimal diff.Output:- First: your numbered list of workflows with why each is worth instrumenting- After my selection: minimal diffs for go.mod and the instrumented functions
// Default (production URL)client := bitfab.NewClient(apiKey)// Omit the key: NewClient reads BITFAB_API_KEY from the environmentclient := bitfab.NewClient("")// Set the key via an option instead of the positional argumentclient := bitfab.NewClient("", bitfab.WithAPIKey(apiKey))// Custom service URLclient := bitfab.NewClient(apiKey, bitfab.WithServiceURL("http://localhost:4000"))// Disable tracing (functions still execute, but no spans are sent)client := bitfab.NewClient(apiKey, bitfab.WithEnabled(false))
Missing API key doesn’t crash. If the API key is missing, empty, or whitespace-only, the SDK automatically disables tracing and logs a warning. All instrumented functions still execute normally - no spans are sent, no errors are thrown. You don’t need any conditional logic around the API key.
When no key is passed (empty argument and no WithAPIKey), the SDK reads BITFAB_API_KEY from the environment. Unlike the JS and Python SDKs, Go resolves the key eagerly in NewClient: clients are constructed explicitly (normally in main, after env has loaded), so there is no import-time construction-before-env trap to defer around. For standalone programs where a run that emits no traces should be a hard failure, use WithStrict:
// Panics in NewClient if no key resolves, instead of disabling quietlyclient := bitfab.NewClient(apiKey, bitfab.WithStrict(true))
For projects with instrumented functions spread across multiple files, create a dedicated package that initializes the client and exposes a function handle. Import it wherever you need to instrument.
// pkg/tracing/tracing.go - single source of truthpackage tracingimport ( "os" bitfab "github.com/Project-White-Rabbit/bitfab-go")var Client = bitfab.NewClient(os.Getenv("BITFAB_API_KEY"))var OrderService = Client.GetFunction("order-processing")
Use span.AddContext() on an ActiveSpan (Start/End style) to attach contextual key-value pairs at runtime - useful when context depends on computed values:
Use span.SetPrompt() on an ActiveSpan (Start/End style) to set the prompt string on the current span. This is stored in span_data.prompt and is useful for capturing the exact prompt text sent to an LLM:
The last SetPrompt call wins — it overwrites any previously set prompt on the span. Calling SetPrompt outside a span context is a no-op (it never crashes).
Use bitfab.GetCurrentTrace(ctx) to set context that applies to the entire trace (all spans within a single execution). This is useful for grouping traces by session or attaching trace-level metadata:
result, err := client.Span(ctx, "order-processing", func(ctx context.Context) (any, error) { trace := bitfab.GetCurrentTrace(ctx) // Set session ID (stored as database column, filterable in dashboard) trace.SetSessionID("session-123") // Set trace metadata (stored in raw trace data) trace.SetMetadata(map[string]any{"region": "us-west-2", "environment": "production"}) // Add context entries (stored as key-value pairs, accumulates across calls) trace.AddContext(map[string]any{"workflow": "checkout-flow", "batch_id": "batch-2024-01"}) return map[string]any{"status": "completed"}, nil}, bitfab.WithName("ProcessOrder"), bitfab.WithType("function"))
SetSessionID(id) - Groups traces by user session. Stored as a database column for efficient filtering.
SetMetadata(map) - Arbitrary key-value metadata on the trace. Merges with existing metadata.
AddContext(map) - Key-value context entries. Accumulates across multiple calls.
Use bitfab.GetCurrentTrace(ctx).Drop() to discard the in-flight trace. Once flagged, spans that complete afterward are not uploaded at all, and the flag rides out on the completion payload, so when the trace completes the server scrubs any payloads that already raced out (the trace, its external trace, and sibling spans), deletes the archived S3 objects, and marks it dropped instead of completed, keeping only a skeleton audit row. Use it to discard runs you never want stored (health checks, test traffic) or a run you know carries sensitive data.
Errors are captured in the span and returned to the caller:
result, err := client.Span(ctx, "risky-service", func(ctx context.Context) (any, error) { return nil, errors.New("something went wrong")}, bitfab.WithName("RiskyOperation"), bitfab.WithType("function"))// err contains "something went wrong"// The span records the error message and timing
Each error is classified by source. Errors returned by your function are recorded with error_source: "code". SDK-internal errors are recorded with source: "sdk". Both appear in the span’s errors array in the Bitfab dashboard.