README
¶
TruLayer AI — Go SDK
Status: Alpha. APIs are pre-
1.0.0and may change between minor releases. Pin a specific version in production until1.0.0ships.
Go SDK for instrumenting AI applications and sending traces to TruLayer AI.
- Documentation: https://docs.trulayer.ai/sdks/go/reference
- Source: https://github.com/trulayer/client-go
- Issues: https://github.com/trulayer/client-go/issues
Installation
go get github.com/trulayer/client-go
Requires Go 1.22+. Zero runtime external dependencies — stdlib only.
Quick Start
package main
import (
"context"
"os"
"github.com/trulayer/client-go/trulayer"
)
func main() {
ctx := context.Background()
tl := trulayer.NewClient(os.Getenv("TRULAYER_API_KEY"))
defer tl.Shutdown(ctx)
trace, ctx := tl.NewTrace(ctx, "answer-question")
trace.SetInput("What is the capital of France?")
span, ctx := trace.NewSpan(ctx, "llm-call", trulayer.SpanTypeLLM,
trulayer.WithSpanModel("gpt-4o-mini"),
)
// ... call your LLM here ...
span.SetOutput("Paris.")
span.SetTokens(12, 4)
span.End(ctx)
trace.SetOutput("Paris.")
trace.End(ctx)
}
Auto-instrumentation
Use an optional sub-module to wrap your OpenAI or Anthropic client. Every API call automatically records a TruLayer span — no manual NewSpan required.
OpenAI
go get github.com/trulayer/client-go/instruments/openai
import (
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
tlopenai "github.com/trulayer/client-go/instruments/openai"
"github.com/trulayer/client-go/trulayer"
)
tl := trulayer.NewClient(os.Getenv("TRULAYER_API_KEY"))
defer tl.Shutdown(ctx)
oai := openai.NewClient(option.WithAPIKey(os.Getenv("OPENAI_API_KEY")))
client := tlopenai.InstrumentOpenAI(oai, tl)
trace, ctx := tl.NewTrace(ctx, "answer-question")
defer trace.End(ctx)
// Every call below records a span automatically.
resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
Model: openai.ChatModelGPT4oMini,
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Why is the sky blue?"),
},
})
Anthropic
go get github.com/trulayer/client-go/instruments/anthropic
import (
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/option"
tlanthropic "github.com/trulayer/client-go/instruments/anthropic"
"github.com/trulayer/client-go/trulayer"
)
tl := trulayer.NewClient(os.Getenv("TRULAYER_API_KEY"))
defer tl.Shutdown(ctx)
ac := anthropic.NewClient(option.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")))
client := tlanthropic.InstrumentAnthropic(&ac, tl)
trace, ctx := tl.NewTrace(ctx, "answer-question")
defer trace.End(ctx)
// Every call below records a span automatically.
resp, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeHaiku4_5,
MaxTokens: 256,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Why is the sky blue?")),
},
})
Each instrument sub-module is independent — only install the provider you use. The core github.com/trulayer/client-go module retains zero external dependencies.
Manual Instrumentation
tl := trulayer.NewClient(os.Getenv("TRULAYER_API_KEY"))
defer tl.Shutdown(context.Background())
ctx := context.Background()
trace, ctx := tl.NewTrace(ctx, "rag-pipeline",
trulayer.WithTraceExternalID("req-42"), // link to your own request ID
)
// Retrieval span
retrieveSpan, ctx := trace.NewSpan(ctx, "retrieve", trulayer.SpanTypeRetrieval)
docs := retrieve(query)
retrieveSpan.End(ctx)
// LLM span
llmSpan, ctx := trace.NewSpan(ctx, "generate", trulayer.SpanTypeLLM,
trulayer.WithSpanMetadata(map[string]any{"model": "gpt-4o", "tokens": 512}),
)
result := llm.Complete(prompt)
llmSpan.SetOutput(result)
llmSpan.End(ctx)
trace.End(ctx)
Span Types
trulayer.SpanTypeLLM // "llm" — language model call
trulayer.SpanTypeTool // "tool" — tool / function call
trulayer.SpanTypeRetrieval // "retrieval" — vector search, document fetch
trulayer.SpanTypeOther // "other" — any other step
Feedback
err := tl.SubmitFeedback(ctx, traceID, trulayer.FeedbackData{
Label: "good",
Comment: "Correct answer",
})
Configuration
tl := trulayer.NewClient(
apiKey,
trulayer.WithBaseURL("https://api.trulayer.ai"), // default; override for staging
trulayer.WithBatchSize(50), // events per flush (default: 50)
trulayer.WithFlushInterval(2*time.Second), // time between flushes (default: 2s)
trulayer.WithHTTPClient(myHTTPClient), // custom http.Client
)
Failure behavior
The SDK is designed to never block or crash your application when the TruLayer API is unavailable.
Default behavior — drop + warn:
- Batches that fail to send are retried up to 3 times with exponential backoff.
- After retries exhaust, the batch is dropped and a
log.Printfwarning is emitted. - User goroutines are never blocked;
NewTraceandNewSpannever return errors.
Dry-run mode — TRULAYER_DRY_RUN=true:
Set this environment variable to no-op all HTTP calls without changing any application code. No API key is required. Used by CI and unit tests.
TRULAYER_DRY_RUN=true go run ./examples/basic_trace/
Shutdown
Always call Shutdown before your process exits to flush any buffered events:
tl := trulayer.NewClient(apiKey)
defer tl.Shutdown(context.Background())
Pass a context with a deadline to cap the drain time:
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
tl.Shutdown(shutdownCtx)
Features
- Zero runtime external dependencies (stdlib only)
- Async, channel-based batched ingestion — never blocks the request path
- Functional options for
NewClient, traces, and spans - Context-propagated parent/child span relationships
- UUIDv7 trace and span IDs (RFC 9562)
TRULAYER_DRY_RUN=truedisables all network calls (CI-safe)
Development
go build ./... # Compile all packages
go test -race -coverprofile=cover.out ./... # Tests with race detector (target: >90%)
go vet ./... # Static analysis
golangci-lint run # Lint
go mod tidy # Clean go.mod / go.sum
Links
License
MIT — see LICENSE.