Documentation
¶
Overview ¶
Package llm is a multi-provider Go SDK for LLM inference endpoints: OpenAI, Google Gemini, DeepSeek, Z.ai, Kimi (Moonshot) and Anthropic, plus any custom OpenAI-compatible gateway.
Design highlights:
- Multiple authenticated endpoints simultaneously, discovered from the environment via <PROVIDER>_API_KEY (aliases supported).
- Dynamic model discovery (ListModels) — no static model profile tables.
- One canonical request/response shape (OpenAI-compatible); Anthropic and Gemini formats are translated by per-format serializers.
- Zero external dependencies: stdlib only.
- Streaming with idle watchdog, hard wall-clock deadline, abort-with- partial-result, and retries that never duplicate partial output.
Index ¶
- Constants
- Variables
- func RunTools(ctx context.Context, cc *ChatClient, req ChatRequest, ...) (*ChatResult, []Message, error)
- func SetLearnObserver(fn func(LearnEvent))
- func SetStreamIdleTimeout(d time.Duration)
- func StreamIdleTimeout() time.Duration
- type APIError
- type AttemptEvent
- type ChatClient
- func (c *ChatClient) Call(ctx context.Context, req *ChatRequest) (*ChatResult, error)
- func (c *ChatClient) CallStream(ctx context.Context, req *ChatRequest, onDelta func(Delta) error) (*ChatResult, error)
- func (c *ChatClient) CountTokens(ctx context.Context, req *ChatRequest) (int, error)
- func (c *ChatClient) Model() string
- func (c *ChatClient) ProviderID() string
- func (c *ChatClient) RequestTimeout() time.Duration
- func (c *ChatClient) SetRequestTimeout(d time.Duration)
- type ChatRequest
- type ChatResult
- type ConfigError
- type ContentPart
- type ContentPartType
- type Delta
- type DeltaKind
- type EmbedRequest
- type EmbedResult
- type Format
- type LearnEvent
- type LearnKind
- type ListOption
- type Message
- type Model
- type Option
- func FromEnv() Option
- func WithAttemptObserver(fn func(AttemptEvent)) Option
- func WithEnv(lookup func(string) (string, bool)) Option
- func WithLearnObserver(fn func(LearnEvent)) Option
- func WithModelCacheTTL(d time.Duration) Option
- func WithProvider(id string, popts ...ProviderOption) Option
- func WithRequestTimeout(d time.Duration) Option
- func WithRetryPolicy(p RetryPolicy) Option
- func WithStreamIdleTimeout(d time.Duration) Option
- func WithTransport(rt http.RoundTripper) Option
- type Provider
- type ProviderConfig
- type ProviderOption
- type Quirks
- type RateLimit
- type RateLimitError
- type ResponseFormat
- type ResponseFormatType
- type ResponseMeta
- type RetryPolicy
- type Role
- type SDK
- func (s *SDK) Chat(providerID, model string) (*ChatClient, error)
- func (s *SDK) Embed(ctx context.Context, providerID, model string, req EmbedRequest) (*EmbedResult, error)
- func (s *SDK) Provider(id string) (*Provider, error)
- func (s *SDK) Providers() []*Provider
- func (s *SDK) Speak(ctx context.Context, providerID, model string, req SpeakRequest) (*SpeakResult, error)
- func (s *SDK) Transcribe(ctx context.Context, providerID, model string, req TranscribeRequest) (*TranscribeResult, error)
- type SpeakRequest
- type SpeakResult
- type StreamAbortedError
- type SystemBlock
- type ThinkingBlock
- type ToolCall
- type ToolChoice
- type ToolChoiceMode
- type ToolDef
- type ToolHandler
- type ToolLoopOptions
- type TranscribeRequest
- type TranscribeResult
- type UnsupportedError
- type Usage
Constants ¶
const ( FinishStop = "stop" FinishLength = "length" FinishToolCalls = "tool_calls" FinishContentFilter = "content_filter" )
Canonical finish reasons.
const (
DefaultTimeout = 120 * time.Second
)
Transport tuning defaults, shared by every provider client an SDK builds.
const MaxDocumentBytes = 20 << 20
MaxDocumentBytes bounds one inline document part.
const MaxImageBytes = 10 << 20
const MaxRequestImageBytes = 32 << 20
MaxRequestImageBytes bounds the aggregate inline image payload in one request, preventing many individually valid parts from causing an unexpectedly large allocation or HTTP body.
Variables ¶
var ErrIdleTimeout = errors.New("llm: stream idle timeout")
ErrIdleTimeout is returned (with the partial result) when a stream goes silent longer than the idle watchdog after the first delta has been emitted. Before the first delta, idle timeouts are retried instead.
var ErrMaxToolTurns = errors.New("llm: max tool turns exceeded")
ErrMaxToolTurns reports that the model still wanted tools after MaxTurns.
var ErrModelListTruncated = errors.New("llm: model listing exceeds the page cap")
ErrModelListTruncated reports a model listing longer than the SDK's page cap; the SDK never returns a silently truncated listing.
var ErrUnsupported = errors.New("llm: operation not supported")
ErrUnsupported marks an operation the provider format cannot perform.
Functions ¶
func RunTools ¶ added in v1.1.0
func RunTools(ctx context.Context, cc *ChatClient, req ChatRequest, handlers map[string]ToolHandler, opts ToolLoopOptions) (*ChatResult, []Message, error)
RunTools drives a buffered tool loop: it calls the model, runs the requested tools through handlers, appends the results and repeats until the model stops asking for tools. It returns the final result and the complete transcript (req.Messages plus everything appended). req.Messages is copied; the caller's slice is never mutated.
A handler error (or panic, or a missing handler) becomes an IsError tool result and the loop continues. If MaxTurns chat calls all end in tool calls, the last result is returned with ErrMaxToolTurns and a transcript without the final (unanswered) assistant turn.
func SetLearnObserver ¶ added in v0.7.0
func SetLearnObserver(fn func(LearnEvent))
SetLearnObserver registers a process-wide callback fired once per engaged learn flag, at the moment the fallback decision is made. Nil (the default) disables observation entirely. Delivery is at-most-once: a callback registered after a flag engaged (or swapped during the engaging call) does not retroactively receive that engagement. The callback runs on the request goroutine: keep it fast and non-blocking; it must never call back into the SDK and must not panic.
func SetStreamIdleTimeout ¶ added in v0.2.1
SetStreamIdleTimeout overrides the process-wide default SSE idle watchdog; non-positive values are ignored. Prefer the per-SDK WithStreamIdleTimeout option, which takes precedence.
func StreamIdleTimeout ¶ added in v0.2.1
StreamIdleTimeout reports the process-wide default idle watchdog.
Types ¶
type APIError ¶
APIError is a non-2xx provider response. Status 0 with a non-nil underlying error form is not used; network failures surface as plain wrapped errors. Message is the provider's own error text when parseable. API keys are never included in any error text.
type AttemptEvent ¶ added in v1.1.0
type AttemptEvent struct {
Provider string
Operation string // "chat" | "stream" | "embed" | "speak" | "transcribe" | "models" | "count_tokens"
Model string // "" for models
Attempt int // 1-based, per entry-point call
Status int // HTTP status; 0 on transport error
Err error // nil on success; API keys are never included
Latency time.Duration // request start -> response headers received
RetryDelay time.Duration // delay before the next attempt; 0 when final
RequestID string // provider request id header, when reported
}
AttemptEvent describes one HTTP attempt of an SDK call. See WithAttemptObserver.
type ChatClient ¶
type ChatClient struct {
// contains filtered or unexported fields
}
ChatClient runs chat completions against one provider+model pair. Learn-once fallbacks are shared by every client from the same Provider; request timeouts are per client. It is safe for concurrent use.
func (*ChatClient) Call ¶
func (c *ChatClient) Call(ctx context.Context, req *ChatRequest) (*ChatResult, error)
Call runs a buffered chat completion.
func (*ChatClient) CallStream ¶
func (c *ChatClient) CallStream(ctx context.Context, req *ChatRequest, onDelta func(Delta) error) (*ChatResult, error)
CallStream runs a streaming chat completion. onDelta receives canonical fragments; returning an error from it aborts generation — CallStream then returns the partial result alongside *StreamAbortedError.
func (*ChatClient) CountTokens ¶ added in v1.1.0
func (c *ChatClient) CountTokens(ctx context.Context, req *ChatRequest) (int, error)
CountTokens reports the input token count of req using the provider's counting endpoint (Anthropic /v1/messages/count_tokens, Gemini :countTokens). OpenAI-format providers, including the Responses API, have none: *UnsupportedError (errors.Is ErrUnsupported), with no network call and never an estimate. Request validation matches Call; Extra and sampling parameters are ignored. Retries, the whole-call timeout, headers and the attempt observer (operation "count_tokens") behave as for chat.
func (*ChatClient) ProviderID ¶
func (c *ChatClient) ProviderID() string
ProviderID returns the bound provider's id.
func (*ChatClient) RequestTimeout ¶
func (c *ChatClient) RequestTimeout() time.Duration
RequestTimeout reports the per-request timeout (streaming wall-clock deadline).
func (*ChatClient) SetRequestTimeout ¶
func (c *ChatClient) SetRequestTimeout(d time.Duration)
SetRequestTimeout adjusts the per-request timeout. Call before the first request.
type ChatRequest ¶
type ChatRequest struct {
// Headers are per-request HTTP headers (stub; behaviour lands with SL03).
Headers map[string]string
Model string
Messages []Message
System []SystemBlock
Tools []ToolDef
Thinking string
ThinkingBudget int
MaxTokens int
Temperature float64
TopP float64
// TopK limits sampling to the K most likely tokens (0 = provider
// default; negative is a ConfigError). Anthropic top_k (omitted when
// thinking is on) and Gemini generationConfig.topK; OpenAI chat and
// Responses ignore it.
TopK int
Stop []string
// ToolChoice constrains tool use (nil = provider default). It requires
// Tools.
ToolChoice *ToolChoice
// ParallelToolCalls, when set with Tools, allows or forbids several
// tool calls in one turn (OpenAI parallel_tool_calls, Anthropic
// disable_parallel_tool_use). Gemini has no equivalent and ignores it.
ParallelToolCalls *bool
// ResponseFormat requests JSON output (nil = free text).
ResponseFormat *ResponseFormat
// Seed asks for deterministic sampling where supported (OpenAI chat
// completions, Gemini). Anthropic and the Responses API ignore it.
Seed *int
// Extra holds provider-specific top-level body fields merged into the
// request JSON after the SDK's own (so they override them): an escape
// hatch for parameters the canonical shape does not model yet. The merge
// is shallow: an object value replaces the SDK's whole object, except
// Gemini generationConfig, whose keys are set one by one over the SDK's
// (nested objects such as thinkingConfig are replaced whole). Keys go to whichever endpoint the request
// is sent to, including /responses when an OpenAI request is diverted
// there. Reserved (ConfigError): "stream" (use Call vs CallStream) and
// the keys the SDK validates — model, messages/contents/input,
// system/systemInstruction/instructions, tools.
Extra map[string]any
}
ChatRequest is the canonical request. Model is filled from the ChatClient when empty. Thinking accepts "", "enabled", "disabled", "low", "medium", "high", "max" and is translated per provider format. Temperature and TopP: 0 means use the provider default (field omitted); use a negative value to explicitly send 0. Stop maps to each provider's stop-sequence field (the OpenAI Responses API has none, so it is not sent on requests diverted there).
type ChatResult ¶
type ChatResult struct {
Content string
ReasoningContent string
// ThinkingSignature authenticates ReasoningContent (Anthropic extended
// thinking). Consumers must carry it back on the next assistant Message
// for tool loops to stay valid.
ThinkingSignature string
// ThinkingBlocks lists every signed reasoning segment in response order
// (Anthropic thinking/redacted_thinking, Responses reasoning items).
ThinkingBlocks []ThinkingBlock
ToolCalls []ToolCall
FinishReason string
Usage Usage
// Refusal is the provider's refusal text when it signals one; empty
// otherwise. Content is never altered.
Refusal string
// Meta is provider-reported response metadata (zero when unreported).
Meta ResponseMeta
}
ChatResult is the canonical response for both buffered and streaming calls.
func (*ChatResult) AssistantMessage ¶ added in v1.0.0
func (r *ChatResult) AssistantMessage() Message
AssistantMessage returns the assistant turn to append to the conversation before the next request. It carries every replay field (reasoning, signatures, thinking blocks, tool calls) so tool loops stay valid on every format. Slices are copied.
type ConfigError ¶
type ConfigError struct{ Msg string }
ConfigError reports SDK misuse: unknown provider id, provider without an API key, or malformed options. Never retryable.
func (*ConfigError) Error ¶
func (e *ConfigError) Error() string
type ContentPart ¶ added in v0.4.0
type ContentPart struct {
Type ContentPartType
Text string
Image []byte
MIMEType string
// Data is the document payload (ContentPartDocument).
Data []byte
// Name is the optional document filename.
Name string
// URL is the remote image location (ContentPartImageURL).
URL string
}
ContentPart is one ordered text or inline-image segment in a message. Image data is raw bytes and is base64-encoded only by provider serializers. A part must contain exactly one supported payload: Text, or Image with a valid MIMEType. Inline images are bounded by MaxImageBytes.
func DocumentPart ¶ added in v1.1.0
func DocumentPart(mimeType string, data []byte, name string) ContentPart
DocumentPart creates an inline document part (application/pdf or text/plain). Name is an optional filename.
func ImagePart ¶ added in v0.4.0
func ImagePart(mimeType string, data []byte) ContentPart
ImagePart creates an ordered inline-image content part.
func ImageURLPart ¶ added in v1.1.0
func ImageURLPart(url string) ContentPart
ImageURLPart creates a part referencing a remote http(s) image. The SDK never fetches it; the provider does. Gemini needs a MIME type: use ImageURLPartMIME or WithMIME.
func ImageURLPartMIME ¶ added in v1.1.0
func ImageURLPartMIME(url, mime string) ContentPart
ImageURLPartMIME creates a remote-image part with a MIME type hint.
func TextPart ¶ added in v0.4.0
func TextPart(text string) ContentPart
TextPart creates an ordered text content part.
func (ContentPart) WithMIME ¶ added in v1.1.0
func (p ContentPart) WithMIME(mime string) ContentPart
WithMIME returns a copy of p with MIMEType set.
type ContentPartType ¶ added in v0.4.0
type ContentPartType string
ContentPartType identifies the payload carried by ContentPart.
const ( ContentPartText ContentPartType = "text" ContentPartImage ContentPartType = "image" // ContentPartDocument is an inline document (PDF or plain text). ContentPartDocument ContentPartType = "document" // ContentPartImageURL is an image the provider fetches by URL. ContentPartImageURL ContentPartType = "image_url" )
type Delta ¶
Delta is one streamed fragment. Text is the fragment for this event, not accumulated output.
type DeltaKind ¶
type DeltaKind int
DeltaKind discriminates streamed fragments.
const ( // DeltaReasoning is a thinking/reasoning fragment, usually before content. DeltaReasoning DeltaKind = iota // DeltaContent is an assistant text fragment. DeltaContent // DeltaToolArgs is a tool-call argument fragment (partial JSON). ToolIndex // and, on the first fragment of a call, ToolID/ToolName identify the call. DeltaToolArgs // DeltaRefusal is a refusal-text fragment carried in Delta.Text. DeltaRefusal )
type EmbedRequest ¶ added in v1.0.0
type EmbedRequest struct {
Headers map[string]string // per-request headers (SL03)
Inputs []string // required: one or more non-empty texts
Dimensions int // optional output dimensionality (0 = model default)
TaskType string // Gemini taskType, e.g. RETRIEVAL_QUERY (OpenAI-format ignores it)
}
EmbedRequest describes one embeddings call.
type EmbedResult ¶ added in v1.0.0
type EmbedResult struct {
Embeddings [][]float64
Model string
Usage Usage // PromptTokens when the provider reports it
Meta ResponseMeta
}
EmbedResult carries one vector per input, in input order.
type Format ¶
type Format string
Format identifies a wire protocol family. The SDK translates the single canonical request shape into exactly one of these.
const ( // FormatOpenAI is the OpenAI chat-completions protocol — also spoken by // DeepSeek, Z.ai (GLM), Kimi/Moonshot and most self-hosted gateways. FormatOpenAI Format = "openai" // FormatAnthropic is the Anthropic Messages API. FormatAnthropic Format = "anthropic" // FormatGemini is the Google Gemini generateContent API. FormatGemini Format = "gemini" )
type LearnEvent ¶ added in v0.7.0
LearnEvent reports one learn-once constraint engaging. Status is the HTTP status that triggered the fallback (0 when none applied, e.g. a 2xx response that arrived without SSE framing); Message carries the provider error text when one exists.
type LearnKind ¶ added in v0.7.0
type LearnKind string
LearnKind identifies which learn-once fallback flag engaged.
const ( // LearnBuffered: streaming was rejected by the provider (explicit // "streaming not supported" 400) or a 2xx response arrived without an // SSE content type. Every later generation on this provider is buffered. LearnBuffered LearnKind = "buffered" // LearnResponses: the provider requires /v1/responses for // reasoning_effort combined with function tools. LearnResponses LearnKind = "responses" // LearnNoneEffort: the provider rejected reasoning_effort with tools; // effort is pinned to "none" for later requests. LearnNoneEffort LearnKind = "none_effort" // LearnDropStreamOpts: the provider rejected stream_options; the field // is omitted from later requests. LearnDropStreamOpts LearnKind = "drop_stream_options" )
type ListOption ¶
type ListOption func(*listOpts)
ListOption tweaks ListModels.
func ForceRefresh ¶
func ForceRefresh() ListOption
ForceRefresh bypasses the model cache for this call and refreshes it.
type Message ¶
type Message struct {
Role Role
Content string
// Parts optionally carries ordered text and inline-image content. When
// empty, Content retains the legacy plain-text representation.
Parts []ContentPart
ReasoningContent string
// ThinkingSignature authenticates ReasoningContent for providers that
// require thinking to be replayed verbatim (Anthropic signature, OpenAI
// Responses encrypted_content).
ThinkingSignature string
// ThinkingBlocks replays every signed reasoning segment of a previous
// assistant turn, in order. When set it supersedes ReasoningContent and
// ThinkingSignature on Anthropic and Responses requests.
ThinkingBlocks []ThinkingBlock
ToolCalls []ToolCall
ToolCallID string
ToolName string
// IsError marks a RoleTool result as a failed tool execution (Anthropic
// tool_result is_error; Gemini functionResponse {"error": …}). OpenAI
// formats have no flag: describe the failure in Content.
IsError bool
// Cache marks this user message for Anthropic prompt caching
// (cache_control ephemeral on the text block). Ignored on other
// formats and on non-user roles.
Cache bool
}
Message is one canonical chat message. For RoleTool messages, ToolCallID and ToolName identify the call being answered and Content carries the tool result. ReasoningContent is provider-reported thinking text (deepseek-reasoner, anthropic thinking, gemini thoughts). The SDK replays it where a provider requires conversation continuity: OpenAI-format assistant messages echo it as reasoning_content (DeepSeek tool loops; with tools present the key is echoed even when empty, because DeepSeek rejects a missing key with 400), and Anthropic re-serializes a signed thinking block as the first content block when ThinkingSignature is also set.
type Model ¶
type Model struct {
ID string
DisplayName string
CreatedAt time.Time
ContextWindow int // input token limit, 0 = unknown
MaxOutputTokens int // 0 = unknown
Capabilities []string
}
Model is one accessible model, as reported by a provider's models endpoint. Fields the provider does not report stay zero — the SDK never guesses.
type Option ¶
type Option func(*SDK)
Option configures an SDK at construction time.
func FromEnv ¶
func FromEnv() Option
FromEnv resolves <PROVIDER>_API_KEY (aliases included, primary first) and optional <PROVIDER>_BASE_URL overrides via os.LookupEnv. Providers without a key stay registered but unauthenticated.
func WithAttemptObserver ¶ added in v1.1.0
func WithAttemptObserver(fn func(AttemptEvent)) Option
WithAttemptObserver registers fn to be called once per HTTP attempt on every entry point (chat, stream, embed, speak, transcribe, models), including retries and learn-once immediate retries. fn runs on the request goroutine: keep it fast and never call back into the SDK. fn must not panic, but a panic is recovered and discarded. A nil fn disables observation.
func WithEnv ¶
WithEnv resolves API keys and base-URL overrides from the environment using lookup — the testable twin of FromEnv.
func WithLearnObserver ¶ added in v1.0.0
func WithLearnObserver(fn func(LearnEvent)) Option
WithLearnObserver registers a callback fired once per learn-once fallback engaged by this SDK's providers. When set it replaces the process-wide SetLearnObserver callback for this SDK. It runs on the request goroutine: keep it fast and never call back into the SDK from it.
func WithModelCacheTTL ¶
WithModelCacheTTL sets the ListModels cache TTL. Zero disables caching — every call hits the provider's models endpoint.
func WithProvider ¶
func WithProvider(id string, popts ...ProviderOption) Option
WithProvider configures one provider — a built-in id (override) or a new custom provider (requires WithFormat and WithBaseURL, plus auth).
func WithRequestTimeout ¶
WithRequestTimeout sets the default per-request timeout for all chat clients (default 120s). Streaming calls use it as the hard wall-clock deadline.
func WithRetryPolicy ¶ added in v1.0.0
func WithRetryPolicy(p RetryPolicy) Option
WithRetryPolicy replaces the default retry ladder (8 attempts, 30s backoff cap) for every chat, speech, transcription and embedding call made through this SDK. Zero fields keep their defaults.
func WithStreamIdleTimeout ¶ added in v1.0.0
WithStreamIdleTimeout sets the SSE idle watchdog for this SDK's streams, taking precedence over the process-wide SetStreamIdleTimeout default. Non-positive values are ignored.
func WithTransport ¶
func WithTransport(rt http.RoundTripper) Option
WithTransport replaces the SDK's pooled HTTP transport (tests, proxies).
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider is one configured endpoint with dynamic model discovery.
func (*Provider) Authenticated ¶
Authenticated reports whether an API key resolved.
func (*Provider) Config ¶
func (p *Provider) Config() ProviderConfig
Config returns the provider configuration. The copy carries the API key: treat it as a secret; it is never logged by the SDK itself.
func (*Provider) GoString ¶ added in v1.0.0
GoString implements fmt.GoStringer with the same redaction as String.
func (*Provider) ListModels ¶
ListModels discovers the models accessible to this provider's account, on the fly — no static tables. Results are cached per SDK instance for the cache TTL (default 5 min; WithModelCacheTTL(0) disables). Fields the provider does not report stay zero.
type ProviderConfig ¶
type ProviderConfig struct {
ID string
Format Format
BaseURL string
APIKey string
// EnvKeys lists environment variable names to consult, primary first
// (aliases after). Resolution stops at the first non-empty value.
EnvKeys []string
Quirks Quirks
// Headers are extra HTTP headers sent on every request to this
// provider (OpenRouter attribution, OpenAI-Organization, anthropic-beta,
// api-key gateways, …). They are applied last, so they can override the
// SDK's own headers; an empty value removes that header. Like APIKey,
// header values are never logged or included in String().
Headers map[string]string
}
ProviderConfig fully describes one inference endpoint. APIKey is resolved from EnvKeys (primary first) or set explicitly; it is never included in error text, logs, or String() output.
func (ProviderConfig) GoString ¶ added in v1.0.0
func (c ProviderConfig) GoString() string
GoString keeps %#v from printing the API key and header values.
func (ProviderConfig) String ¶
func (c ProviderConfig) String() string
type ProviderOption ¶
type ProviderOption func(*ProviderConfig)
ProviderOption configures one provider entry.
func WithAPIKey ¶
func WithAPIKey(key string) ProviderOption
WithAPIKey sets an explicit API key (overrides env).
func WithBaseURL ¶
func WithBaseURL(url string) ProviderOption
WithBaseURL overrides the provider's default base URL.
func WithEnvKeys ¶
func WithEnvKeys(keys ...string) ProviderOption
WithEnvKeys sets env var names consulted for this provider's key, primary first.
func WithFormat ¶
func WithFormat(f Format) ProviderOption
WithFormat sets the wire format (custom providers).
func WithHeaders ¶ added in v1.0.0
func WithHeaders(h map[string]string) ProviderOption
WithHeaders adds extra HTTP headers to every request this provider makes (merged over earlier WithHeaders calls). An empty value removes that header, e.g. {"Authorization": ""} for gateways that authenticate with an api-key header instead. The map is copied.
func WithQuirks ¶
func WithQuirks(q Quirks) ProviderOption
WithQuirks overrides protocol quirk flags.
type Quirks ¶
type Quirks struct {
// ThinkingObject: provider accepts the Anthropic-style top-level
// "thinking" object (Anthropic, DeepSeek, Z.ai GLM).
ThinkingObject bool
// EchoReasoningWithTools: the provider requires the reasoning_content key
// on every replayed assistant message while the request carries tools,
// even for turns where it returned no reasoning itself. DeepSeek's
// thinking mode rejects a bare omission with 400 "The reasoning_content
// in the thinking mode must be passed back to the API" — which poisons
// every later request in the tool loop, not just the offending one.
EchoReasoningWithTools bool
// AnthropicToolJSON: the Anthropic-format endpoint lacks
// output_config.format, so JSON ResponseFormat is emulated with a forced
// synthetic tool folded back into Content. Off for the built-in anthropic
// provider (native structured outputs).
AnthropicToolJSON bool
// ReasoningEffort: provider accepts reasoning_effort (OpenAI, GLM-5.3+).
ReasoningEffort bool
// IncludeReasoning: the provider needs the explicit reasoning opt-in
// flag on OpenAI-format requests. OpenRouter documents the legacy
// `include_reasoning: true` as the equivalent of the canonical
// `reasoning: {}` object; without some opt-in such gateways return no
// reasoning at all.
IncludeReasoning bool
// ForceThinking lists model-name prefixes that reject
// thinking.type=disabled outright (GLM-5.3 always reasons; the
// documented migration is {type: enabled} + reasoning_effort "low").
ForceThinking []string
// NoJSONSchema: the provider accepts response_format json_object but
// rejects json_schema (DeepSeek); a json_schema ResponseFormat fails fast
// with a ConfigError instead of a provider 400.
NoJSONSchema bool
// AnthropicVersion is the anthropic-version header value required by
// FormatAnthropic providers ("2023-06-01").
AnthropicVersion string
}
Quirks carries per-provider protocol deviations, resolved at registration time. This replaces odek's URL-sniffing: a custom base URL gets its format's default quirks unless the caller overrides them explicitly.
type RateLimit ¶ added in v1.1.0
type RateLimit struct {
Reported bool // true when at least one field below was parsed
RemainingRequests int
RemainingTokens int
ResetRequests time.Duration // time until the window resets; 0 = unknown
ResetTokens time.Duration
}
RateLimit is the provider-reported rate-limit headroom.
type RateLimitError ¶
RateLimitError is the final failure after persistent 429 responses. RetryAfter carries the last observed Retry-After hint (0 if none).
func (*RateLimitError) Error ¶
func (e *RateLimitError) Error() string
func (*RateLimitError) Unwrap ¶
func (e *RateLimitError) Unwrap() error
Unwrap exposes the embedded APIError so errors.As(err, *APIError) reaches Status/Retryable without a type switch.
type ResponseFormat ¶ added in v1.0.0
type ResponseFormat struct {
Type ResponseFormatType
Name string
Schema json.RawMessage
Strict bool
}
ResponseFormat requests structured output. Name (json_schema only; default "response", letters/digits/_/- up to 64) names the schema; Schema is a JSON Schema object; Strict asks OpenAI-format providers for strict schema adherence.
type ResponseFormatType ¶ added in v1.0.0
type ResponseFormatType string
ResponseFormatType selects the shape of the model's text output.
const ( ResponseText ResponseFormatType = "text" // free text (default; nothing sent) ResponseJSONObject ResponseFormatType = "json_object" // any JSON object ResponseJSONSchema ResponseFormatType = "json_schema" // JSON matching Schema )
type ResponseMeta ¶ added in v1.1.0
type ResponseMeta struct {
ID string // provider response/message id; "" when not reported
Model string // model reported by the provider; "" when not reported
RequestID string // from the x-request-id family of response headers
RateLimit RateLimit
}
ResponseMeta is provider-reported response metadata. Every field is provider-reported or zero — never guessed.
type RetryPolicy ¶ added in v1.0.0
type RetryPolicy struct {
// MaxAttempts is the total number of attempts including the first
// (default 8). 1 disables retries.
MaxAttempts int
// MaxBackoff caps one exponential backoff step (default 30s).
// Retry-After from the server is honored independently (capped at
// 120s).
MaxBackoff time.Duration
}
RetryPolicy configures the retry ladder shared by chat (buffered and streaming), speech, transcription and embeddings. Zero fields keep the defaults.
type SDK ¶
type SDK struct {
// contains filtered or unexported fields
}
SDK is a configured set of LLM endpoints. Build one with New and options:
sdk := llm.New(llm.FromEnv())
Providers are authenticated when an API key resolves from the environment or is set explicitly; every authenticated provider is usable concurrently through the SDK's shared connection pool.
func New ¶
New builds an SDK with the built-in provider registry and the given options. Options apply in order; later WithProvider calls for the same id override earlier ones.
func (*SDK) Chat ¶
func (s *SDK) Chat(providerID, model string) (*ChatClient, error)
Chat returns a chat client bound to a provider and model.
func (*SDK) Embed ¶ added in v1.0.0
func (s *SDK) Embed(ctx context.Context, providerID, model string, req EmbedRequest) (*EmbedResult, error)
Embed computes embeddings for req.Inputs with the named provider and model.
func (*SDK) Provider ¶
Provider looks a provider up by id. Unknown ids return a ConfigError; known-but-unauthenticated providers are returned with Authenticated() == false.
func (*SDK) Speak ¶ added in v0.5.0
func (s *SDK) Speak(ctx context.Context, providerID, model string, req SpeakRequest) (*SpeakResult, error)
Speak synthesizes speech with the named provider and model. Buffered only — no streaming in v1 — with the same retry ladder as chat.
func (*SDK) Transcribe ¶ added in v0.6.0
func (s *SDK) Transcribe(ctx context.Context, providerID, model string, req TranscribeRequest) (*TranscribeResult, error)
Transcribe converts speech to text with the named provider and model. Buffered only, with the same retry ladder as chat.
type SpeakRequest ¶ added in v0.5.0
type SpeakRequest struct {
Headers map[string]string // per-request headers (SL03)
Text string // required, non-empty
Voice string // provider voice id (no fallback tables — invariant #5)
Format string // container for OpenAI-compat providers ("mp3" default)
Speed float64 // optional playback speed, OpenAI-compat only (0 = omit)
}
SpeakRequest describes one text-to-speech conversion.
type SpeakResult ¶ added in v0.5.0
type SpeakResult struct {
Audio []byte // raw audio bytes as the provider returned them
Model string // model that produced the audio
MIMEType string // provider-declared (or well-known default) MIME type
Meta ResponseMeta
}
SpeakResult carries the synthesized audio.
type StreamAbortedError ¶
type StreamAbortedError struct{ Reason error }
StreamAbortedError is returned by CallStream when the delta handler aborted generation. CallStream also returns the partial ChatResult assembled so far alongside this error.
func (*StreamAbortedError) Error ¶
func (e *StreamAbortedError) Error() string
func (*StreamAbortedError) Unwrap ¶
func (e *StreamAbortedError) Unwrap() error
type SystemBlock ¶
SystemBlock is one system-prompt segment. On Anthropic each block maps to a system text block (Cache marks it for prompt caching); on OpenAI-format providers blocks concatenate into a leading system message; on Gemini they become systemInstruction parts.
type ThinkingBlock ¶ added in v1.0.0
type ThinkingBlock struct {
Text string // reasoning text (summary on Responses); empty when redacted
Signature string // Anthropic signature / Responses encrypted_content
Redacted string // Anthropic redacted_thinking data; Text and Signature stay empty
}
ThinkingBlock is one provider-signed reasoning segment that must be replayed verbatim and in order: an Anthropic thinking or redacted_thinking block, or an OpenAI Responses reasoning item.
type ToolCall ¶
type ToolCall struct {
ID string
Name string
Arguments string // JSON object as a string
// Signature is the provider's opaque per-call reasoning signature
// (Gemini thoughtSignature). Replay it unchanged; Gemini 3 rejects a
// function-call turn whose signature is missing.
Signature string
}
ToolCall is a model-requested tool invocation.
type ToolChoice ¶ added in v1.0.0
type ToolChoice struct {
Mode ToolChoiceMode
Name string
}
ToolChoice constrains tool use. Name is set only with ToolChoiceTool and must match one of the request's Tools.
func ToolChoiceNamed ¶ added in v1.0.0
func ToolChoiceNamed(name string) *ToolChoice
ToolChoiceNamed forces a call to the named tool.
type ToolChoiceMode ¶ added in v1.0.0
type ToolChoiceMode string
ToolChoiceMode selects how the model may use the request's tools.
const ( ToolChoiceAuto ToolChoiceMode = "auto" // the model decides (provider default) ToolChoiceNone ToolChoiceMode = "none" // never call a tool ToolChoiceRequired ToolChoiceMode = "required" // call at least one tool ToolChoiceTool ToolChoiceMode = "tool" // call the tool named by Name )
type ToolDef ¶
type ToolDef struct {
Name string
Description string
Parameters json.RawMessage
// Cache marks this tool for Anthropic prompt caching. Set it on the
// last tool so the catalog is a stable prefix independent of later
// system rows. OpenAI-format serializers ignore it.
Cache bool
}
ToolDef declares a callable tool. Parameters is a JSON Schema object (json.RawMessage so callers can pass through marshaled schemas verbatim).
type ToolHandler ¶ added in v1.1.0
ToolHandler executes one model-requested tool call and returns its result.
type ToolLoopOptions ¶ added in v1.1.0
type ToolLoopOptions struct {
// MaxTurns is the number of chat calls allowed; <=0 means 8.
MaxTurns int
// Parallel runs one turn's handlers concurrently. Results are still
// appended in call order.
Parallel bool
// OnTurn is called after each chat call (1-based), before handlers run.
// Panics in it are not recovered.
OnTurn func(turn int, res *ChatResult)
}
ToolLoopOptions tunes RunTools.
type TranscribeRequest ¶ added in v0.6.0
type TranscribeRequest struct {
Headers map[string]string // per-request headers (SL03)
Audio []byte // required, non-empty: raw audio bytes (caller-owned)
Filename string // file part name for the audio (default "audio.wav")
MIMEType string // optional content type for the audio part
Language string // optional ISO-639-1 hint, OpenAI-compat only
Prompt string // optional conditioning text, OpenAI-compat only
Format string // optional response_format (provider default when empty)
}
TranscribeRequest describes one speech-to-text conversion.
type TranscribeResult ¶ added in v0.6.0
type TranscribeResult struct {
Text string
Model string
Language string
DurationSec float64
Meta ResponseMeta
}
TranscribeResult carries the recognized text. Fields the provider omits stay zero — unknown data stays unknown (invariant #5).
type UnsupportedError ¶ added in v1.1.0
type UnsupportedError struct{ Op, Provider, Reason string }
UnsupportedError reports an unsupported operation; it unwraps to ErrUnsupported.
func (*UnsupportedError) Error ¶ added in v1.1.0
func (e *UnsupportedError) Error() string
func (*UnsupportedError) Unwrap ¶ added in v1.1.0
func (e *UnsupportedError) Unwrap() error
type Usage ¶
type Usage struct {
PromptTokens int
CompletionTokens int
ReasoningTokens int
CacheReadTokens int
CacheCreationTokens int
CachedTokens int
CacheReported bool
}
Usage reports token accounting with one meaning on every format. Fields the provider does not report stay 0.
- PromptTokens is uncached input only. Inclusive provider counts (OpenAI cached_tokens, DeepSeek prompt_cache_hit_tokens, Gemini cachedContentTokenCount) are subtracted and moved to CacheReadTokens; Anthropic reports cache volumes exclusively and is left alone.
- CacheReadTokens / CacheCreationTokens are cache reads and cache writes. A DeepSeek cache miss is ordinary input (billed as such), not a write, so it stays in PromptTokens.
- CompletionTokens counts every generated token, reasoning included (Gemini's candidatesTokenCount excludes thoughts, so they are added); ReasoningTokens is the reasoning subset.
- CachedTokens is the raw provider-reported cached count, kept for diagnostics. It is already included in CacheReadTokens — never add it.
Sum with InputTokens and TotalTokens rather than by hand.
func (Usage) InputTokens ¶ added in v1.0.0
InputTokens is the full input volume: uncached prompt plus cache reads and writes.
func (Usage) TotalTokens ¶ added in v1.0.0
TotalTokens is InputTokens plus CompletionTokens.