Skip to content

Latest commit

 

History

439 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Braintrust

Braintrust Go SDK

Go Reference Beta

Overview

This library provides tools for evaluating and tracing AI applications in Braintrust. Use it to:

  • Evaluate your AI models with custom test cases and scoring functions
  • Trace LLM calls and monitor AI application performance with OpenTelemetry
  • Integrate seamlessly with OpenAI, Anthropic, Google Gemini, Genkit, ADK, CloudWeGo Eino, LangChainGo, and other LLM providers

This SDK is currently in BETA status and APIs may change.

Installation

go get github.com/braintrustdata/braintrust-sdk-go

export BRAINTRUST_API_KEY="your-api-key"  # Get from https://www.braintrust.dev/app/settings

Each tracing integration is published as its own Go module. Install only the ones you need:

go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/openai       # OpenAI (openai-go)
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/anthropic     # Anthropic
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/genai         # Google GenAI
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/genkit        # Firebase Genkit
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/adk           # Google ADK
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/cloudwego/eino # CloudWeGo Eino
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/langchaingo   # LangChainGo
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/github.com/sashabaranov/go-openai # sashabaranov/go-openai
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/bedrockruntime # AWS Bedrock Runtime
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/a2a          # A2A protocol

Or install all integrations at once with the meta-module:

go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/all

Instrumentation

Trace LLM calls with automatic or manual instrumentation.

Automatic Instrumentation

Use Orchestrion to automatically inject tracing at compile time—no code changes required.

1. Install orchestrion:

go install github.com/DataDog/orchestrion@v1.12.1

2. Create orchestrion.tool.go in your project root:

//go:build tools

package main

import (
    _ "github.com/DataDog/orchestrion"
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/all" // Dedicated meta-module for all Braintrust LLM integrations
)

Or import only the integrations you need:

import (
    _ "github.com/DataDog/orchestrion"
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/openai"    // OpenAI (openai-go)
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/anthropic" // Anthropic
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/genai"     // Google GenAI
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/genkit"    // Firebase Genkit
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/adk"       // Google ADK
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/cloudwego/eino" // CloudWeGo Eino
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/langchaingo" // LangChainGo
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/github.com/sashabaranov/go-openai" // sashabaranov/go-openai
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/bedrockruntime" // AWS Bedrock Runtime
    _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/a2a" // A2A protocol
)

3. Build with orchestrion:

# Build with orchestrion
orchestrion go build ./...

# Or configure GOFLAGS to use orchestrion automatically
export GOFLAGS="-toolexec='orchestrion toolexec'"
go build ./...

4. Initialize OpenTelemetry and Braintrust in your application:

import (
    "context"
    "log"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/sdk/trace"
    "github.com/braintrustdata/braintrust-sdk-go"
)

func main() {
    ctx := context.Background()

    // Set up OpenTelemetry tracer
    tp := trace.NewTracerProvider()
    defer tp.Shutdown(ctx)
    otel.SetTracerProvider(tp)

    // Initialize Braintrust (registers the exporter)
    _, err := braintrust.New(tp, braintrust.WithProject("my-project"))
    if err != nil {
        log.Fatal(err)
    }

    // Your LLM calls are now automatically traced
}

That's it! Your LLM client calls are now automatically traced. No middleware or wrapper code needed in your application.

Manual Instrumentation

If you prefer explicit control, you can add tracing middleware manually to your LLM clients. See the Manual Instrumentation Guide for detailed examples with OpenAI, Anthropic, Google Gemini, and other providers.

Span customizers

Register ordered, synchronous export hooks with braintrust.WithSpanCustomizers. Each config.SpanCustomizer has an optional OnSpanExport hook; an omitted hook is a no-op. Hooks receive completed OpenTelemetry sdktrace.ReadOnlySpan export snapshots, not live spans or provider responses. Embed the snapshot and override the fields you want to export, for example:

package main

import (
    "context"
    "log"

    braintrust "github.com/braintrustdata/braintrust-sdk-go"
    "github.com/braintrustdata/braintrust-sdk-go/config"
    "go.opentelemetry.io/otel/attribute"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

type redactedSpan struct {
    sdktrace.ReadOnlySpan
    attrs []attribute.KeyValue
}

func (s redactedSpan) Attributes() []attribute.KeyValue { return s.attrs }

func redact(span sdktrace.ReadOnlySpan) (sdktrace.ReadOnlySpan, error) {
    attrs := make([]attribute.KeyValue, 0, len(span.Attributes()))
    for _, attr := range span.Attributes() {
        if attr.Key != "braintrust.input_json" && attr.Key != "braintrust.output_json" {
            attrs = append(attrs, attr)
        }
    }
    return redactedSpan{ReadOnlySpan: span, attrs: attrs}, nil
}

func main() {
    tp := sdktrace.NewTracerProvider()
    defer tp.Shutdown(context.Background())
    client, err := braintrust.New(tp,
        braintrust.WithSpanCustomizers(config.SpanCustomizer{OnSpanExport: redact}),
    )
    if err != nil {
        log.Fatal(err)
    }
    _, span := client.Tracer("example").Start(context.Background(), "redacted")
    span.SetAttributes(attribute.String("braintrust.input_json", `"private"`))
    span.End()
}
  • Repeated options append in execution order. Each hook receives its predecessor's result. Registration copies the list and hook functions; exporter construction takes another snapshot. Changes to caller-owned slices do not change existing exporters. Mutable state captured by callbacks is still your responsibility. There is no environment-variable registration.
  • Hooks apply to all spans reaching the Braintrust exporter, including manual and instrumented spans, after filtering, span-origin metadata, and attachment processing. Attachment uploads may therefore already have happened. Hooks run before OTLP serialization, including when authentication is resolved lazily or a custom exporter is supplied. Other span processors and application-visible values are unchanged. Separately enabled trace-console logging is not customized.
  • Return the original snapshot or a valid replacement. Replacements are not merged; retain all fields you want to export. Returning nil (including a typed nil) is an error, not a way to drop spans. Attribute removal removes it from this completed export, not from any data previously exported.
  • The trace ID, span ID, parent trace ID, and parent span ID must remain identical, including zero/invalid IDs for root spans. They are checked after each hook. Context flags and trace state are not protected IDs. Routing attributes such as braintrust.parent may be changed.
  • Errors, panics (recovered as errors), nil results, or changed IDs fail closed: the exporter logs and returns an error and sends none of that batch. It does not export originals as a fallback or mutate the caller's batch. This does not roll back arbitrary external side effects performed by hooks.
  • Hooks run synchronously on the export path, often on a background goroutine. Keep them fast, avoid blocking I/O, and make shared state concurrency-safe. Do not retain or asynchronously mutate the supplied or returned span. Submitting the batch to the exporter again runs hooks again; retries inside the OTLP transport reuse the already transformed payload. With no hooks, the existing export path is unchanged.

Run the complete redaction example with BRAINTRUST_API_KEY set:

go run ./examples/internal/span-customizers/main.go

The example prints a Braintrust link. The exported span's input is "[redacted]", its output is absent, and its redacted attribute is true.

Evaluations

Run evals with custom test cases and scoring functions.

Define and run

Define an eval once with its task and scorers, then run it against any dataset:

package main

import (
    "context"
    "log"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/sdk/trace"

    "github.com/braintrustdata/braintrust-sdk-go"
    "github.com/braintrustdata/braintrust-sdk-go/eval"
)

func main() {
    ctx := context.Background()

    // Set up OpenTelemetry tracer
    tp := trace.NewTracerProvider()
    defer tp.Shutdown(ctx)
    otel.SetTracerProvider(tp)

    // Initialize Braintrust
    client, err := braintrust.New(tp)
    if err != nil {
        log.Fatal(err)
    }

    // Create an eval
    e := braintrust.NewEval(client, &eval.Eval[string, string]{
        Name: "greeting-experiment",
        Task: eval.T(func(ctx context.Context, input string) (string, error) {
            return "Hello " + input, nil
        }),
        Scorers: []eval.Scorer[string, string]{
            eval.NewScorer("exact_match", func(ctx context.Context, r eval.TaskResult[string, string]) (eval.Scores, error) {
                score := 0.0
                if r.Expected == r.Output {
                    score = 1.0
                }
                return eval.S(score), nil
            }),
        },
    })

    // Run against a dataset
    _, err = e.Run(ctx, eval.RunOpts[string, string]{
        Dataset: eval.NewDataset([]eval.Case[string, string]{
            {Input: "World", Expected: "Hello World"},
            {Input: "Alice", Expected: "Hello Alice"},
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
}

Examples

Complete working examples are available in examples/:

Getting Started:

Evaluations:

  • evals - Evaluations with custom scorers
  • datasets - Run evals against downloaded datasets
  • dataset-api - Create datasets, use prompts, run evals
  • scorers - Custom scoring with online and code-based scorers

Alternative Providers & Libraries:

Advanced:

  • manual-llm-logging - Manually log LLM calls
  • attachments - Include images and files in traces
  • prompts - Render a prompt locally and call the model yourself, or invoke it server-side
  • distributed-tracing - W3C baggage propagation across services
  • otel - Add Braintrust to existing OpenTelemetry setup

Features

  • Evaluations - Systematic testing with custom scoring functions
  • Tracing - Automatic instrumentation for major LLM providers
  • Prompts - Load Braintrust prompts, render them locally, and call any LLM client
  • Datasets - Manage and version evaluation datasets
  • Experiments - Track versions and configurations
  • Observability - Monitor AI applications in production

Documentation

Contributing

See CONTRIBUTING.md for development setup and contribution guidelines.

License

Apache License 2.0. See LICENSE for details.

About

Braintrust Go SDK

Resources

Contributing

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages