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@latest

Go 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

Go SDK | SkyAIApp Docs — SkyAIApp