Betav0.9.0
Go SDK
github.com/skyaiapp/skyaiapp-go · Go 1.21+ · Beta — interface may shift slightly before GA
Type-safe Go client: context.Context cancellation, HTTP/2 fast-path, built-in retry semantics, streaming and agents.
Installation
go get github.com/skyaiapp/skyaiapp-go@latestGo 1.21+. Zero runtime dependencies beyond stdlib + golang.org/x/*. OpenTelemetry tracing is opt-in.
Basic usage
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"time"
"github.com/skyaiapp/skyaiapp-go"
)
func main() {
// Singleton — reuse across goroutines. Internal http.Client + retry budget.
client, err := skyai.NewClient(
skyai.WithAPIKey(os.Getenv("SKYAIAPP_API_KEY")),
skyai.WithTimeout(60 * time.Second),
skyai.WithMaxRetries(2),
)
if err != nil {
log.Fatal(err)
}
// context — for cancellation, deadlines, request-scoped values.
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
res, err := client.Route(ctx, &skyai.RouteRequest{
Goal: skyai.GoalQuality, // "cost" / "quality" / "stability"
Strategy: skyai.StrategyQualityFirst,
Messages: []skyai.Message{
{Role: skyai.RoleSystem, Content: "You are a senior summarizer."},
{Role: skyai.RoleUser, Content: "Summarize: ..."},
},
Fallback: &skyai.Fallback{
Models: []string{"claude-opus-4.7", "gemini-3.1-pro"},
MaxRetries: 2,
},
Budget: &skyai.Budget{MaxCostUSD: 0.05},
Cache: &skyai.CacheOpts{Enabled: true, Similarity: 0.92, TTL: 24 * time.Hour},
Metadata: map[string]string{"tenant": "acme-corp", "workflow": "summary"},
TimeoutMS: 30_000,
})
if err != nil {
// Branch on typed errors instead of string-matching.
var rl *skyai.RateLimitError
if errors.As(err, &rl) {
time.Sleep(rl.RetryAfter)
return
}
var to *skyai.TimeoutError
if errors.As(err, &to) {
log.Println("timeout — falling back")
return
}
var re *skyai.RouterError
if errors.As(err, &re) {
log.Printf("router error: code=%s trace=%s", re.Code, re.TraceID)
}
log.Fatal(err)
}
fmt.Println("model:", res.Routing.SelectedModel)
fmt.Println("cost:", res.Routing.CostUSD)
fmt.Println("trace:", res.TraceID)
fmt.Println("output:", res.Output)
}Streaming
stream, err := client.Stream(ctx, &skyai.RouteRequest{
Goal: skyai.GoalQuality,
Messages: []skyai.Message{{Role: skyai.RoleUser, Content: "Write a story."}},
})
if err != nil {
log.Fatal(err)
}
defer stream.Close()
fmt.Println("Routing to:", stream.Routing.SelectedModel)
for chunk := range stream.Chan() {
switch chunk.Type {
case skyai.ChunkToken:
fmt.Print(chunk.Delta)
case skyai.ChunkToolCall:
fmt.Printf("\n[tool] %s %v\n", chunk.ToolName, chunk.Arguments)
case skyai.ChunkDone:
fmt.Printf("\nfinal cost: $%.6f\n", chunk.Routing.CostUSD)
}
}
if err := stream.Err(); err != nil {
log.Printf("stream error: %v", err)
}Context, timeouts, retries
Context is the standard way to propagate deadline / cancellation / metadata. The SDK forwards ctx to every downstream HTTP call.
// Pattern 1: hard deadline at the request boundary.
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
// Pattern 2: idempotency keys for safe automatic retry.
res, err := client.Route(ctx, req,
skyai.WithIdempotencyKey(fmt.Sprintf("req_%s_%d", userID, time.Now().UnixMilli())),
)
// Pattern 3: per-call retry override.
res, err := client.Route(ctx, req, skyai.WithMaxRetries(0)) // critical path: don't retry
// Pattern 4: extract trace ID for logging even on error.
if err != nil {
var re *skyai.RouterError
_ = errors.As(err, &re)
log.Printf("err code=%s trace=%s", re.Code, re.TraceID)
}Beta caveats
- Agent runtime API may shift before GA; for now, call REST directly.
- OpenTelemetry exporter works, but option params may evolve.
- File bugs on GitHub issues — smaller reproducers ship faster fixes.
See also
Was this page helpful?
Let us know how we can improve