Skip to content

Commit c43f9d9

Browse files
authored
docs and api tweaks (#5)
- small docs fixes - api tweaks (e.g. don't let `API()` return nil)
1 parent da94c7e commit c43f9d9

27 files changed

Lines changed: 422 additions & 293 deletions

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ jobs:
3232
version: v2.1.6
3333
- name: Lint, vet, tests, etc.
3434
run: make ci
35+
36+
ci-passed:
37+
name: ci-passed
38+
needs: build
39+
runs-on: ubuntu-latest
40+
steps:
41+
- name: Mark CI as passed
42+
run: echo "All CI jobs passed successfully"

README.md

Lines changed: 144 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,118 @@ This library provides tools for **evaluating** and **tracing** AI applications i
1414

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

17-
## Installation
17+
## Setup
1818

1919
```bash
2020
go get github.com/braintrustdata/braintrust-sdk-go
21+
22+
export BRAINTRUST_API_KEY="your-api-key"
2123
```
2224

23-
## Quick Start
25+
### Getting started
2426

25-
### Set up your API key
27+
Braintrust uses [OpenTelemetry](https://opentelemetry.io/) for distributed tracing. Every application needs:
2628

27-
```bash
28-
export BRAINTRUST_API_KEY="your-api-key"
29+
1. **TracerProvider**: Collects and exports traces from your application
30+
2. **API Key**: Authenticates your application with Braintrust. [Braintrust Settings](https://www.braintrust.dev/app/settings).
31+
3. **Braintrust Client**: Connects to Braintrust and registers your TracerProvider for automatic instrumentation
32+
33+
### Setup
34+
35+
```go
36+
package main
37+
38+
import (
39+
"context"
40+
"log"
41+
42+
"go.opentelemetry.io/otel"
43+
"go.opentelemetry.io/otel/sdk/trace"
44+
45+
"github.com/braintrustdata/braintrust-sdk-go"
46+
)
47+
48+
func main() {
49+
// Setup a global OTel Tracer Provider.
50+
tp := trace.NewTracerProvider()
51+
defer tp.Shutdown(context.Background())
52+
otel.SetTracerProvider(tp)
53+
54+
bt, err := braintrust.New(tp)
55+
if err != nil {
56+
log.Fatal(err)
57+
}
58+
_ = bt // Your client is ready for use
59+
}
60+
```
61+
62+
### API Usage
63+
64+
Use the API client to manage Braintrust resources like prompts, datasets, and projects:
65+
66+
```go
67+
package main
68+
69+
import (
70+
"context"
71+
"log"
72+
73+
"go.opentelemetry.io/otel/sdk/trace"
74+
75+
"github.com/braintrustdata/braintrust-sdk-go"
76+
functionsapi "github.com/braintrustdata/braintrust-sdk-go/api/functions"
77+
)
78+
79+
func main() {
80+
ctx := context.Background()
81+
82+
// Create tracer provider
83+
tp := trace.NewTracerProvider()
84+
defer tp.Shutdown(ctx)
85+
86+
// Initialize Braintrust
87+
client, err := braintrust.New(tp,
88+
braintrust.WithProject("my-project"),
89+
)
90+
if err != nil {
91+
log.Fatal(err)
92+
}
93+
94+
// Get API client
95+
api := client.API()
96+
97+
// Create a prompt
98+
prompt, err := api.Functions().Create(ctx, functionsapi.CreateParams{
99+
ProjectID: "your-project-id",
100+
Name: "My Prompt",
101+
Slug: "my-prompt",
102+
FunctionData: map[string]any{
103+
"type": "prompt",
104+
},
105+
PromptData: map[string]any{
106+
"prompt": map[string]any{
107+
"type": "chat",
108+
"messages": []map[string]any{
109+
{
110+
"role": "system",
111+
"content": "You are a helpful assistant.",
112+
},
113+
{
114+
"role": "user",
115+
"content": "{{input}}",
116+
},
117+
},
118+
},
119+
"options": map[string]any{
120+
"model": "gpt-4o-mini",
121+
},
122+
},
123+
})
124+
if err != nil {
125+
log.Fatal(err)
126+
}
127+
_ = prompt // Prompt is ready to use
128+
}
29129
```
30130

31131
### Evals
@@ -51,16 +151,13 @@ func main() {
51151
otel.SetTracerProvider(tp)
52152

53153
// Initialize Braintrust
54-
bt, err := braintrust.New(tp,
55-
braintrust.WithProject("my-project"),
56-
braintrust.WithBlockingLogin(true),
57-
)
154+
client, err := braintrust.New(tp)
58155
if err != nil {
59156
log.Fatal(err)
60157
}
61158

62-
// Create evaluator
63-
evaluator := braintrust.NewEvaluator[string, string](bt)
159+
// Create an evaluator with your task's input and output types.
160+
evaluator := braintrust.NewEvaluator[string, string](client)
64161

65162
// Run an evaluation
66163
_, err = evaluator.Run(context.Background(), eval.Opts[string, string]{
@@ -73,11 +170,12 @@ func main() {
73170
return "Hello " + input, nil
74171
}),
75172
Scorers: []eval.Scorer[string, string]{
76-
eval.NewScorer("exact_match", func(ctx context.Context, taskResult eval.TaskResult[string, string]) (eval.Scores, error) {
77-
if taskResult.Expected == taskResult.Output {
78-
return eval.S(1.0), nil
173+
eval.NewScorer("exact_match", func(ctx context.Context, r eval.TaskResult[string, string]) (eval.Scores, error) {
174+
score := 0.0
175+
if r.Expected == r.Output {
176+
score = 1.0
79177
}
80-
return eval.S(0.0), nil
178+
return eval.S(score), nil
81179
}),
82180
},
83181
})
@@ -112,9 +210,7 @@ func main() {
112210
otel.SetTracerProvider(tp)
113211

114212
// Initialize Braintrust
115-
_, err := braintrust.New(tp,
116-
braintrust.WithProject("my-project"),
117-
)
213+
_, err := braintrust.New(tp)
118214
if err != nil {
119215
log.Fatal(err)
120216
}
@@ -124,8 +220,16 @@ func main() {
124220
option.WithMiddleware(traceopenai.NewMiddleware()),
125221
)
126222

127-
// Your OpenAI API calls will now be automatically traced
128-
_ = client // Use the client for your API calls
223+
// Make API calls - they'll be automatically traced and logged to Braintrust
224+
_, err = client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
225+
Messages: []openai.ChatCompletionMessageParamUnion{
226+
openai.UserMessage("Hello!"),
227+
},
228+
Model: openai.ChatModelGPT4oMini,
229+
})
230+
if err != nil {
231+
log.Fatal(err)
232+
}
129233
}
130234
```
131235

@@ -166,8 +270,17 @@ func main() {
166270
option.WithMiddleware(traceanthropic.NewMiddleware()),
167271
)
168272

169-
// Your Anthropic API calls will now be automatically traced
170-
_ = client // Use the client for your API calls
273+
// Make API calls - they'll be automatically traced and logged to Braintrust
274+
_, err = client.Messages.New(context.Background(), anthropic.MessageNewParams{
275+
Model: anthropic.ModelClaude3_7SonnetLatest,
276+
Messages: []anthropic.MessageParam{
277+
anthropic.NewUserMessage(anthropic.NewTextBlock("Hello!")),
278+
},
279+
MaxTokens: 1024,
280+
})
281+
if err != nil {
282+
log.Fatal(err)
283+
}
171284
}
172285
```
173286

@@ -213,8 +326,15 @@ func main() {
213326
log.Fatal(err)
214327
}
215328

216-
// Your Gemini API calls will now be automatically traced
217-
_ = client // Use the client for your API calls
329+
// Make API calls - they'll be automatically traced and logged to Braintrust
330+
_, err = client.Models.GenerateContent(context.Background(),
331+
"gemini-1.5-flash",
332+
genai.Text("Hello!"),
333+
nil,
334+
)
335+
if err != nil {
336+
log.Fatal(err)
337+
}
218338
}
219339
```
220340

api/client.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
package api
33

44
import (
5-
"fmt"
6-
75
"github.com/braintrustdata/braintrust-sdk-go/api/datasets"
86
"github.com/braintrustdata/braintrust-sdk-go/api/experiments"
97
"github.com/braintrustdata/braintrust-sdk-go/api/functions"
@@ -43,11 +41,8 @@ func WithLogger(log logger.Logger) Option {
4341
}
4442

4543
// NewClient creates a new Braintrust API client with the given API key and options.
46-
func NewClient(apiKey string, opts ...Option) (*API, error) {
47-
if apiKey == "" {
48-
return nil, fmt.Errorf("apiKey is required")
49-
}
50-
44+
// The apiKey must be non-empty (validated at config level).
45+
func NewClient(apiKey string, opts ...Option) *API {
5146
options := &options{
5247
apiURL: "https://api.braintrust.dev", // default
5348
logger: nil,
@@ -57,14 +52,11 @@ func NewClient(apiKey string, opts ...Option) (*API, error) {
5752
opt(options)
5853
}
5954

60-
client, err := https.NewClient(apiKey, options.apiURL, options.logger)
61-
if err != nil {
62-
return nil, err
63-
}
55+
client := https.NewClient(apiKey, options.apiURL, options.logger)
6456

6557
return &API{
6658
client: client,
67-
}, nil
59+
}
6860
}
6961

7062
// Projects returns a client for project operations

client.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ type Client struct {
2323
tracerProvider *trace.TracerProvider
2424
}
2525

26-
// New creates a new Braintrust client with the provided TracerProvider.
26+
// New creates a new Braintrust client.
2727
//
28-
// The TracerProvider is required and should be managed by the caller.
29-
// The client will NOT shut down the provider - you must do this yourself.
28+
// It will add a Braintrust exporter to the given tracer provider..
3029
//
3130
// Configuration is loaded from environment variables first, then
3231
// explicit options are applied (options take precedence).
@@ -53,6 +52,11 @@ func New(tp *trace.TracerProvider, opts ...Option) (*Client, error) {
5352
opt(cfg)
5453
}
5554

55+
// Validate configuration before proceeding
56+
if err := cfg.IsValid(); err != nil {
57+
return nil, fmt.Errorf("invalid configuration: %w", err)
58+
}
59+
5660
// Setup default logger if none provided
5761
log := cfg.Logger
5862
if log == nil {
@@ -192,22 +196,22 @@ func (c *Client) Tracer(name string, opts ...oteltrace.TracerOption) oteltrace.T
192196
//
193197
// Example:
194198
//
195-
// client, _ := braintrust.New(tp, braintrust.WithProject("my-project"))
199+
// client, _ := braintrust.New(tp)
196200
//
197201
// // Create an evaluator for string → string evaluations
198202
// evaluator := braintrust.NewEvaluator[string, string](client)
199203
//
200204
// // Run multiple evaluations
201205
// result1, _ := evaluator.Run(ctx, eval.Opts[string, string]{
202206
// Experiment: "test-1",
203-
// Cases: cases1,
207+
// Dataset: dataset1,
204208
// Task: task1,
205209
// Scorers: scorers,
206210
// })
207211
//
208212
// result2, _ := evaluator.Run(ctx, eval.Opts[string, string]{
209213
// Experiment: "test-2",
210-
// Cases: cases2,
214+
// Dataset: dataset2,
211215
// Task: task2,
212216
// Scorers: scorers,
213217
// })
@@ -224,7 +228,7 @@ func NewEvaluator[I, R any](client *Client) *eval.Evaluator[I, R] {
224228
//
225229
// // Create a dataset
226230
// apiClient := client.API()
227-
// project, _ := apiClient.Projects().Register(ctx, "my-project")
231+
// project, _ := apiClient.Projects().Create(ctx, "my-project")
228232
// dataset, _ := apiClient.Datasets().Create(ctx, api.DatasetRequest{
229233
// ProjectID: project.ID,
230234
// Name: "my-dataset",
@@ -234,17 +238,11 @@ func (c *Client) API() *api.API {
234238
// Get endpoints from session (prefers logged-in info, falls back to config)
235239
endpoints := c.session.Endpoints()
236240

237-
client, err := api.NewClient(
241+
return api.NewClient(
238242
endpoints.APIKey,
239243
api.WithAPIURL(endpoints.APIURL),
240244
api.WithLogger(c.logger),
241245
)
242-
if err != nil {
243-
// Log error but return nil - this shouldn't happen with valid session
244-
c.logger.Error("failed to create API client", "error", err)
245-
return nil
246-
}
247-
return client
248246
}
249247

250248
// Permalink returns a URL to the span in the Braintrust UI.

0 commit comments

Comments
 (0)