llmkit

package module
v2.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: MIT Imports: 25 Imported by: 0

README

LLMKit

One Go API for Anthropic, OpenAI, Google, and 20+ other providers — including local models through Ollama and vLLM. Switch providers without rewriting your request.

Zero external dependencies. Stdlib only.

Also available for TypeScript, Python, Rust, Swift, and Java.

Go, TypeScript, Python, Rust, Swift, Java

Anthropic, OpenAI, Google, and 26 more providers

Install

go get github.com/aktagon/llmkit-go/v2

Quick Start

c := llmkit.New("anthropic", os.Getenv("ANTHROPIC_API_KEY"))
resp, err := c.Text.System("You are helpful").Prompt(ctx, "Hello")
fmt.Println(resp.Text)

c.Text, c.Image, c.Music, c.Video, c.Agent, and c.Upload are pointer fields on *Client, not method calls. Chain methods clone the prototype and return a fresh builder, so successive c.Text.System(...) calls each yield a new *Text.

See examples/ for runnable single-file demos (quickstart, agent, stream, upload, image-gen, image-gen-openai, middleware, vertex-imagen). The shapes shown below are exercised against mock HTTP servers by example_test.go, so the documented call shapes are guaranteed to match the public surface.

Providers

Provider Default Model Env Var
anthropic claude-sonnet-4-6 ANTHROPIC_API_KEY
openai gpt-4o-2024-08-06 OPENAI_API_KEY
google gemini-2.5-flash GOOGLE_API_KEY
grok grok-3-fast GROK_API_KEY
mistral mistral-large-latest MISTRAL_API_KEY
deepseek deepseek-chat DEEPSEEK_API_KEY
groq llama-3.3-70b-versatile GROQ_API_KEY
together meta-llama/Llama-3.3-70B-Instruct-Turbo TOGETHER_API_KEY
fireworks accounts/fireworks/models/llama-v3p3-70b-instruct FIREWORKS_API_KEY
perplexity sonar-pro PERPLEXITY_API_KEY
openrouter openai/gpt-4o OPENROUTER_API_KEY
qwen qwen-plus DASHSCOPE_API_KEY
zhipu glm-4-plus ZHIPU_API_KEY
moonshot moonshot-v1-8k MOONSHOT_API_KEY
doubao doubao-1.5-pro-32k-250115 ARK_API_KEY
ernie ernie-4.0-8k QIANFAN_API_KEY
ollama llama3.2 OLLAMA_API_KEY
cohere command-r-plus COHERE_API_KEY
ai21 jamba-1.5-large AI21_API_KEY
cerebras llama-3.3-70b CEREBRAS_API_KEY
sambanova Meta-Llama-3.3-70B-Instruct SAMBANOVA_API_KEY
yi yi-large YI_API_KEY
minimax MiniMax-Text-01 MINIMAX_API_KEY
lmstudio default LM_STUDIO_API_KEY
vllm default VLLM_API_KEY

30 providers, 4 API shapes (OpenAI-compatible, Anthropic Messages, Google Generative AI, AWS Bedrock Converse). Bedrock auth uses SigV4; other providers use API-key auth. Full provider list — including azure, bedrock, vertex, jan, and llamacpp — in providers/providers.go.

API

Text — one-shot prompt

One-shot request:

c := llmkit.New("anthropic", os.Getenv("ANTHROPIC_API_KEY"))
resp, err := c.Text.
    System("You are helpful").
    Temperature(0.7).
    Prompt(ctx, "What is 2+2?")

fmt.Println(resp.Text)               // "4"
fmt.Println(resp.Usage.Input)       // prompt tokens
fmt.Println(resp.Usage.Output)      // completion tokens
fmt.Println(resp.Usage.CacheRead)   // tokens served from cache (all caching modes)
fmt.Println(resp.Usage.CacheWrite)  // tokens written to cache (Anthropic explicit caching)
fmt.Println(resp.Usage.Reasoning)   // internal reasoning tokens (OpenAI o1/o3/o4, Gemini 2.5+ thinking)

Capability-scoped fields (CacheRead, CacheWrite, Reasoning) are zero when the provider doesn't report them separately.

Stream — chunks + trailing handle

Streaming with a trailing-handle iterator. Stream returns a *TextStream; range over Chunks() to consume deltas, then read Response() for the accumulated text + token counts:

stream := c.Text.System("Be brief").Stream(context.Background(), "Tell me a one-line joke")
for chunk, err := range stream.Chunks() {
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(chunk)
}
fmt.Println()
final := stream.Response()
fmt.Printf("input=%d output=%d finish_reason=%s\n",
	final.Usage.Input, final.Usage.Output, final.FinishReason)

Breaking the range loop cancels the producer goroutine cleanly.

Structured output

Pass a JSON schema to get typed responses:

resp, err := c.Text.
    Schema(`{"type":"object","properties":{"color":{"type":"string"}}}`).
    Prompt(ctx, "The sky is blue")
// resp.Text == `{"color":"blue"}`
Agent — tool loop

Multi-turn conversations with function calling. c.Agent is a stateful builder — repeated Prompt calls on the same *Agent accumulate conversation history. Any chain method (System, AddTool, Temperature, ...) returns a forked clone with empty state. agent.Reset() clears history without dropping the configured tools or other chain state.

agent := c.Agent.
    System("You are a calculator").
    AddTool(llmkit.Tool{
        Name:        "add",
        Description: "Add two numbers",
        Schema: map[string]any{"type": "object", "properties": map[string]any{
            "a": map[string]any{"type": "number"},
            "b": map[string]any{"type": "number"},
        }},
        Run: func(args map[string]any) (string, error) {
            return fmt.Sprintf("%g", args["a"].(float64)+args["b"].(float64)), nil
        },
    }).
    MaxToolIterations(5)

resp, err := agent.Prompt(ctx, "What is 2+3?")
Upload — Path or Bytes

Upload files to a provider. Path and Bytes are mutually exclusive on the same *Upload; Bytes requires Filename. The returned File plugs into *Text.File(id):

file, err := c.Upload.Path("document.pdf").Run(ctx)
if err != nil {
    return err
}
resp, err := c.Text.
    File(file.ID).
    Prompt(ctx, "Summarize this document")

In-memory variant:

file, err := c.Upload.
    Bytes(payload).
    Filename("greeting.txt").
    MimeType("text/plain").
    Run(ctx)
Image input (vision)

Attach an image to a text prompt with *Text.Image(mime, bytes); it is sent as the provider's native image block (works on Anthropic, OpenAI, Google, and Bedrock). Bytes-based, so no filesystem is required:

resp, err := c.Text.
    Image("image/png", screenshotBytes).
    Prompt(ctx, "Describe this screenshot in one sentence.")
Image — text-to-image and edit

Generate images from text, optionally conditioned on reference images for editing or composition. Use the typed-builder chain on c.Image:

c := llmkit.New(providers.Google, key)
resp, err := c.Image.Model("gemini-3.1-flash-image-preview").
    AspectRatio("16:9").ImageSize("2K").
    Generate(ctx, "A nano banana dish in a fancy restaurant")
os.WriteFile("out.png", resp.Images[0].Bytes, 0o644)

For editing or compositional generation, accumulate text and image parts on the chain — the on-wire ordering matches the call order:

resp, err := c.Image.Model("gemini-3.1-flash-image-preview").
    Text("Person:").Image("image/png", personBytes).
    Text("Outfit:").Image("image/png", outfitBytes).
    Generate(ctx, "Generate the person wearing the outfit.")

The trailing Generate(ctx, msg) argument is desugared into a final text Part appended to the chain — pass "" to omit it when every Part is already supplied.

Empty whitelists mean "no client-side check; pass through" — providers like OpenAI accept arbitrary sizes within documented bounds, so the SDK trusts the API boundary instead of carrying a stale list.

Provider Model Aspect ratios Sizes
Google Nano Banana 2 (Flash) 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, 1:4, 4:1, 1:8, 8:1 512, 1K, 2K, 4K
Google Nano Banana Pro 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 1K, 2K, 4K
OpenAI gpt-image-2 / 1.5 / 1 / 1-mini n/a (size only) any (e.g. 1024x1024, 1536x1024)
xAI grok-imagine-image-quality 1:1, 2:3, 3:2, 3:4, 4:3, 9:16, 16:9, 1:2, 2:1, 19.5:9, 9:19.5, 20:9, 9:20, auto 1k, 2k
Vertex imagen-3.0 / 4.0 1:1, 9:16, 16:9, 3:4, 4:3 fixed per model

OpenAI gpt-image-* models accept arbitrary sizes within documented bounds (max edge ≤3840, both edges multiples of 16, ratio ≤3:1, total pixels 655K–8.3M). They always return base64-encoded images, so resp.Images[0].Bytes works the same on both providers.

Provider knobs are typed chain methods on *Image:

Method Provider support Wire field
Quality(s) OpenAI gpt-image-* quality
OutputFormat(s) OpenAI gpt-image-* output_format
Background(s) OpenAI gpt-image-* background
Count(n) OpenAI + xAI Grok n
Mask(mime, bytes) OpenAI gpt-image-* (edits) multipart mask

The chain validates per provider — calling Quality(...) on a Google or xAI builder returns ValidationError immediately, without an HTTP round-trip. Provider knobs that don't yet have typed methods (OpenAI: output_compression, moderation) remain reachable via ExtraFields, which is unvalidated and freeform.

c := llmkit.New(providers.OpenAI, key)
resp, err := c.Image.Model("gpt-image-2").
    ImageSize("1024x1024").
    Quality("high").
    Count(4).
    Generate(ctx, "A red circle on a white background")

The dispatch is automatic: chains without image parts hit OpenAI's /v1/images/generations (JSON); chains carrying one or more Image(...) parts hit /v1/images/edits (multipart/form-data with one image[] field per reference, in caller order).

OpenAI gpt-image-* models require organization verification — see platform.openai.com/docs/guides/your-data#organization-verification.

Up to 14 reference images per Google request, 16 per OpenAI request. See examples/image-gen (Google) and examples/image-gen-openai (OpenAI) for end-to-end runnable samples.

Vertex AI Imagen (Google Cloud)

Vertex Imagen uses a different endpoint family (:predict) and OAuth auth instead of API keys. The SDK takes a bearer token (string); caller manages OAuth refresh externally (e.g. gcloud auth print-access-token, service-account JSON, or workload identity).

// Caller substitutes {project_id} and {location} before passing the URL.
const baseURL = "https://us-central1-aiplatform.googleapis.com" +
    "/v1/projects/my-gcp-project/locations/us-central1/publishers/google/models"

token := os.Getenv("VERTEX_BEARER_TOKEN") // e.g. `gcloud auth print-access-token`
c := llmkit.Vertex(token).BaseURL(baseURL)

resp, err := c.Image.Model("imagen-3.0-generate-002").
    AspectRatio("16:9").
    Count(2).
    Generate(ctx, "A red circle")

Edit-mode (single image into instances[0].image) and inpainting (Mask(mime, bytes) into instances[0].mask.image) work the same way. Imagen-specific knobs like negativePrompt and safetySetting are reachable through ExtraFields(...) — they spread into the request's parameters block. Vertex's :predict response does not carry token counts; resp.Usage stays zero.

Music — text-to-music

Generate audio from a text prompt. Use the typed-builder chain on c.Music; the trailing Generate(ctx, prompt) argument is the prompt text. Decoded audio bytes come back on resp.Audio[0].Bytes.

c := llmkit.Vertex(token).BaseURL(vertexBaseURL)
resp, err := c.Music.Model("lyria-002").
    Generate(ctx, "a calm, slow instrumental with warm piano and soft strings")
os.WriteFile("out.wav", resp.Audio[0].Bytes, 0o644)

Models that support vocals take lyrics via the .Lyrics(...) chain method (use section tags like [verse] / [chorus]):

c := llmkit.New(providers.Google, key)
resp, err := c.Music.Model("lyria-3-pro-preview").
    Lyrics("[verse] neon lights over the avenue").
    Generate(ctx, "dream pop, 90 bpm")

Instrumental-only models reject lyrics before the request is sent.

Provider Model(s) Lyrics Output
Vertex lyria-002 (Lyria 2) no WAV (~30s)
Google lyria-3-pro-preview, lyria-3-clip-preview yes MP3
MiniMax music-2.6 yes MP3

Vertex Lyria 2 uses the same OAuth bearer flow as Vertex Imagen above. See examples/music-gen for an end-to-end runnable sample.

Video — text-to-video

Generate video from a text prompt. Video generation is asynchronous: Submit returns a VideoHandle immediately; Wait polls until the job finishes. The result carries a temporary hosted URL on resp.Videos[0].URL — download it yourself (url delivery). The handle holds the request id and provider, so Wait works across process boundaries.

c := llmkit.Grok(key)
h, err := c.Video.Model("grok-imagine-video").
    Submit(ctx, "a slow cinematic drone shot over snow-capped alpine peaks")
resp, err := h.Wait(ctx)
v := resp.Videos[0]
fmt.Printf("url=%s duration=%ds mime=%s\n", v.URL, v.DurationSeconds, v.MimeType)
Provider Model Delivery
Grok grok-imagine-video URL

See examples/video-gen for an end-to-end runnable sample.

Safety Settings

Control content filtering for Gemini providers. SafetySettings applies to text generation, streaming, agents, and Gemini image generation. SafetyFilter applies to Vertex Imagen only.

import llmkit "github.com/aktagon/llmkit-go/v2"

// Gemini text or agent
resp, err := c.Text.
    SafetySettings([]llmkit.SafetySetting{
        {Category: llmkit.HarmCategoryDangerousContent, Threshold: llmkit.HarmBlockThresholdNone},
        {Category: llmkit.HarmCategoryHarassment, Threshold: llmkit.HarmBlockThresholdHighOnly},
    }).
    Prompt(ctx, "Write a story")

// Vertex Imagen
img, err := c.Image.Model("imagen-3.0-generate-002").
    SafetyFilter(llmkit.ImageSafetyFilterBlockFew).
    Generate(ctx, "A landscape")

SafetySettings on Vertex Imagen and SafetyFilter on non-Imagen providers return a *ValidationError. The HarmCategory*, HarmBlockThreshold*, and ImageSafetyFilter* constants cover all documented values; raw strings also work.

Model catalogue

c.Models and c.Providers cover model discovery in three modes. Runnable counterpart at examples/catalogue/main.go.

// 1. Compiled-in catalogue -- synchronous, no HTTP.
all := c.Models.List()
info, ok := c.Models.Get("claude-opus-4-7")                       // (ModelInfo, bool)
chat := c.Models.WithCapability(llmkit.CapChatCompletion).List()

// 2. Providers namespace.
c.Providers.List()       // configured (credentials + /v1/models endpoint)
providers.List()         // every provider the SDK ships with (static, keyless)

// 3. Live + scoped HTTP.
live, err := c.Models.Live(ctx)                                   // LiveResult -- fan-out
p := llmkit.Provider{Name: "anthropic", APIKey: "sk-..."}
scoped, err := c.Models.Provider(p).List(ctx)                     // single-provider list
raw, err := c.Models.Provider(p).Raw().List(ctx)                  // ModelInfo.Raw populated

Live(ctx) calls every configured provider's /v1/models in parallel and aggregates results into LiveResult.Models + a per-provider LiveResult.Errors map (partial success is the normal case). Provider(p).Raw().List(ctx) opts into populating ModelInfo.Raw with the provider-native record -- useful when you need fields the universal ModelInfo does not carry (Anthropic's capability matrix, Google's supportedGenerationMethods, etc.).

Options

Sampling and decoding knobs are typed chain methods on *Text and *Agent. They're all PascalCase and return a fresh builder:

c.Text.
    Temperature(0.7).
    TopP(0.9).
    TopK(40).
    MaxTokens(1000).
    StopSequences("END").
    Seed(42).
    FrequencyPenalty(0.5).
    PresencePenalty(0.5).
    ThinkingBudget(2000).
    ReasoningEffort("high").
    Prompt(ctx, "...")

*Agent exposes the same set plus MaxToolIterations(n). *Text exposes History(...Message) for multi-turn replay; *Agent retains history internally across Prompt calls instead.

Option anthropic openai google grok
temperature x x x x
top_p x x x x
top_k x x x
max_tokens x x x x
stop_sequences x x x x
seed x x x
frequency_penalty x x
presence_penalty x x
thinking_budget x x
reasoning_effort x x

Self-hosted endpoints

BaseURL retargets the API host — any OpenAI-compatible server (vLLM, LM Studio, Ollama, corporate gateways):

c := llmkit.OpenAI("anything").BaseURL("http://localhost:8080/v1")

Custom headers

AddHeader attaches a custom HTTP header to every request — for example an authenticated gateway that needs its own auth header alongside the provider key. AddHeader is chainable and calls accumulate.

c := llmkit.Anthropic(apiKey).
    BaseURL("https://gateway.example.com/anthropic").
    AddHeader("cf-aig-authorization", "Bearer "+gatewayToken)

The custom header is sent in addition to the provider's auth header; it cannot override the provider auth header or the required version header.

Middleware

Register pre/post hooks around LLM requests, tool calls, cache creation, uploads, and batch submits. Pre-phase middleware can veto an operation by returning a non-nil error; post-phase runs for observation only.

import (
    "context"
    "fmt"

    "github.com/aktagon/llmkit-go/v2"
    "github.com/aktagon/llmkit-go/v2/providers"
)

// Observation: log token usage after every LLM request.
func logUsage(ctx context.Context, e providers.Event) error {
    if e.Op == providers.OpLLMRequest && e.Phase == providers.PhasePost {
        fmt.Printf("%s/%s: %d in, %d out, took %s\n",
            e.Provider, e.Model,
            e.Usage.Input, e.Usage.Output, e.Duration)
    }
    return nil
}

// Veto: abort if a daily budget is exceeded (pre-phase).
func budgetGate(limit float64, spent *float64) providers.MiddlewareFn {
    return func(ctx context.Context, e providers.Event) error {
        if e.Op == providers.OpLLMRequest && e.Phase == providers.PhasePre && *spent >= limit {
            return fmt.Errorf("daily budget $%.2f exceeded", limit)
        }
        return nil
    }
}

c.Text.
    AddMiddleware(budgetGate(5.00, &spent), logUsage).
    Prompt(ctx, "Hello")

See examples/middleware/ for a spend-cap implementation with a price table and mutex-guarded accumulation. Middlewares fire in registration order; the first pre-phase non-nil error aborts.

Streaming uses the same middleware shape: one pre-phase before the request, one post-phase after the stream closes. Event.Usage reflects the accumulated usage at stream close. Per-chunk observation stays on the *TextStream.Chunks() range loop.

Telemetry

Opt-in OpenTelemetry. Attach a Telemetry and every call — success and rejection alike — produces one OTEL GenAI span (operation, provider, model, token usage, and error.type on failure) as standards-compliant OTLP/JSON bytes. llmkit builds the span; you decide where the bytes go. Off unless attached.

import "github.com/aktagon/llmkit-go/v2"

// Batteries: POST every span to an OTLP collector.
c := llmkit.New("openai", os.Getenv("OPENAI_API_KEY")).
    AddTelemetry(llmkit.Telemetry{
        Export: llmkit.HTTPExport("https://collector:4318", nil),
    })

// Or bring your own transport — hand the bytes to your OTEL SDK:
c.AddTelemetry(llmkit.Telemetry{
    Export: func(b []byte) { batchProcessor.Enqueue(b) },
})

resp, err := c.Text.Prompt(context.Background(), "Hello")

HTTPExport is a synchronous, fail-open POST — convenient for low volume; for high volume hand your own callback into your OTEL SDK's batch processor. The same OTLP span shape is emitted byte-for-byte across all six SDKs, so one collector serves a polyglot fleet. A Telemetry with no Export is a ValidationError.

CLI

# Install
go install github.com/aktagon/llmkit-go/v2/cmd/llmkit@latest

# Usage
llmkit -provider anthropic -system "You are helpful" -user "Hello"
llmkit -provider openai -stream -system "Count to 5" -user "Go"
llmkit -provider google -system "Extract color" -user "Sky is blue" \
  -schema '{"type":"object","properties":{"color":{"type":"string"}}}'

Wire-format stability

*Agent history persists across process boundaries through two paired functions:

data, _ := bot.Save()                 // []byte
// ...later, fresh process...
bot, err := c.Agent.System("...").Tool(t).Load(data)
if errors.Is(err, llmkit.ErrUnsupportedWireVersion) { /* upgrade prompt */ }

Or the free-function form for admin tooling:

data, _ := llmkit.SaveHistory(msgs)   // []byte
msgs, _ := llmkit.LoadHistory(data)   // []llmkit.Message

The output is a JSON document with a _v integer envelope plus a messages array. The version is tracked through llmkit.WireSchemaVersion; the same in-memory Message schema may evolve additively under one version (new optional fields work on older readers), but a renamed, removed, or retyped field requires a _v bump and a migrator.

SaveHistory / LoadHistory are the ONLY guaranteed-stable serialization path. Direct json.Marshal on a Message value produces valid JSON but lacks the _v envelope, and LoadHistory rejects it with ErrMissingWireVersion. Use the contract path for anything that crosses a process boundary or a release.

Mirror

This repo is a read-only mirror of a private source. File issues and feature requests here; patches should be submitted against the private source via christian@aktagon.com.

License

MIT

Documentation

Overview

Typed-builder API (ADR-009 / plan 016 / plan 018). Hoisted into package llmkit at the root once the legacy free-function layer was deleted (ADR-010).

Package llmkit is a unified LLM client library for Go.

One API across 27 providers — Anthropic (Claude), OpenAI (GPT), Google (Gemini), AWS Bedrock, Mistral, Groq, DeepSeek, and 20 more — with zero external dependencies (stdlib only).

Capabilities: text generation, streaming, batches, tool-calling agents, image generation, caching (automatic / explicit / resource), and middleware.

Quick start

c := llmkit.Anthropic(apiKey)
resp, err := c.Text.System("You are a helpful assistant.").
    Temperature(0.7).
    Prompt(ctx, "Hello!")

See https://llmkit.aktagon.com for the full provider matrix and guides.

Sister SDKs share the same API across languages: @aktagon/llmkit-ts on npm, llmkit on PyPI, llmkit on crates.io.

Index

Examples

Constants

View Source
const (
	HarmCategoryHarassment       = "HARM_CATEGORY_HARASSMENT"
	HarmCategoryHateSpeech       = "HARM_CATEGORY_HATE_SPEECH"
	HarmCategorySexuallyExplicit = "HARM_CATEGORY_SEXUALLY_EXPLICIT"
	HarmCategoryDangerousContent = "HARM_CATEGORY_DANGEROUS_CONTENT"
	HarmCategoryCivicIntegrity   = "HARM_CATEGORY_CIVIC_INTEGRITY"
)

Harm category constants for SafetySetting.Category.

View Source
const (
	HarmBlockThresholdNone           = "BLOCK_NONE"
	HarmBlockThresholdLowAndAbove    = "BLOCK_LOW_AND_ABOVE"
	HarmBlockThresholdMediumAndAbove = "BLOCK_MEDIUM_AND_ABOVE"
	HarmBlockThresholdHighOnly       = "BLOCK_ONLY_HIGH"
)

Harm block threshold constants for SafetySetting.Threshold.

View Source
const (
	ImageSafetyFilterBlockFew      = "block_few"
	ImageSafetyFilterBlockSome     = "block_some"
	ImageSafetyFilterBlockMost     = "block_most"
	ImageSafetyFilterBlockOnlyHigh = "block_only_high"
)

Vertex Imagen safety filter threshold constants for SafetyFilter.

View Source
const Responses = "responses"

Responses is the ADR-055 opt-in chat-protocol token for OpenAI's Responses API. Pass it to Text.Protocol to POST the {input} envelope to /v1/responses instead of the default Chat Completions {messages} envelope to /v1/chat/completions. It is a plain string; c.Text.Protocol("responses") is equivalent (per-SDK idiom note, ADR-055 — Go adds this ergonomic const).

View Source
const WireSchemaVersion uint32 = 1

WireSchemaVersion is the current generation of the on-disk wire format for serialized agent history (ADR-023 STAB-001).

Variables

View Source
var (
	ErrModelsNotSupported = errors.New("llmkit: provider does not expose a models endpoint")
	ErrModelsUnavailable  = errors.New("llmkit: provider models endpoint unavailable")
	ErrModelsScope        = errors.New("llmkit: api key lacks scope for models endpoint")
)

Catalogue error sentinels (ADR-019). Provider live calls map to:

  • ErrModelsNotSupported: provider lacks llm:hasModelsEndpoint (no /v1/models route; nothing to fetch). Also returned by Vertex and Bedrock until their dedicated parsers land.
  • ErrModelsScope: HTTP 403 whose body mentions scope (OpenAI's api.model.read scope is the canonical case).
  • ErrModelsUnavailable: any other non-2xx response or network failure during a live HTTP call.
View Source
var ErrMalformedWire = errors.New("llmkit: malformed wire document")

ErrMalformedWire is returned when LoadHistory parses a document that satisfies the version envelope but whose shape violates the wire schema (non-integer `_v`, non-array `messages`, non-object message entry, etc.). Symmetric with the Missing / Unsupported / UnknownKey sentinels so consumers can branch typed on every failure mode.

View Source
var ErrMissingWireVersion = errors.New("llmkit: wire document missing _v key")

ErrMissingWireVersion is returned by LoadHistory when the document has no top-level `_v` key. STAB-011: bare-array dumps (the ADR-020 bypass path) are rejected at the boundary to keep the contract path the only safe write source.

View Source
var ErrPollTimeout = errors.New("poll: deadline exceeded")

ErrPollTimeout is the sentinel a blocking Wait / waitBatch wraps when the deadline backstop fires (ADR-063 POLL-008). Test it with errors.Is:

if errors.Is(err, llmkit.ErrPollTimeout) { /* the job may still be running —
    persist the handle and poll it later, or raise WithPollTimeout */ }

It is reachable only from Wait, never from Poll: a single Poll is one round-trip and never times out. Provider-reported failures are NOT this error; branch on them via Poll's JobStatus.Cause.

View Source
var ErrUnknownWireKey = errors.New("llmkit: unknown top-level wire key")

ErrUnknownWireKey is returned when LoadHistory encounters a top-level key other than `_v`, `messages`, or `_meta` (the only keys the contract reserves).

View Source
var ErrUnsupportedWireVersion = errors.New("llmkit: unsupported wire schema version")

ErrUnsupportedWireVersion is returned by LoadHistory when the document's `_v` value is greater than the SDK's compiled-in WireSchemaVersion. Consumers MAY prompt the user to upgrade.

Functions

func HTTPExport

func HTTPExport(endpoint string, headers map[string]string) func([]byte)

HTTPExport returns an Export callback that POSTs each OTLP payload to endpoint + "/v1/traces" with a bounded timeout, fail-open (every network error is swallowed). It spawns no background worker and needs no Close.

Low-volume only: the POST is SYNCHRONOUS on the request path, so a slow or hung collector adds up to the client timeout of latency to the call. For high volume, hand your own Export callback that enqueues into your OTEL SDK's batch processor instead.

func SaveHistory

func SaveHistory(msgs []Message) ([]byte, error)

SaveHistory serializes a slice of public Message values into a versioned JSON document (ADR-023 STAB-002). The output carries a `_v` key (uint32 matching WireSchemaVersion) and a `messages` array. tool_calls is always emitted as a (possibly empty) array; tool_result is always emitted as either an object or JSON null — neither field is omitted (STAB-004).

Types

type APIError

type APIError struct {
	Provider   string
	StatusCode int
	Type       string
	Message    string
	Retryable  bool
	RetryAfter time.Duration
}

APIError represents a provider API error.

func (*APIError) Error

func (e *APIError) Error() string

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent accumulates configuration for a ToolCalling call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Agent) AddMiddleware

func (b *Agent) AddMiddleware(fns ...MiddlewareFn) *Agent

func (*Agent) AddTool

func (b *Agent) AddTool(t Tool) *Agent

func (*Agent) Caching

func (b *Agent) Caching() *Agent

func (*Agent) FrequencyPenalty

func (b *Agent) FrequencyPenalty(v float64) *Agent

func (*Agent) History

func (b *Agent) History(msgs ...Message) *Agent

func (*Agent) Load

func (b *Agent) Load(data []byte) (*Agent, error)

Load decodes a wire document and replaces the chain's history list, then zeroes the runtime state so the next Prompt rebuilds the legacy agent with the loaded history. Returns a typed error (ErrMissingWireVersion / ErrUnsupportedWireVersion / ErrUnknownWireKey) on a non-conforming document (ADR-023 STAB-012).

func (*Agent) MaxTokens

func (b *Agent) MaxTokens(n int) *Agent

func (*Agent) MaxToolIterations

func (b *Agent) MaxToolIterations(n int) *Agent

func (*Agent) Messages

func (b *Agent) Messages() []Message

Messages returns the accumulated conversation history as a fresh []Message slice (ADR-020 HIST-004). Empty when the builder has no runtime state — i.e. before the first Prompt call.

Both the outer slice and each Message.ToolCalls slice are fresh allocations; mutating them does NOT affect the agent's runtime state. The narrow aliasing risk is ToolCall.Input, which is a json.RawMessage carrying a reference to the JSON bytes from the internal map[string]any encoding — replacing it on a returned Message is safe, but in-place byte mutation would corrupt the agent. Treat the inner Input bytes as read-only per llmkit's user-misuse-not-library's-problem posture.

func (*Agent) Model

func (b *Agent) Model(name string) *Agent

func (*Agent) PresencePenalty

func (b *Agent) PresencePenalty(v float64) *Agent

func (*Agent) Prompt

func (b *Agent) Prompt(ctx context.Context, msg string) (Response, error)

Prompt sends a message through the underlying Agent and returns the response. State (history, tool calls, tool results) is retained between successive Prompt calls on the same *Agent. Forking via a chain method (e.g., bot.System("new")) produces a new clone with empty state.

func (*Agent) Raw

func (b *Agent) Raw() *Agent

func (*Agent) ReasoningEffort

func (b *Agent) ReasoningEffort(level string) *Agent

func (*Agent) Reset

func (b *Agent) Reset()

Reset wipes the conversation history. Chain config (system, tools, max-tokens, ...) is preserved — Reset on the typed builder does NOT throw away the configured tools, even though the underlying Agent.Reset clears tools too. We re-add them on the next Prompt automatically.

func (*Agent) SafetySettings

func (b *Agent) SafetySettings(s []SafetySetting) *Agent

func (*Agent) Save

func (b *Agent) Save() ([]byte, error)

Save serializes the agent's accumulated history into the canonical wire format (ADR-023 STAB-012). Sugar over SaveHistory(b.Messages()). Returns nil bytes + nil error when the builder has no runtime state, mirroring an empty conversation.

func (*Agent) Seed

func (b *Agent) Seed(n int64) *Agent

func (*Agent) StopSequences

func (b *Agent) StopSequences(seqs ...string) *Agent

func (*Agent) System

func (b *Agent) System(s string) *Agent

func (*Agent) Temperature

func (b *Agent) Temperature(t float64) *Agent

func (*Agent) ThinkingBudget

func (b *Agent) ThinkingBudget(n int) *Agent

func (*Agent) TopK

func (b *Agent) TopK(n int) *Agent

func (*Agent) TopP

func (b *Agent) TopP(v float64) *Agent

type AudioData

type AudioData struct {
	// MimeType is the IANA media type of the returned audio (audio/wav, audio/mpeg). Drives the file extension the caller picks for storage.
	MimeType string

	// Bytes is the raw (not encoded) decoded audio payload. The SDK decodes the provider wire format (base64 for Vertex/Gemini, hex for MiniMax) before returning so callers always see raw bytes.
	Bytes []byte
}

AudioData is one decoded audio payload returned in a MusicResponse. Same shape as MediaRef (mime type + raw bytes) but a distinct type so capability-specific return semantics stay typed: MusicResponse.audio carries decoded outputs; MediaRef appears in input payloads.

type BatchHandle

type BatchHandle struct {
	// ID is the provider-assigned batch identifier returned by the create endpoint. Opaque to the SDK; round-tripped to the polling and result endpoints verbatim.
	ID string

	// Provider is the Provider config used to submit the batch. Carried on the handle so Wait knows where to poll without re-parameterising the client.
	Provider Provider

	// Raw is the ADR-014 opt-in: when true, every Response returned from Wait carries Response.raw set to the parsed per-item provider body. Text.Batch propagates the chain's .raw() flag onto the handle; cross-process resume callers set the field directly.
	Raw bool
}

BatchHandle is a value struct identifying a submitted batch. Cross-process resume works by persisting the three fields and reconstructing the handle.

func (BatchHandle) Poll

func (h BatchHandle) Poll(ctx context.Context, opts ...Option) (JobStatus[[]Response], error)

Poll performs exactly ONE provider round-trip and returns the normalized JobStatus (ADR-063 POLL-001) — the enterprise seam for callers that drive the poll loop from their own orchestrator (Temporal, a queue, cron) instead of blocking on Wait. When the batch has completed, JobStatus.Result carries the ordered responses (the two-hop result fetch is performed inline); a provider-reported terminal failure (llm:pollingErrorValues) yields State JobFailed with the status on JobStatus.Cause; otherwise Result is nil and State is JobRunning. Honors h.Raw like Wait, and is safe to call on a reconstituted handle (ADR-014 cross-process resume; POLL-005).

func (BatchHandle) Wait

func (h BatchHandle) Wait(ctx context.Context, opts ...Option) ([]Response, error)

Wait polls the provider's batch lifecycle until completion and returns the ordered Response slice. Cross-process resume works by reconstructing a BatchHandle{ID, Provider, Raw} from persisted state and calling Wait on it.

ADR-014: when h.Raw is true, each returned Response carries Response.Raw set to the parsed per-item provider body.

type Capability

type Capability string

Capability names one of the SDK's modelled capabilities. The set mirrors llm:Capability instances in the ontology; ModelInfo.Capabilities is a slice of these. Ontology-derived per ADR-019 — never populated from provider wire data.

const (
	CapChatCompletion  Capability = "chat_completion"
	CapImageGeneration Capability = "image_generation"
	CapToolCalling     Capability = "tool_calling"
	CapFileUpload      Capability = "file_upload"
	CapBatching        Capability = "batching"
	CapCaching         Capability = "caching"
	CapReasoning       Capability = "reasoning"
	CapCatalogue       Capability = "catalogue"
)

type Client

type Client struct {
	Text          *Text
	Image         *Image
	Music         *Music
	Speech        *Speech
	Transcription *Transcription
	Video         *Video
	Agent         *Agent
	Upload        *Upload
	Models        *Models
	Providers     *Providers
	// contains filtered or unexported fields
}

Client is the entry point for the typed-builder API. Each sub-namespace field is a *<Capability> builder prototype tied to this client; chain methods return new instances, the field stays constant.

Example (Agent)

ExampleClient_agent walks the stateful agent path. The mock server returns a tool-free final response so the loop exits after one iteration. The chain composes System + Tool + MaxToolIterations.

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	// Return a plain text reply -- no tool_calls -- so the agent
	// loop terminates immediately.
	_ = json.NewEncoder(w).Encode(map[string]any{
		"choices": []map[string]any{
			{"message": map[string]any{"content": "The sum is 5"}},
		},
		"usage": map[string]any{"prompt_tokens": 10, "completion_tokens": 4},
	})
}))
defer server.Close()

c := New(providers.OpenAI, "sk-test")
c.provider.baseURL = server.URL

addTool := Tool{
	Name:        "add",
	Description: "Add two numbers",
	Schema: map[string]any{
		"type": "object",
		"properties": map[string]any{
			"a": map[string]any{"type": "number"},
			"b": map[string]any{"type": "number"},
		},
	},
	Run: func(args map[string]any) (string, error) {
		return fmt.Sprintf("%g", args["a"].(float64)+args["b"].(float64)), nil
	},
}

bot := c.Agent.
	System("You are a calculator").
	AddTool(addTool).
	MaxToolIterations(5)
resp, err := bot.Prompt(context.Background(), "What is 2+3?")
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(resp.Text)
Output:
The sum is 5
Example (Caching)

ExampleClient_caching walks the prompt-caching path against Anthropic's wire shape. The mock returns the cache-token split (cache_creation / cache_read) so resp.Usage.CacheWrite and CacheRead read back non-zero.

server := mockJSON(map[string]any{
	"content": []map[string]any{
		{"type": "text", "text": "cached!"},
	},
	"usage": map[string]any{
		"input_tokens":                12,
		"output_tokens":               5,
		"cache_creation_input_tokens": 100,
		"cache_read_input_tokens":     80,
	},
})
defer server.Close()

c := New(providers.Anthropic, "sk-test")
c.provider.baseURL = server.URL

resp, err := c.Text.
	System("You are helpful").
	Caching().
	Prompt(context.Background(), "Hi")
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(resp.Text)
fmt.Println("cache read:", resp.Usage.CacheRead)
fmt.Println("cache write:", resp.Usage.CacheWrite)
Output:
cached!
cache read: 80
cache write: 100
Example (Catalogue)

ExampleClient_catalogue walks the c.Models / c.Providers surface (ADR-019). Mirrors the chain in examples/catalogue/main.go — three modes: compiled-in (sync, no HTTP), providers namespace, and live / scoped / scoped-raw HTTP against /v1/models.

server := mockJSON(map[string]any{
	"data": []map[string]any{{
		"type":             "model",
		"id":               "claude-opus-4-7",
		"display_name":     "Claude Opus 4.7",
		"created_at":       "2026-04-14T00:00:00Z",
		"max_input_tokens": 1000000,
		"max_tokens":       128000,
	}},
	"has_more": false,
	"last_id":  "claude-opus-4-7",
})
defer server.Close()

c := New(providers.Anthropic, "sk-test")
c.provider.baseURL = server.URL
ctx := context.Background()

// Compiled-in catalogue.
fmt.Println("compiled-in non-empty:", len(c.Models.List()) > 0)
info, ok := c.Models.Get("claude-opus-4-7")
fmt.Println("claude-opus-4-7 context > 0:", ok && info.ContextWindow > 0)
fmt.Println("chat-capable non-empty:",
	len(c.Models.WithCapability(CapChatCompletion).List()) > 0)

// Providers namespace.
names := make([]string, 0, len(c.Providers.List()))
for _, p := range c.Providers.List() {
	names = append(names, p.Slug)
}
fmt.Println("configured:", names)
fmt.Println("supported >= 1:", len(providers.List()) > 0)

// Live + scoped HTTP.
p := Provider{Name: "anthropic", APIKey: "sk-test"}
live, err := c.Models.Live(ctx)
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println("live models:", len(live.Models))

scoped, err := c.Models.Provider(p).List(ctx)
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println("scoped list:", len(scoped))

rawScoped, err := c.Models.Provider(p).Raw().List(ctx)
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println("raw populated:", len(rawScoped) > 0 && rawScoped[0].Raw != nil)
Output:
compiled-in non-empty: true
claude-opus-4-7 context > 0: true
chat-capable non-empty: true
configured: [anthropic]
supported >= 1: true
live models: 1
scoped list: 1
raw populated: true
Example (Image)

ExampleClient_image walks the image-generation path against Google's Nano Banana wire shape. resp.Images[0].Bytes carries the decoded PNG; the mock returns a tiny fake byte sequence so the round-trip through base64 is observable.

fakePNG := []byte("\x89PNG\r\n\x1a\n<fake>")
encoded := base64.StdEncoding.EncodeToString(fakePNG)

server := mockJSON(map[string]any{
	"candidates": []map[string]any{{
		"content": map[string]any{
			"parts": []map[string]any{
				{"inlineData": map[string]any{
					"mimeType": "image/png",
					"data":     encoded,
				}},
			},
		},
	}},
	"usageMetadata": map[string]any{
		"promptTokenCount":     5,
		"candidatesTokenCount": 10,
	},
})
defer server.Close()

c := New(providers.Google, "k")
c.provider.baseURL = server.URL

resp, err := c.Image.
	Model("gemini-3.1-flash-image-preview").
	AspectRatio("16:9").
	ImageSize("2K").
	Generate(context.Background(), "A nano banana dish")
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(resp.Images[0].MimeType, len(resp.Images[0].Bytes))
Output:
image/png 14
Example (Middleware)

ExampleClient_middleware walks the text path with a registered middleware that counts pre/post phase fires. Mirrors the chain in examples/middleware/spend.go (which adds spend-cap accounting on top of the same observer shape).

server := mockJSON(map[string]any{
	"content": []map[string]any{
		{"type": "text", "text": "ok"},
	},
	"usage": map[string]any{"input_tokens": 7, "output_tokens": 1},
})
defer server.Close()

var preCalls, postCalls int
observer := func(ctx context.Context, e providers.Event) error {
	if e.Op != providers.OpLLMRequest {
		return nil
	}
	switch e.Phase {
	case providers.PhasePre:
		preCalls++
	case providers.PhasePost:
		postCalls++
	}
	return nil
}

c := New(providers.Anthropic, "sk-test")
c.provider.baseURL = server.URL

resp, err := c.Text.
	AddMiddleware(observer).
	Prompt(context.Background(), "What is 2+2?")
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(resp.Text)
fmt.Println("pre:", preCalls, "post:", postCalls)
fmt.Println("usage:", resp.Usage.Input, resp.Usage.Output)
Output:
ok
pre: 1 post: 1
usage: 7 1
Example (Reasoning)

ExampleClient_reasoning walks the reasoning-effort path against OpenAI's o-series wire shape. The mock returns completion_tokens_details. reasoning_tokens so resp.Usage.Reasoning reads back non-zero.

server := mockJSON(map[string]any{
	"choices": []map[string]any{
		{"message": map[string]any{"content": "There are 3 r's."}},
	},
	"usage": map[string]any{
		"prompt_tokens":     40,
		"completion_tokens": 25,
		"completion_tokens_details": map[string]any{
			"reasoning_tokens": 17,
		},
	},
})
defer server.Close()

c := New(providers.OpenAI, "sk-test")
c.provider.baseURL = server.URL

resp, err := c.Text.
	ReasoningEffort("high").
	Prompt(context.Background(), "How many r's are in strawberry?")
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(resp.Text)
fmt.Println("reasoning tokens:", resp.Usage.Reasoning)
Output:
There are 3 r's.
reasoning tokens: 17
Example (Stream)

ExampleClient_stream walks the streaming path. *TextStream carries chunks via Chunks() and exposes a trailing Response() after the range loop drains.

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/event-stream")
	w.WriteHeader(http.StatusOK)
	flusher := w.(http.Flusher)
	events := []string{
		"event: content_block_delta",
		`data: {"delta":{"text":"Hi"}}`,
		"",
		"event: content_block_delta",
		`data: {"delta":{"text":" there"}}`,
		"",
		"event: message_delta",
		`data: {"usage":{"output_tokens":3}}`,
		"",
		"event: message_stop",
		`data: {"type":"message_stop","stop_reason":"end_turn"}`,
	}
	for _, e := range events {
		fmt.Fprintln(w, e)
		flusher.Flush()
	}
}))
defer server.Close()

c := New(providers.Anthropic, "sk-test")
c.provider.baseURL = server.URL

stream := c.Text.System("Be brief").Stream(context.Background(), "Say hi")
for chunk, err := range stream.Chunks() {
	if err != nil {
		fmt.Println("err:", err)
		return
	}
	fmt.Print(chunk)
}
fmt.Println()
Output:
Hi there
Example (Text)

ExampleClient_text walks the one-shot text path. Mirrors the README "Prompt" section: c.Text.<chain>.Prompt(ctx, msg) returns a Response whose Text and Tokens fields carry the parsed reply.

server := mockJSON(map[string]any{
	"choices": []map[string]any{
		{"message": map[string]any{"content": "4"}},
	},
	"usage": map[string]any{"prompt_tokens": 7, "completion_tokens": 1},
})
defer server.Close()

c := New(providers.OpenAI, "sk-test")
c.provider.baseURL = server.URL

resp, err := c.Text.
	System("Be terse").
	Temperature(0.3).
	MaxTokens(50).
	Prompt(context.Background(), "What is 2+2?")
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(resp.Text)
fmt.Println(resp.Usage.Input, resp.Usage.Output)
Output:
4
7 1
Example (Upload)

ExampleClient_upload walks the file-upload path. Reads from a temp file so the example does not depend on repo layout.

server := mockJSON(map[string]any{
	"id":     "file-zzz",
	"object": "file",
})
defer server.Close()

c := New(providers.OpenAI, "sk-test")
c.provider.baseURL = server.URL

dir, err := os.MkdirTemp("", "llmkit-example-")
if err != nil {
	fmt.Println("err:", err)
	return
}
defer os.RemoveAll(dir)
path := filepath.Join(dir, "data.pdf")
if err := os.WriteFile(path, []byte("%PDF-1.4 stub"), 0o644); err != nil {
	fmt.Println("err:", err)
	return
}

file, err := c.Upload.Path(path).Run(context.Background())
if err != nil {
	fmt.Println("err:", err)
	return
}
fmt.Println(file.ID)
Output:
file-zzz

func Ai21

func Ai21(apiKey string) *Client

=== Per-provider constructors ===

func Anthropic

func Anthropic(apiKey string) *Client

func Assemblyai

func Assemblyai(apiKey string) *Client

func Azure

func Azure(apiKey string) *Client

func Bedrock

func Bedrock(apiKey string) *Client

func Cerebras

func Cerebras(apiKey string) *Client

func Cohere

func Cohere(apiKey string) *Client

func Deepseek

func Deepseek(apiKey string) *Client

func Doubao

func Doubao(apiKey string) *Client

func Ernie

func Ernie(apiKey string) *Client

func Fireworks

func Fireworks(apiKey string) *Client

func Google

func Google(apiKey string) *Client

func Grok

func Grok(apiKey string) *Client

func Groq

func Groq(apiKey string) *Client

func Inworld

func Inworld(apiKey string) *Client

func Jan

func Jan(apiKey string) *Client

func Llamacpp

func Llamacpp(apiKey string) *Client

func Lmstudio

func Lmstudio(apiKey string) *Client

func Minimax

func Minimax(apiKey string) *Client

func Mistral

func Mistral(apiKey string) *Client

func Moonshot

func Moonshot(apiKey string) *Client

func New

func New(p providers.ProviderName, apiKey string) *Client

New constructs a Client for the given provider (ADR-040: the typed providers.ProviderName identity). Per-provider helpers below are ergonomic shortcuts; a slug from config crosses in via providers.Parse.

func Ollama

func Ollama(apiKey string) *Client

func Openai

func Openai(apiKey string) *Client

func Openrouter

func Openrouter(apiKey string) *Client

func Perplexity

func Perplexity(apiKey string) *Client

func Pixverse

func Pixverse(apiKey string) *Client

func Qwen

func Qwen(apiKey string) *Client

func Recraft

func Recraft(apiKey string) *Client

func Sambanova

func Sambanova(apiKey string) *Client

func Together

func Together(apiKey string) *Client

func Vertex

func Vertex(apiKey string) *Client

func Vidu

func Vidu(apiKey string) *Client

func Vllm

func Vllm(apiKey string) *Client

func Workersai

func Workersai(apiKey string) *Client

func Yi

func Yi(apiKey string) *Client

func Zhipu

func Zhipu(apiKey string) *Client

func (*Client) AddHeader

func (c *Client) AddHeader(name, value string) *Client

AddHeader attaches a custom HTTP header to every request for this client; calls accumulate. Applied before the provider auth header, so a gateway header (e.g. cf-aig-authorization) rides alongside the provider key. Returns the same *Client for chaining.

func (*Client) AddTelemetry

func (c *Client) AddTelemetry(t Telemetry) *Client

AddTelemetry enables opt-in telemetry on this client. The builder rides the middleware seam, so every capability path that fires middleware emits one OTEL span on the post phase. A nil Export is fail-loud: the first call is vetoed with a ValidationError naming the field (Go defers construction-time validation to first use, the resolveModel idiom). Returns the same *Client for chaining.

func (*Client) BaseURL

func (c *Client) BaseURL(url string) *Client

BaseURL overrides the provider's default endpoint root for this client. Required for providers whose default base URL is a template the caller must substitute (e.g. Vertex AI Imagen) and to point an OpenAI-compatible provider or gateway at a self-hosted endpoint. Returns the same *Client for chaining.

func (*Client) Supports

func (c *Client) Supports(cap Capability) bool

Supports reports whether an explicit request for cap will not hard-fail pre-flight on this client's provider (ADR-030). Gated capabilities (caching, batching, file upload, image generation) dispatch the same generated lookups their strict validation paths use — never a parallel table — so the query and the error cannot drift. Capabilities with no provider-level pre-flight gate return true. Says nothing about per-model or per-option rejections — use the catalogue's ModelInfo.Capabilities for model-level facts. Sync, no IO, infallible.

type File

type File struct {
	// ID is the provider-assigned file identifier returned by the upload endpoint. Empty when the provider returns only a URI.
	ID string

	// URI is the provider-hosted URI of the uploaded file. Used in subsequent prompts to refer back to the file without re-uploading.
	URI string

	// MimeType is the IANA media type of the uploaded file as recorded by the provider (e.g., application/pdf, image/png). Carried so downstream prompts can route the file to the correct vision / document / audio path.
	MimeType string

	// Name is the original filename supplied at upload time. Round-tripped through the provider so the caller can correlate the handle with the source artifact.
	Name string
}

File is a reference to an uploaded file. Returned by UploadFile and attached to subsequent text-generation requests via Request.Files.

type Image

type Image struct {
	// contains filtered or unexported fields
}

Image accumulates configuration for a ImageGeneration call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Image) AddMiddleware

func (b *Image) AddMiddleware(fns ...MiddlewareFn) *Image

func (*Image) AspectRatio

func (b *Image) AspectRatio(r string) *Image

func (*Image) Background

func (b *Image) Background(s string) *Image

func (*Image) Count

func (b *Image) Count(n int) *Image

func (*Image) ExtraFields

func (b *Image) ExtraFields(extras map[string]any) *Image

ExtraFields stages caller-supplied keys for the wire body. Use it to reach provider knobs that don't yet have typed chain methods (OpenAI: quality, output_format, output_compression, background, n, moderation). Chain immutability is preserved — the input map is shallow-copied so callers can mutate their map after the call without affecting the builder.

func (*Image) Generate

func (b *Image) Generate(ctx context.Context, finalText string) (ImageResponse, error)

Generate executes the chained ImageGeneration request against the client's provider. Chain state populates ImageRequest and the matching ImageOption set; finalText, when non-empty, becomes a trailing text Part appended to the chain's accumulated Parts.

Phase 3 wiring: typed front door on GenerateImage. Per ADR-008, ImageRequest already speaks Parts natively, so the translation is just chain → ImageRequest{Model, Parts} + options.

func (*Image) Image

func (b *Image) Image(mime string, data []byte) *Image

func (*Image) ImageSize

func (b *Image) ImageSize(s string) *Image

func (*Image) IncludeText

func (b *Image) IncludeText() *Image

func (*Image) Mask

func (b *Image) Mask(mime string, data []byte) *Image

func (*Image) Model

func (b *Image) Model(name string) *Image

func (*Image) OutputFormat

func (b *Image) OutputFormat(s string) *Image

func (*Image) Quality

func (b *Image) Quality(s string) *Image

func (*Image) Raw

func (b *Image) Raw() *Image

func (*Image) SafetyFilter

func (b *Image) SafetyFilter(s string) *Image

func (*Image) SafetySettings

func (b *Image) SafetySettings(s []SafetySetting) *Image

func (*Image) Text

func (b *Image) Text(s string) *Image

type ImageData

type ImageData struct {
	// MimeType is the IANA media type of the returned image (image/png, image/jpeg, image/webp). Drives the file extension or data URI scheme the caller picks for storage.
	MimeType string

	// Bytes is the raw (not base64-encoded) decoded image payload. The SDK decodes provider wire format (base64, URL fetch) before returning so callers always see raw bytes.
	Bytes []byte
}

ImageData is one decoded image payload returned in an ImageResponse. Same shape as MediaRef (mime type + raw bytes) but a distinct type so capability-specific return semantics stay typed: ImageResponse.images carries decoded outputs; MediaRef appears in input payloads and edit masks.

type ImageOption

type ImageOption func(*imageOptions)

ImageOption configures GenerateImage.

func WithAspectRatio

func WithAspectRatio(ratio string) ImageOption

WithAspectRatio constrains the output aspect ratio (e.g., "16:9"). The value must appear in ImageGenConfig(provider).Models[].AspectRatios for the requested model, otherwise GenerateImage returns ValidationError.

func WithImageBackground

func WithImageBackground(s string) ImageOption

WithImageBackground sets the OpenAI gpt-image-* background treatment (transparent|opaque|auto). ValidationError on other providers.

func WithImageCount

func WithImageCount(n int) ImageOption

WithImageCount sets the number of images to generate (wire field `n`). Accepted by OpenAI gpt-image-* and xAI Grok; ValidationError on Google (where output count is bound to the model's per-aspect-ratio default).

func WithImageExtraFields

func WithImageExtraFields(extras map[string]any) ImageOption

WithImageExtraFields adds caller-supplied keys to the wire body (JSON for the generations branch; form fields for the edits branch). Reserved for provider-specific knobs that don't yet have typed chain methods (OpenAI: output_compression, moderation). Knobs covered by typed methods (quality, output_format, background, n) should use those — typed methods are validated per provider; ExtraFields is not.

func WithImageHTTPClient

func WithImageHTTPClient(c *http.Client) ImageOption

WithImageHTTPClient overrides the http.Client used for the GenerateImage call.

func WithImageMask

func WithImageMask(mime string, data []byte) ImageOption

WithImageMask attaches a PNG mask to the request (transparent pixels mark the region to edit). OpenAI gpt-image-* /v1/images/edits only — Google, xAI Grok, and the OpenAI generations branch (no image parts) all return ValidationError.

func WithImageMiddleware

func WithImageMiddleware(fns ...providers.MiddlewareFn) ImageOption

WithImageMiddleware registers pre/post hooks that fire around the image generation request. Op is providers.OpImageGeneration. Pre-phase can veto.

func WithImageOutputFormat

func WithImageOutputFormat(s string) ImageOption

WithImageOutputFormat sets the OpenAI gpt-image-* output MIME format (png|webp|jpeg). ValidationError on Google and xAI Grok.

func WithImageQuality

func WithImageQuality(s string) ImageOption

WithImageQuality sets the OpenAI gpt-image-* quality enum (low|medium|high|auto). ValidationError on Google and xAI Grok.

func WithImageSafetyFilter

func WithImageSafetyFilter(threshold string) ImageOption

WithImageSafetyFilter sets the global safety threshold for Vertex Imagen. Wire field: parameters.safetySetting. Use ImageSafetyFilter* constants or a raw string. ValidationError on all other image-gen providers.

func WithImageSafetySettings

func WithImageSafetySettings(s ...SafetySetting) ImageOption

WithImageSafetySettings sets per-category safety thresholds for Google image generation (the same safetySettings top-level field as text-gen). Wire field: safetySettings[]. Use SafetySetting{Category, Threshold} with the HarmCategory* / HarmBlockThreshold* constants. ValidationError on all non-Google image-gen providers (safetySettingsWirePath must be non-empty).

func WithImageSize

func WithImageSize(size string) ImageOption

WithImageSize sets the output resolution (e.g., "1K", "2K", "4K", "512"). Same per-model whitelist enforcement as WithAspectRatio.

func WithIncludeText

func WithIncludeText() ImageOption

WithIncludeText asks the model to also emit text parts (captions, refusals) alongside images. Defaults to off — most callers want pure image output.

type ImageRequest

type ImageRequest struct {
	Model  string
	Prompt string
	Parts  []Part
}

ImageRequest is the canonical image-generation request.

Model is required: image-generation models are explicit choices and the text-generation default (e.g., gemini-2.5-flash) does not generate images.

Input is provided in one of two mutually-exclusive forms:

  • Prompt: terse sugar for the text-only hot path. Internally desugars to Parts: []Part{Text(Prompt)} before serialisation.
  • Parts: canonical multimodal input. A positionally-ordered sequence of text and image parts; required for editing and compositional generation where caller-controlled ordering matters.

Pre-flight validation requires exactly one of Prompt or Parts to be non-empty (XOR). Image-typed parts respect ImageGenConfig.MaxInputCount.

type ImageResponse

type ImageResponse struct {
	// Images are the decoded image payloads (mime type + raw bytes) returned by the provider. Empty when the provider blocks or refuses the request — inspect FinishReason / FinishMessage for the cause.
	Images []ImageData

	// Text is the optional text response accompanying the images (captions, refusals, or model commentary). Populated only when the caller opted into mixed text + image output via the builder's IncludeText() chain method on providers that support it.
	Text string

	// Usage holds token consumption metrics for the image-generation call. Google reports image-output tokens in usageMetadata.candidatesTokenCount; OpenAI Images API and Vertex Imagen do not return token counts so this stays zero on those providers.
	Usage Usage

	// FinishReason is the provider stop signal. Examples per provider: Google STOP/IMAGE_OTHER/SAFETY/MAX_TOKENS; OpenAI Images API has no equivalent field (always empty); xAI Grok has no equivalent field (always empty); Vertex Imagen surfaces the RAI filter reason when content is blocked.
	FinishReason string

	// FinishMessage is the free-text provider explanation of the stop signal. Gemini populates this for non-success FinishReason values; other providers leave it empty. Use as the user-facing message when len(Images) == 0.
	FinishMessage string

	// Raw is the parsed provider response body, populated only when the caller opted in via the builder's .raw() chain method (ADR-014). Type-erased — consumers cast to a provider-shape type for fields the universal ImageResponse does not carry.
	Raw json.RawMessage
}

ImageResponse is the universal image-generation response container returned by Image.Generate. Carries the decoded images, optional text captions/refusals, usage, and the same finish-reason / finish-message / raw fields the text-gen Response carries.

type InputImage

type InputImage struct {
	URL      string // URL or base64 data URI
	MimeType string
	Detail   string // "auto", "low", "high" (provider-specific)
}

InputImage references an image attached to a text-generation request (vision input). The Text builder's Image(mime, bytes) part lowers into this carrier as a base64 data URI and reaches the wire as the provider's native image block (ADR-060). Distinct from Part's Image() constructor used for image-generation calls; unifying text-gen input onto Part vocabulary wholesale remains future work.

type JobFailure

type JobFailure struct {
	// Status is the raw provider status string that classified as failure
	// (OpenAI batch "failed"/"expired"/"cancelled"; AssemblyAI "error"). Empty
	// when the failure is the engine's deadline backstop firing.
	Status string
	// Message is the provider error message when the provider reports one
	// (AssemblyAI's top-level "error"); empty otherwise.
	Message string
	// TimedOut is true iff this failure is the engine's deadline backstop, not a
	// provider-reported terminal.
	TimedOut bool
}

JobFailure is the normalized failure detail carried by a JobFailed status. It is ONE terminal, not a taxonomy (ADR-062 §"Implementation refinements" 1): the raw provider status, an optional provider error message, and a timedOut flag. A consumer that needs the expired-vs-cancelled distinction reads Status; promoting it to a typed cause enum is a non-breaking follow-up (slice 2).

type JobState

type JobState int

JobState is the lifecycle state of an async job. It is PUBLIC because it is what Poll returns (ADR-063 POLL-004). The lifecycle is monotonic — Running → (Succeeded | Failed) — because pollJob returns on the FIRST terminal classification and no state is stored that could regress, not because any test proves it. A single Poll is one observation of that lifecycle, never a writer.

const (
	// JobRunning is the non-terminal state: the job is submitted or in progress
	// and the caller should keep polling. A reconstituted handle (ADR-014
	// cross-process resume) re-enters here.
	JobRunning JobState = iota
	// JobSucceeded is the terminal success state; the result is available.
	JobSucceeded
	// JobFailed is the terminal failure state; see JobStatus.Cause.
	JobFailed
)

func (JobState) String

func (s JobState) String() string

String renders the state for logs / telemetry.

type JobStatus

type JobStatus[T any] struct {
	// State is the job's lifecycle state at this poll.
	State JobState
	// Result is the normalized capability response, set iff State == JobSucceeded
	// (the second network hop, if any, has already been performed).
	Result *T
	// Cause is the normalized failure detail, set iff State == JobFailed.
	Cause *JobFailure
	// RawStatus is the provider's raw status string, for logging or a consumer
	// that wants to branch below the normalized state.
	RawStatus string
}

JobStatus is the normalized result of a single Poll (ADR-063 POLL-001): the state plus the result XOR the failure cause — never a raw provider payload. Result is set iff State == JobSucceeded; Cause is set iff State == JobFailed.

Contract: on any error from Poll, the returned JobStatus is the zero value — check the error before reading it (standard Go err-first). The zero State is JobRunning, so a JobStatus read without checking the error would look falsely live; there is deliberately no JobUnknown state (ADR-063 §"Implementation refinements" 2 — it would be an asymmetric/dead member across the four SDKs).

type LifecycleConfig

type LifecycleConfig struct {
	// Noun labels the capability in the failure error string ("transcription",
	// "batch") so a JobFailed terminal reads "<noun> failed: <message>",
	// preserving transcription's existing surface (S02).
	Noun string
	// StatusPath is the dotted path to the status string in the poll body.
	StatusPath string
	// DoneValues are the status strings marking terminal success (precedence
	// over ErrorValues).
	DoneValues []string
	// ErrorValues are the status strings marking terminal failure. An empty set
	// means "no failure terminal" — today's batch behavior, additive and
	// backward-safe (ADR-062 §"Implementation refinements" 4). Batch gains real
	// values via the errorValues A-Box fact (slice 1, step 6); transcription
	// supplies its ErrorStatus.
	ErrorValues []string
	// ErrorMessagePath is the dotted path to a provider error message, surfaced
	// in JobFailure.Message. Empty = no message extraction.
	ErrorMessagePath string
	// PollInterval is the cadence between polls.
	PollInterval time.Duration
	// PollTimeout is the overall wall-clock backstop for the pollJob LOOP — NOT
	// a per-request HTTP timeout (do NOT conflate with an HTTP request timeout,
	// S05). Zero = no backstop (the caller ctx is the only bound). Batch gains a
	// ~10-min default here (ADR-062 OQ-1); in Go the caller ctx still bounds
	// first, so the backstop only fires on an unbounded ctx.
	PollTimeout time.Duration
}

LifecycleConfig is the config half of the engine seam: the classification facts (status path + done / error value sets + the error-message path) and the poll cadence. Each capability assembles it from its own generated facts (batch Lifecycle.*, transcription StatusPath / DoneStatus / ErrorStatus). Slice 1 assembles it from today's facts; the shared llm:AsyncJobLifecycle block is slice 2 (ADR-062 §(a)).

type LiveResult

type LiveResult struct {
	// Models are the ModelInfo records that returned successfully across configured providers. Sorted by (provider name, id) for deterministic ordering across calls.
	Models []ModelInfo

	// Errors is the per-provider failure map. Empty when every configured provider succeeded. Keyed by Provider; each value carries the per-provider error sentinel (ErrModelsScope / ErrModelsUnavailable / ErrModelsNotSupported).
	Errors map[string]ProviderError
}

LiveResult is returned by c.Models.Live(ctx) — the aggregated cross-provider live result. Partial success is the documented normal case: per-provider failures land in Errors while everything that succeeded lands in Models.

type MediaRef

type MediaRef struct {
	// MimeType is the IANA media type of the bytes payload (image/png, image/jpeg, audio/wav, ...). Drives both the wire encoding (base64 mime prefix on data URIs) and provider routing on multimodal endpoints.
	MimeType string

	// Bytes is the raw (not base64-encoded) media payload. The transform layer base64-encodes at wire time per provider; callers always pass raw bytes.
	Bytes []byte
}

MediaRef is an inline media payload (mime type + raw bytes). Reused by every Part variant that carries non-text content, and by image-generation knobs like Mask that pass through a single binary blob.

type Message

type Message struct {
	// Role is the speaker identifier. Conventionally "user", "assistant", or "tool"; provider transforms may map to other roles (Bedrock's "USER"/"ASSISTANT", Google's "user"/"model").
	Role string

	// Content is the turn's text content. Empty on assistant-with-tools turns and on tool turns (the carrier field switches to tool_calls or tool_result respectively).
	Content string

	// ToolCalls are the tool invocations the model produced on an assistant turn. Defaults to an empty list (never null) so consumers can iterate without a None-guard regardless of role. Empty on text turns and tool turns.
	ToolCalls []ToolCall

	// ToolResult is the tool execution result on a role=tool turn. Null on text turns and assistant turns. Singular (not plural) because one tool turn carries exactly one result.
	ToolResult *ToolResult
}

Message is a single turn in a multi-turn conversation. Discriminated by role: text turns set content; assistant-with-tools turns set tool_calls; tool turns set tool_result. Consumers MUST inspect role before reading the optional tool-turn fields. ADR-020 extends Message with tool_calls and tool_result so *Agent history round-trips fully across process boundaries.

func LoadHistory

func LoadHistory(data []byte) ([]Message, error)

LoadHistory parses a wire document and returns the in-memory Message slice. Rejects documents missing `_v`, with `_v` above the compiled-in WireSchemaVersion, or with unknown top-level keys (STAB-003 + STAB-011). Tolerates unknown keys nested inside Message / ToolCall / ToolResult so additive evolution under the same `_v` keeps loading on older readers (STAB-003 lax-read).

type MiddlewareFn

type MiddlewareFn = providers.MiddlewareFn

MiddlewareFn is the user-supplied hook fired around capability calls. Aliased to providers.MiddlewareFn so callers don't need to import the providers subpackage just to declare a hook.

type MiddlewareVetoError

type MiddlewareVetoError struct {
	Cause error
}

MiddlewareVetoError wraps a pre-phase veto. Callers can errors.As against this type to discriminate a veto from a transport or provider error.

func (*MiddlewareVetoError) Error

func (e *MiddlewareVetoError) Error() string

func (*MiddlewareVetoError) Unwrap

func (e *MiddlewareVetoError) Unwrap() error

type ModelInfo

type ModelInfo struct {
	// ID is the provider-scoped model identifier (e.g. claude-opus-4-7, gpt-5, gemini-2.5-flash). Round-tripped to provider endpoints verbatim.
	ID string

	// Provider is the Provider value that exposes this model. Used by Models.Provider(m.Provider).Get(ctx, m.ID) to round-trip back to live data when needed.
	Provider Provider

	// Capabilities is the SDK's understanding of what this model supports — chat completion, image generation, tool calling, etc. Always populated from the ontology, never from wire data. Empty (nil) for live IDs the SDK does not recognise.
	Capabilities []Capability

	// DisplayName is the human-readable name when the provider supplies one (Anthropic display_name, Google displayName). Empty when the provider's wire shape does not carry one or for compiled-in entries.
	DisplayName string

	// Description is the provider's free-text description (Google description). Empty for providers that do not publish a description field and for compiled-in entries.
	Description string

	// ContextWindow is the maximum input token count when published (Anthropic max_input_tokens, Google inputTokenLimit). Zero when the provider does not publish it (OpenAI-shape cohort) or for compiled-in entries without a curated value.
	ContextWindow int

	// MaxOutput is the maximum output token count when published (Anthropic max_tokens, Google outputTokenLimit). Zero when the provider does not publish it or for compiled-in entries.
	MaxOutput int

	// Created is the Unix-timestamp creation time when the provider publishes one (Anthropic created_at parsed to Unix, OpenAI created). Zero for compiled-in entries and providers that do not publish it.
	Created int

	// Raw is the parsed provider-native record for this model, populated only when the caller opted in via the builder's .Raw() chain method (ADR-014). Type-erased — consumers cast to a provider-shape type for fields the universal ModelInfo does not carry (Anthropic capability matrix, Google supportedGenerationMethods, etc.).
	Raw json.RawMessage
}

ModelInfo is the universal model descriptor returned by c.Models methods (compiled-in and live). Capabilities is always ontology-derived — never from wire data; wire fills the metadata fields when present.

type Models

type Models struct {
	// contains filtered or unexported fields
}

Models is the catalogue builder. Chain methods are immutable; List/Get walk the compiled-in slice, Live(ctx) fans out HTTP across configured providers, Provider(p) scopes to one provider and returns *ScopedModels.

func (*Models) Get

func (b *Models) Get(id string) (ModelInfo, bool)

Get returns a compiled-in model by ID; the bool reports whether an entry was found.

func (*Models) List

func (b *Models) List() []ModelInfo

List returns the compiled-in catalogue, filtered by WithCapability when set. Sync, no IO, no error.

func (*Models) Live

func (b *Models) Live(ctx context.Context) (LiveResult, error)

Live runs an HTTP fan-out across configured providers and returns a LiveResult aggregating the union of successful records plus a per-provider error map. WithCapability composes post-fetch.

func (*Models) Provider

func (b *Models) Provider(p Provider) *ScopedModels

Provider scopes the catalogue to a single Provider and returns the *ScopedModels sub-builder on which Raw(), List(ctx), and Get(ctx, id) are reachable. Compiled-in *Models.Get(id) is sync; scoped *ScopedModels.Get(ctx, id) is the HTTP variant.

func (*Models) WithCapability

func (b *Models) WithCapability(c Capability) *Models

WithCapability filters the catalogue to models whose ontology-derived Capabilities slice contains c. Composes with List (compiled-in), Live (live aggregate), and Provider(p).List.

type Music

type Music struct {
	// contains filtered or unexported fields
}

Music accumulates configuration for a MusicGeneration call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Music) AddMiddleware

func (b *Music) AddMiddleware(fns ...MiddlewareFn) *Music

func (*Music) Generate

func (b *Music) Generate(ctx context.Context, finalText string) (MusicResponse, error)

Generate executes the chained MusicGeneration request against the client's provider. Chain state populates MusicRequest and the matching MusicOption set; finalText, when non-empty, becomes a trailing text Part appended to the chain's accumulated Parts (ADR-033).

func (*Music) Lyrics

func (b *Music) Lyrics(s string) *Music

func (*Music) Model

func (b *Music) Model(name string) *Music

func (*Music) Raw

func (b *Music) Raw() *Music

func (*Music) Text

func (b *Music) Text(s string) *Music

type MusicOption

type MusicOption func(*musicOptions)

MusicOption configures GenerateMusic.

func WithMusicHTTPClient

func WithMusicHTTPClient(c *http.Client) MusicOption

WithMusicHTTPClient overrides the http.Client used for the GenerateMusic call.

func WithMusicMiddleware

func WithMusicMiddleware(fns ...providers.MiddlewareFn) MusicOption

WithMusicMiddleware registers pre/post hooks that fire around the music generation request. Op is providers.OpMusicGeneration. Pre-phase can veto.

type MusicRequest

type MusicRequest struct {
	Model  string
	Prompt string
	Parts  []Part
}

MusicRequest is the canonical music-generation request (ADR-033).

Model is required: music-generation models are explicit choices and the text-generation default does not generate audio.

Input is provided in one of two mutually-exclusive forms:

  • Prompt: terse sugar for the prompt-only hot path. Internally desugars to Parts: []Part{Text(Prompt)} before serialisation.
  • Parts: canonical sequence of text and lyrics parts. A music request never carries image parts; the runtime rejects them pre-flight.

Pre-flight validation requires exactly one of Prompt or Parts to be non-empty (XOR). Lyrics on an instrumental-only model are advisory, not rejected (ADR-037 MUS-008): they fold into the prompt for the Predict shape.

type MusicResponse

type MusicResponse struct {
	// Audio are the decoded audio payloads (mime type + raw bytes) returned by the provider. Empty when the provider blocks or refuses the request — inspect FinishReason / FinishMessage for the cause.
	Audio []AudioData

	// Text is the optional text accompanying the audio (generated lyrics, song structure, or model commentary). Populated by Gemini Lyria 3; empty on Vertex Lyria 2 and MiniMax.
	Text string

	// Usage holds token consumption metrics for the music-generation call. None of the three verified providers report audio-output tokens as a distinct dimension; this stays zero unless a provider surfaces counts (ADR-033 OQ-3).
	Usage Usage

	// FinishReason is the provider stop signal. Gemini surfaces STOP/SAFETY etc.; Vertex Imagen-style providers surface a RAI filter reason when content is blocked; MiniMax carries a base_resp status. Optional.
	FinishReason string

	// FinishMessage is the free-text provider explanation of the stop signal. Use as the user-facing message when len(Audio) == 0.
	FinishMessage string

	// Raw is the parsed provider response body, populated only when the caller opted in via the builder's .raw() chain method (ADR-014). Type-erased — consumers cast to a provider-shape type for fields the universal MusicResponse does not carry.
	Raw json.RawMessage
}

MusicResponse is the universal music-generation response container returned by Music.Generate. Carries the decoded audio, optional text (generated lyrics / commentary), usage, and the same finish-reason / finish-message / raw fields the image-gen and text-gen responses carry.

type Option

type Option func(*options)

Option configures a Prompt or Agent call.

func CacheTTL

func CacheTTL(d time.Duration) Option

CacheTTL sets the cache time-to-live. Used by resource caching (Google). Ignored by providers with automatic or explicit caching.

func WithCaching

func WithCaching() Option

WithCaching enables prompt caching for providers that support it. Behavior depends on the provider's caching mode (automatic, explicit, or resource).

func WithFrequencyPenalty

func WithFrequencyPenalty(v float64) Option

WithFrequencyPenalty sets the repetition penalty (-2.0 to 2.0).

func WithHTTPClient

func WithHTTPClient(c *http.Client) Option

WithHTTPClient sets a custom HTTP client.

func WithMaxTokens

func WithMaxTokens(n int) Option

WithMaxTokens sets the maximum output length.

func WithMaxToolIterations

func WithMaxToolIterations(n int) Option

WithMaxToolIterations sets the maximum tool call loop iterations for Agent.

func WithMiddleware

func WithMiddleware(fns ...providers.MiddlewareFn) Option

WithMiddleware registers pre/post hooks that fire around LLM requests, tool calls, cache creation, uploads, and batch submits. Pre-phase middleware can veto an operation by returning a non-nil error. Post-phase return values are ignored (observation only). Middlewares fire in registration order.

func WithPollTimeout

func WithPollTimeout(d time.Duration) Option

WithPollTimeout overrides the overall wall-clock backstop for a blocking batch Wait (ADR-062 OQ-1). The default is ~10 minutes — a sane ceiling for a request/serverless thread. Raise it (up to the provider's batch window, e.g. OpenAI's 24h) for a caller that legitimately blocks on a long batch; the caller ctx deadline still bounds Wait first. This is the OVERALL loop deadline, not a per-request HTTP timeout (that is WithHTTPClient's transport).

func WithPresencePenalty

func WithPresencePenalty(v float64) Option

WithPresencePenalty sets the diversity encouragement (-2.0 to 2.0).

func WithReasoningEffort

func WithReasoningEffort(v string) Option

WithReasoningEffort sets reasoning intensity ("low", "medium", "high").

func WithSafetySettings

func WithSafetySettings(settings ...SafetySetting) Option

WithSafetySettings sets per-category content safety filters. Gemini AI Studio only — ValidationError on providers without a safetySettingsWirePath.

func WithSeed

func WithSeed(n int64) Option

WithSeed sets the seed for deterministic generation.

func WithStopSequences

func WithStopSequences(seqs ...string) Option

WithStopSequences sets generation halt strings.

func WithTemperature

func WithTemperature(v float64) Option

WithTemperature sets the sampling temperature (0.0-2.0).

func WithThinkingBudget

func WithThinkingBudget(n int) Option

WithThinkingBudget sets the extended thinking token budget.

func WithTopK

func WithTopK(n int) Option

WithTopK sets top-K token limiting.

func WithTopP

func WithTopP(v float64) Option

WithTopP sets nucleus sampling probability (0.0-1.0).

type Part

type Part struct {
	Text   string
	Image  *MediaRef
	Lyrics string

	// AudioURL is a public audio URL for transcription (ADR-048), constructed
	// via parts.Audio(url). Submitted to the provider directly as audio_url.
	AudioURL string

	// Audio is local audio bytes for transcription (ADR-048), constructed via
	// parts.AudioBytes(mime, raw). The runtime uploads them first to obtain a
	// URL, then submits that.
	Audio *MediaRef
}

Part is the universal multimodal input atom. Exactly one of Text, Image, or Lyrics is set; none or more than one is invalid (rejected by pre-flight validation). Lyrics is a text payload tagged as song lyrics, used only by music generation (ADR-033) — image and text generation reject it. Construct via the parts/ sub-package: parts.Text(s) / parts.Image(mime, bytes) / parts.Lyrics(s).

type Provider

type Provider struct {
	Name    string // "anthropic", "openai", "google", "grok"
	APIKey  string
	Model   string // optional, uses default if empty
	BaseURL string // optional, overrides default API endpoint
	// Headers are custom HTTP headers added via Client.AddHeader (ADR-052).
	// Merged into every request before the provider auth header and the
	// static required header, so a gateway header (e.g. cf-aig-authorization)
	// rides alongside the provider key without clobbering it.
	Headers map[string]string
}

Provider identifies an LLM provider with its API key and optional overrides.

type ProviderError

type ProviderError struct {
	// Kind is the sentinel discriminant: "not_supported", "unavailable", or "scope". Mirrors the three Err* sentinels declared in ADR-019 § Error story. String (not enum) keeps the codegen-table footprint at zero and dodges the Python str(Enum) trap that Phase 2.5's review surfaced.
	Kind string

	// Message is the human-readable explanation. Free-form; not part of the contract beyond display.
	Message string
}

ProviderError is the per-provider failure carried in LiveResult.errors (ADR-019 Amendment 1). Discriminated by Kind so consumers can branch typed in any SDK; Message is the human-readable form for display.

type Providers

type Providers struct {
	// contains filtered or unexported fields
}

Providers is the providers-namespace prototype. List() returns the providers with both credentials configured and llm:hasModelsEndpoint declared, as secret-free ProviderInfo (ADR-040 PSR-005). The static roster of every supported provider is providers.List().

func (*Providers) List

func (b *Providers) List() []providers.ProviderInfo

List returns the providers eligible for *Models.Live(ctx) as secret-free ProviderInfo metadata (ADR-040 PSR-005).

type Request

type Request struct {
	System   string       // system prompt
	User     string       // user message (for single-turn)
	Messages []Message    // conversation history (for multi-turn)
	Schema   string       // JSON schema for structured output (optional)
	Files    []File       // file attachments (optional)
	Images   []InputImage // image inputs (optional)
}

Request is the canonical request format (OpenAI-compatible shape).

type Response

type Response struct {
	// Text is the assistant's response text, extracted from the provider response body at the path declared by llm:hasResponseTextPath.
	Text string

	// Usage holds token consumption metrics — input, output, cache_write, cache_read, and reasoning counts. Each dimension is populated from the provider-specific path declared in the ontology.
	Usage Usage

	// FinishReason is the provider stop signal, passed through verbatim. Empty when the provider response carries no signal or the parser does not yet read this provider's location. Examples per provider: Google STOP/MAX_TOKENS/SAFETY/RECITATION; OpenAI stop/length/content_filter/tool_calls; Anthropic end_turn/max_tokens/stop_sequence/tool_use; xAI stop/length/content_filter.
	FinishReason string

	// FinishMessage is the provider-supplied free-text explanation of the stop signal. Populated by Google when present; OpenAI / Anthropic / xAI do not carry an equivalent field, so this stays empty for them.
	FinishMessage string

	// Raw is the parsed provider response body, populated only when the caller opted in via the typed builder's .raw() chain method (ADR-014). Type-erased — provider-specific fields (Anthropic citations, OpenAI logprobs, Google promptFeedback, ...) are not part of the universal Response shape; consumers cast to a provider-shape type once they know which provider they're talking to.
	Raw json.RawMessage
}

Response is the universal response container returned by text-generation terminals (Text.Prompt, Agent.Prompt). Five fields; all five are core (no per-capability augmentation).

type SafetySetting

type SafetySetting struct {
	Category  string // e.g. "HARM_CATEGORY_DANGEROUS_CONTENT"
	Threshold string // e.g. "BLOCK_ONLY_HIGH" or "BLOCK_NONE"
}

SafetySetting configures a per-category content safety filter for Gemini providers. Category and Threshold are passed through verbatim to the provider wire body — use the HARM_CATEGORY_* and HARM_BLOCK_THRESHOLD_* constants or Google's latest string values directly.

type ScopedModels

type ScopedModels struct {
	// contains filtered or unexported fields
}

ScopedModels is the single-provider live-catalogue sub-builder. Reached via *Models.Provider(p). Raw() opts into populating ModelInfo.Raw with the parsed provider-native record per ADR-014.

func (*ScopedModels) Get

func (b *ScopedModels) Get(ctx context.Context, id string) (ModelInfo, error)

Get fetches a single live model record by ID.

func (*ScopedModels) List

func (b *ScopedModels) List(ctx context.Context) ([]ModelInfo, error)

List performs the live HTTP call for this provider, looping through pagination per the provider's PaginationStyle.

func (*ScopedModels) Raw

func (b *ScopedModels) Raw() *ScopedModels

Raw flags the chain to populate ModelInfo.Raw on each record.

type Speech

type Speech struct {
	// contains filtered or unexported fields
}

Speech accumulates configuration for a SpeechGeneration call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Speech) Generate

func (b *Speech) Generate(ctx context.Context, text string) (SpeechResponse, error)

Generate executes the chained SpeechGeneration request against the client's provider. Chain state (Model, Voice) populates SpeechRequest; text is the single utterance to speak (ADR-049).

func (*Speech) Model

func (b *Speech) Model(name string) *Speech

func (*Speech) Voice

func (b *Speech) Voice(id string) *Speech

type SpeechRequest

type SpeechRequest struct {
	Model string
	Voice string
	Text  string
}

SpeechRequest is the canonical text-to-speech request (ADR-049).

Model is required: speech-generation models are explicit choices and the text-generation default does not synthesize audio. Voice is required and is validated pre-flight against the provider's voice catalogue (SPK-004). Text is the single utterance to speak — single-turn, no Message/Role wrapper (SPK-003).

type SpeechResponse

type SpeechResponse struct {
	// Audio is the synthesized audio (mime type + raw bytes). One synthesis yields one clip, so this is a single AudioData, not a list (ADR-049 OQ-4).
	Audio AudioData

	// Usage holds provider-reported usage. Inworld returns usage.processedCharactersCount, but the SDK does not yet surface it: the Usage carrier has no characters axis and OQ-3 declined to overload a token axis, so this stays zero pending a typed characters dimension (ADR-049 OQ-3, deferred).
	Usage Usage

	// FinishReason is the provider stop signal, when present. Optional.
	FinishReason string
}

SpeechResponse is the universal text-to-speech response container returned by Speech.Generate. Carries the synthesized audio (a single clip), the provider-reported usage, and an optional finish reason — reusing the music pipeline's AudioData container unchanged (ADR-049).

type StreamCallback

type StreamCallback func(chunk string)

StreamCallback is called with each text chunk during streaming.

type Telemetry

type Telemetry struct {
	// Export receives the finished OTLP/HTTP proto3-JSON bytes for one span,
	// called synchronously on the post phase. Mandatory. Use HTTPExport for the
	// batteries POST, or supply your own to bridge into an existing OTEL stack.
	Export func([]byte)
	// CaptureContent gates tier-2 message payloads (default false for privacy).
	// The middleware Event does not carry payloads yet, so this reserves the
	// semantics; content-log emission is a deferred follow-up (ADR-054 tier 2).
	CaptureContent bool
}

Telemetry is the opt-in observability config (ADR-059, superseding ADR-054's transport half). Attach it with Client.AddTelemetry: on every provider call — success and rejection — llmkit builds an OTEL GenAI-aligned OTLP span (proto3 JSON) and hands the finished bytes to Export. llmkit performs no telemetry network I/O and spawns no goroutine; what Export does with the bytes (enqueue into an OTEL SDK, POST, drop) and all batching/backpressure/shutdown is the caller's concern. Off unless attached; a nil Export is a ValidationError (the honest-contract lineage — no enabled-but-no-sink state). Use HTTPExport for a batteries POST. A sibling of the ADR-052 baseURL / custom-header runtime overrides — a handwritten config value, not modelled in the ontology.

type Text

type Text struct {
	// contains filtered or unexported fields
}

Text accumulates configuration for a ChatCompletion call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Text) AddMiddleware

func (b *Text) AddMiddleware(fns ...MiddlewareFn) *Text

func (*Text) Batch

func (b *Text) Batch(ctx context.Context, prompts ...string) (BatchHandle, error)

Batch queues the chained text request as a batch and returns a handle without blocking (ADR-064, revised: batch is a text EXECUTION MODE on the *Text builder, parallel to Stream — not a separate capability). The chain's accumulated config (System, MaxTokens, Schema, ...) applies to EVERY prompt in the variadic; per-prompt divergence is tracked as plan-016 OQ-2. The blocking one-liner is the compose Batch(...).Wait(...); there is no run() terminal and no blocking-sugar variant.

Provider gate: only Anthropic, Google, OpenAI support batch APIs; other providers surface a ValidationError from the internal submit. A non-default chat protocol (e.g. Responses) is rejected — batch runs the default envelope.

ADR-014: the chain's Raw() opt-in is remembered on the returned BatchHandle.Raw so handle.Wait() honors it without the caller needing to re-specify. Cross-process resume callers persist {ID, Provider, Raw} and reconstruct directly.

func (*Text) Caching

func (b *Text) Caching() *Text

func (*Text) File

func (b *Text) File(id string) *Text

func (*Text) FrequencyPenalty

func (b *Text) FrequencyPenalty(v float64) *Text

func (*Text) History

func (b *Text) History(msgs ...Message) *Text

func (*Text) Image

func (b *Text) Image(mime string, data []byte) *Text

func (*Text) MaxTokens

func (b *Text) MaxTokens(n int) *Text

func (*Text) Model

func (b *Text) Model(name string) *Text

func (*Text) PresencePenalty

func (b *Text) PresencePenalty(v float64) *Text

func (*Text) Prompt

func (b *Text) Prompt(ctx context.Context, finalText string) (Response, error)

Prompt executes the chained ChatCompletion request against the client's provider. Body absorbed from legacy free function in plan-018 D1.3a.

func (*Text) Protocol

func (b *Text) Protocol(name string) *Text

func (*Text) Raw

func (b *Text) Raw() *Text

func (*Text) ReasoningEffort

func (b *Text) ReasoningEffort(level string) *Text

func (*Text) SafetySettings

func (b *Text) SafetySettings(s []SafetySetting) *Text

func (*Text) Schema

func (b *Text) Schema(s string) *Text

func (*Text) Seed

func (b *Text) Seed(n int64) *Text

func (*Text) StopSequences

func (b *Text) StopSequences(seqs ...string) *Text

func (*Text) Stream

func (b *Text) Stream(ctx context.Context, finalText string) *TextStream

Stream begins a streaming chat completion call. The returned *TextStream is a trailing-handle: chunks are produced lazily via Chunks(); Response() is populated when iteration completes.

func (*Text) System

func (b *Text) System(s string) *Text

func (*Text) Temperature

func (b *Text) Temperature(t float64) *Text

func (*Text) Text

func (b *Text) Text(s string) *Text

func (*Text) ThinkingBudget

func (b *Text) ThinkingBudget(n int) *Text

func (*Text) TopK

func (b *Text) TopK(n int) *Text

func (*Text) TopP

func (b *Text) TopP(v float64) *Text

type TextStream

type TextStream struct {
	// contains filtered or unexported fields
}

TextStream is the trailing-handle wrapper returned by *Text.Stream. Range over Chunks() to consume deltas as they arrive; after iteration completes (without a break), Response() returns the accumulated Response carrying final token counts. Err() returns any error that terminated the stream early.

stream := c.Text.System("...").Stream(ctx, "hi")
for chunk, err := range stream.Chunks() {
    if err != nil { return err }
    fmt.Print(chunk)
}
resp := stream.Response()  // populated after the range loop ends
fmt.Println(resp.Usage)

Response() before iteration completes returns the zero value; Err() returns nil. After iteration, both reflect the producer's final outcome. Breaking the range loop cancels the producer; in that case Response() reflects whatever was accumulated by the legacy callback up to the break point and Err() returns nil.

func (*TextStream) Chunks

func (s *TextStream) Chunks() iter.Seq2[string, error]

Chunks returns an iter.Seq2[string, error] that yields chunk-string / error pairs in producer order. Errors land at the end of iteration (one final yield with chunk == ""). To stop early, break the range loop; the producer goroutine is cancelled and any pending chunks are drained so the goroutine exits cleanly.

func (*TextStream) Err

func (s *TextStream) Err() error

Err returns any error that terminated the stream. Errors are also surfaced via the final Chunks() yield; Err() is the convenience accessor for code that doesn't want to inspect every iteration.

func (*TextStream) Response

func (s *TextStream) Response() Response

Response returns the accumulated Response (text + token counts). Populated after Chunks() iteration completes; the zero value is returned before iteration starts or if the stream errored before the provider sent any usage events.

type Tool

type Tool struct {
	Name        string
	Description string
	Schema      map[string]any
	Run         func(map[string]any) (string, error)
}

Tool defines a callable function for the agent.

type ToolCall

type ToolCall struct {
	// ID is the provider-issued call identifier. Round-tripped to ToolResult.tool_use_id on the response turn so the model can correlate the request with its execution outcome.
	ID string

	// Name is the tool name the model selected. Matches the name registered via the Tool functional option on the *Agent builder.
	Name string

	// Input is the JSON-decoded argument object the model passed. Null on absent or empty arg sets; otherwise a provider-specific JSON value (typically an object, but type-erased through OptionalAny because schemas vary per tool).
	Input json.RawMessage
}

ToolCall is a single tool invocation issued by the model on an assistant turn. Carries the provider-issued id, the tool name, and the JSON-decoded argument object. ADR-020 promotes this from a private per-SDK type into a public generated struct so *Agent history can carry tool turns end to end.

type ToolResult

type ToolResult struct {
	// ToolUseID is the ToolCall.id this result responds to. Lets the model correlate the response with its earlier request when multiple tools are called in parallel.
	ToolUseID string

	// Content is the stringified tool return value. Tool authors that return non-string types must stringify before yielding; this stays string-typed to keep the wire shape uniform across providers.
	Content string
}

ToolResult is the execution result of one tool call, attached to a role=tool turn. Pairs with a prior ToolCall via tool_use_id. ADR-020 promotes this from a private per-SDK type into a public generated struct.

type TranscriptSegment

type TranscriptSegment struct {
	// Text is the segment text.
	Text string

	// Start is the segment start offset in milliseconds.
	Start int

	// End is the segment end offset in milliseconds.
	End int

	// Speaker is the diarized speaker label, when the provider reports one. Empty otherwise.
	Speaker string
}

TranscriptSegment is one timed span of transcript (ADR-048). Slice-1 segments carry text + millisecond offsets + an optional diarized speaker label; confidence (a float) is deferred until the struct-field type table gains a float type (ADR-048 OQ-4).

type Transcription

type Transcription struct {
	// contains filtered or unexported fields
}

Transcription accumulates configuration for a Transcription call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Transcription) Model

func (b *Transcription) Model(name string) *Transcription

func (*Transcription) Submit

func (b *Transcription) Submit(ctx context.Context, audioParts ...Part) (TranscriptionHandle, error)

Submit executes the chained Transcription request against the client's provider and returns a TranscriptionHandle immediately (ADR-048). The audio source is supplied as the terminal's audio Parts (exactly one is valid in slice 1): parts.Audio(url) or parts.AudioBytes(mime, raw). Poll the returned handle with Wait.

func (*Transcription) Transcribe

func (b *Transcription) Transcribe(ctx context.Context, audioParts ...Part) (TranscriptionResponse, error)

Transcribe executes a SYNCHRONOUS transcription against the client's provider and returns the finished TranscriptionResponse directly — no job handle (ADR-051). The audio is supplied inline as exactly one bytes Part (parts.AudioBytes(mime, raw)); a remote audio URL is not accepted. Use this for sync providers (OpenAI); async providers (AssemblyAI) reject it pre-flight in favor of Submit/Wait.

type TranscriptionHandle

type TranscriptionHandle struct {
	// ID is the provider-assigned transcript id returned by the submit endpoint (AssemblyAI: id). Opaque to the SDK; round-tripped to the poll endpoint verbatim.
	ID string

	// Provider is the Provider config used to submit the job. Carried on the handle so Wait knows where to poll without re-parameterising the client.
	Provider Provider
}

TranscriptionHandle is a value struct identifying a submitted transcription job, modeled on VideoHandle / BatchHandle (ADR-014 / ADR-034). Cross-process resume works by persisting the fields and reconstructing the handle; the poll loop (Wait) is hand-written runtime, not part of the generated value.

func (TranscriptionHandle) Poll

Poll performs exactly ONE provider round-trip and returns the normalized JobStatus (ADR-063 POLL-001) — the non-blocking primitive for callers driving their own poll loop. On a completed job JobStatus.Result carries the finished TranscriptionResponse; a failed job populates JobStatus.Cause (the provider error surfaces in Cause.Message, preserving the Wait error surface). Safe on a reconstituted handle (ADR-014 cross-process resume; POLL-005).

func (TranscriptionHandle) Wait

Wait polls the provider until the transcription job reaches a terminal state, then returns the finished TranscriptionResponse. A status=error job surfaces as an error (never a silent empty success). The status-to-terminal mapping is read from config (STT-005); only result extraction is wire-shape-keyed. The handle carries the transcript id and provider config, so Wait works across process boundaries.

type TranscriptionOption

type TranscriptionOption func(*transcriptionOptions)

TranscriptionOption configures Submit / Wait.

func WithTranscriptionHTTPClient

func WithTranscriptionHTTPClient(c *http.Client) TranscriptionOption

WithTranscriptionHTTPClient overrides the http.Client used for the transcription calls.

type TranscriptionRequest

type TranscriptionRequest struct {
	Model string
	Parts []Part
}

TranscriptionRequest is the canonical speech-to-text request (ADR-048). It carries exactly one audio Part — a public URL (parts.Audio) or local bytes (parts.AudioBytes). Transcription is single-turn, so there is no Message/Role wrapper (golden rule). Model is used only by synchronous providers (OpenAI, ADR-051), where it is a required multipart field; async providers ignore it.

type TranscriptionResponse

type TranscriptionResponse struct {
	// Text is the full transcript text.
	Text string

	// Segments are the timed transcript segments (start/end offsets in milliseconds). Empty when the provider returns no word-level timing.
	Segments []TranscriptSegment

	// Usage holds provider-reported usage. AssemblyAI bills by audio duration, not tokens; this stays zero unless a provider surfaces a token axis (ADR-048 OQ-2).
	Usage Usage
}

TranscriptionResponse is the universal speech-to-text response container returned by TranscriptionHandle.Wait. Carries the full transcript text, the timed transcript segments, and the provider-reported usage. The container is text-shaped, NOT a media *Data container — the structural divergence from video (ADR-048).

type Upload

type Upload struct {
	// contains filtered or unexported fields
}

Upload accumulates configuration for a FileUpload call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Upload) AddMiddleware

func (b *Upload) AddMiddleware(fns ...MiddlewareFn) *Upload

func (*Upload) Bytes

func (b *Upload) Bytes(data []byte) *Upload

func (*Upload) Filename

func (b *Upload) Filename(name string) *Upload

func (*Upload) MimeType

func (b *Upload) MimeType(mime string) *Upload

func (*Upload) Path

func (b *Upload) Path(p string) *Upload

func (*Upload) Run

func (b *Upload) Run(ctx context.Context) (File, error)

Run uploads the configured file to the client's provider and returns a File reference suitable for inclusion in a *Text.File() chain. Path and Bytes are mutually exclusive — Run validates exactly one is set. When Path is used, the filename in the multipart form is derived from filepath.Base(path) unless Filename() overrides it. When Bytes is used, Filename() is required (no path to derive a name from). MimeType() overrides the default detection when set.

type Usage

type Usage = providers.Usage

Usage holds token consumption metrics. Aliased to providers.Usage so middleware events and the public API share one type without conversion.

type ValidationError

type ValidationError struct {
	Field   string
	Message string
}

ValidationError represents a request validation error.

func (*ValidationError) Error

func (e *ValidationError) Error() string

type Video

type Video struct {
	// contains filtered or unexported fields
}

Video accumulates configuration for a VideoGeneration call. Chain methods return new instances (immutable); skipped terminals live in hand-written text.go / image.go.

func (*Video) AddMiddleware

func (b *Video) AddMiddleware(fns ...MiddlewareFn) *Video

func (*Video) Image

func (b *Video) Image(mime string, data []byte) *Video

func (*Video) Model

func (b *Video) Model(name string) *Video

func (*Video) OutputURI

func (b *Video) OutputURI(uri string) *Video

func (*Video) Raw

func (b *Video) Raw() *Video

func (*Video) Submit

func (b *Video) Submit(ctx context.Context, finalText string) (VideoHandle, error)

Submit executes the chained VideoGeneration request against the client's provider and returns a VideoHandle immediately (ADR-034). Chain state populates VideoRequest and the matching VideoOption set; finalText, when non-empty, becomes a trailing text Part appended to the chain's accumulated Parts. Poll the returned handle with Wait.

func (*Video) Text

func (b *Video) Text(s string) *Video

type VideoData

type VideoData struct {
	// MimeType is the IANA media type of the video (video/mp4). Drives the file extension the caller picks for storage.
	MimeType string

	// URL is the provider link (grok: a temporary xAI-hosted URL) or the caller-supplied S3 URI (Bedrock). Set for url and output-uri delivery; empty for download delivery. XOR with bytes.
	URL string

	// Bytes is the raw (not encoded) video payload, present only for download-delivery providers the SDK fetched on the caller's behalf. Empty for url and output-uri delivery. XOR with url.
	Bytes []byte

	// DurationSeconds is the duration of the finished video in seconds, when the provider reports it (grok: video.duration). Zero when unreported.
	DurationSeconds int
}

VideoData is one finished video returned in a VideoResponse. Models bytes (downloaded payload) XOR url (a provider link or caller S3 URI) — the source-XOR pattern (VID-004). url-delivery and output-uri providers set url; download-delivery providers set bytes.

type VideoHandle

type VideoHandle struct {
	// ID is the provider-assigned request id returned by the submit endpoint (grok: request_id). Opaque to the SDK; round-tripped to the poll endpoint verbatim.
	ID string

	// Provider is the Provider config used to submit the job. Carried on the handle so Wait knows where to poll without re-parameterising the client.
	Provider Provider

	// Raw is the ADR-014 opt-in: when true, the VideoResponse returned from Wait carries raw set to the parsed provider poll body. Submit propagates the chain's .raw() flag onto the handle; cross-process resume callers set it directly.
	Raw bool

	// Model is the submitted model id, carried so Wait can build a model-templated poll URL (Vertex Veo polls POST /{model}:fetchPredictOperation). Submit sets it from the request; empty for providers whose poll endpoint does not template the model.
	Model string
}

VideoHandle is a value struct identifying a submitted video job, modeled on BatchHandle (ADR-014). Cross-process resume works by persisting the fields and reconstructing the handle; the poll loop (Wait) is hand-written runtime, not part of the generated value.

func (VideoHandle) Wait

func (h VideoHandle) Wait(ctx context.Context, opts ...VideoOption) (VideoResponse, error)

Wait polls the provider until the video job reaches a terminal state, then returns the finished VideoResponse. A failed or expired job surfaces as an error. Poll cadence uses videoPollInterval until videoPollTimeout elapses (ADR-034 D2; per-call overrides deferred). The handle carries the request id and provider config, so Wait works across process boundaries.

type VideoOption

type VideoOption func(*videoOptions)

VideoOption configures Submit / Wait.

func WithVideoHTTPClient

func WithVideoHTTPClient(c *http.Client) VideoOption

WithVideoHTTPClient overrides the http.Client used for the video calls.

func WithVideoMiddleware

func WithVideoMiddleware(fns ...providers.MiddlewareFn) VideoOption

WithVideoMiddleware registers pre/post hooks that fire around the video submit request. Op is providers.OpVideoGeneration. Pre-phase can veto.

type VideoRequest

type VideoRequest struct {
	Model  string
	Prompt string
	Parts  []Part

	// OutputURI is the caller-supplied destination S3 URI for output-uri
	// delivery providers (Bedrock Nova Reel writes the mp4 to the caller's own
	// S3 bucket). Required when the provider's config sets RequiresOutputURI;
	// ignored otherwise. Set it on the builder via (*Video).OutputURI.
	OutputURI string
}

VideoRequest is the canonical video-generation request (ADR-034).

Model is required: video-generation models are explicit choices and the text-generation default does not generate video.

Input is provided in one of two mutually-exclusive forms:

  • Prompt: terse sugar for the prompt-only hot path. Internally desugars to Parts: []Part{Text(Prompt)} before serialisation.
  • Parts: canonical sequence of text parts (slice 1 is text-to-video).

Pre-flight validation requires exactly one of Prompt or Parts to be non-empty (XOR).

type VideoResponse

type VideoResponse struct {
	// Videos are the finished video references. url-delivery providers (grok) fill VideoData.url; download-delivery providers fill VideoData.bytes with bytes the SDK fetched; output-uri providers (Bedrock) carry the caller S3 URI in url. Empty when the job failed — inspect FinishReason / FinishMessage.
	Videos []VideoData

	// Usage holds token consumption metrics for the video-generation call. No verified provider reports a video usage axis yet; this stays zero unless a provider surfaces counts (ADR-034 OQ-3).
	Usage Usage

	// FinishReason is the provider terminal status / stop signal (grok: a non-done status such as expired or failed). Empty on success. Optional.
	FinishReason string

	// FinishMessage is the free-text provider explanation of a non-success status (grok: error.message on a failed job). Use as the user-facing message when len(Videos) == 0.
	FinishMessage string

	// Raw is the parsed provider poll response body, populated only when the caller opted in via the builder's .raw() chain method (ADR-014). Type-erased — consumers cast to a provider-shape type for fields the universal VideoResponse does not carry.
	Raw json.RawMessage
}

VideoResponse is the universal video-generation response container returned by VideoHandle.Wait. Carries the finished video references, usage, and the same finish-reason / finish-message / raw fields the image-gen and music-gen responses carry.

Directories

Path Synopsis
cmd
llmkit command
examples
agent command
Agent tool loop with a single add tool.
Agent tool loop with a single add tool.
batch command
Batch: send several prompts as one async job and collect every response in order.
Batch: send several prompts as one async job and collect every response in order.
caching command
Caching: opt a prompt into provider-side prompt caching with the .Caching() chain method.
Caching: opt a prompt into provider-side prompt caching with the .Caching() chain method.
catalogue command
Model catalogue + provider lookup.
Model catalogue + provider lookup.
image-gen command
Example: text-to-image generation against Google's Nano Banana 2 (Gemini 3.1 Flash Image), with a follow-up edit pass that uses the first output as a reference image.
Example: text-to-image generation against Google's Nano Banana 2 (Gemini 3.1 Flash Image), with a follow-up edit pass that uses the first output as a reference image.
image-gen-openai command
Example: text-to-image generation against OpenAI's gpt-image-2, with a follow-up edit pass that uses the first output as a reference.
Example: text-to-image generation against OpenAI's gpt-image-2, with a follow-up edit pass that uses the first output as a reference.
middleware command
Example: spend-cap middleware.
Example: spend-cap middleware.
music-gen command
Example: text-to-music generation against Google Cloud Vertex AI's Lyria 2 (ADR-033).
Example: text-to-music generation against Google Cloud Vertex AI's Lyria 2 (ADR-033).
quickstart command
Minimal one-shot text prompt.
Minimal one-shot text prompt.
reasoning command
Reasoning: ask the model to spend extra hidden reasoning effort with the .ReasoningEffort() chain method ("low", "medium", "high").
Reasoning: ask the model to spend extra hidden reasoning effort with the .ReasoningEffort() chain method ("low", "medium", "high").
smoketest command
Smoke-test every provider whose API-key env var is set.
Smoke-test every provider whose API-key env var is set.
stream command
Streaming with the trailing-handle iterator.
Streaming with the trailing-handle iterator.
upload command
File upload -- Path and Bytes branches.
File upload -- Path and Bytes branches.
vertex-imagen command
Example: text-to-image generation against Google Cloud Vertex AI's Imagen.
Example: text-to-image generation against Google Cloud Vertex AI's Imagen.
video-gen command
Example: text-to-video generation against xAI's Grok Imagine (ADR-034).
Example: text-to-video generation against xAI's Grok Imagine (ADR-034).
Package parts provides constructors for the universal multimodal input atom llmkit.Part.
Package parts provides constructors for the universal multimodal input atom llmkit.Part.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL