Help us shape the future of Go! Take the 2026 Go Developer Survey

provider

package
v1.2.20 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

This file implements the Anthropic Messages API streaming decoder (US-008). It is a stateful Decoder (see transport.go) that translates Anthropic SSE event payloads into the provider-agnostic AssistantMessageEvent set, accumulating a partial AssistantMessage as deltas arrive.

Anthropic streams a fixed event sequence:

message_start        → seeds id/model and initial usage (input tokens)
content_block_start  → opens a text / thinking / tool_use block at an index
content_block_delta  → text_delta / thinking_delta / signature_delta /
                       input_json_delta append to the open block
content_block_stop   → closes the block (tool_use JSON is parsed here)
message_delta        → carries the final stop_reason and output-token usage
message_stop         → terminal; the accumulated message is emitted as done
error                → a runtime error payload → terminal error event

Per the dual failure model (FR-13) the decoder never panics: malformed payloads and Anthropic `error` events are surfaced as a returned error (which the transport turns into a terminal StreamErrorEvent) rather than crashing.

This file implements credential resolution (US-012): API key lookup from environment variables and a config file (per provider), plus an OAuth token source that refreshes short-lived tokens on expiry. The resolver satisfies the LoopConfig.GetAPIKey shape (func(ctx, provider) string) so the agent loop can obtain a fresh key per request.

Security (FR: secret values are not written to logs): secret values are never logged or embedded in error messages. Errors and String()/redaction helpers reference credentials by key name / provider only.

This file holds the shared disk-cache plumbing for the two model catalogs golder keeps under the golder home directory:

  • the models.dev reasoning ladder catalog (reasoning_catalog.go)
  • the provider model list (modelcatalog.go)

Both are JSON files written opportunistically and read best-effort: a missing, unreadable, or corrupt file never surfaces an error, it just means "no cache". Both share one freshness window (catalogTTL) so a long-lived install refreshes its provider metadata about once a day in total.

This file implements the credential-hygiene primitives from issue #568 (ported from dsh's credential handling):

  • ScrubEnv: derive a child-process environment with credential-shaped variables removed, so a spawned process (sub-agent, plugin) does not inherit secrets it does not need. Mirrors dsh's stdio bridge, which "deliberately removes ambient variables whose names usually identify credentials and all DSH_* variables"; golder scrubs credential-shaped names and all GOLDER_* internals. Explicit allow entries are re-injected last, so a parent can still hand a child exactly the one key it needs.
  • CredentialFile: a named-credential store at $GOLDER_HOME/.credentials.yaml (mode 0600). Config files reference a credential by NAME; the literal secret lives only here, mirroring dsh's write-only keys + "settings retain only its credential reference".

This file persists the provider model lists fetched by /model (issue #566), so the list is requested about once per catalogTTL (24h) per provider and endpoint instead of once per session. It shares the file plumbing and the TTL with the reasoning ladder catalog (catalogcache.go), keeping the two caches one mechanism.

A cache key is the provider name plus the effective base URL, so a --base-url override (or a self-hosted endpoint) never serves another endpoint's list. Entries are independent: each carries its own checked_at.

This file implements online model discovery (issue #566, ported from dsh's "Fetch available models"): asking an endpoint which models it serves, so the /model catalog and switching can follow a provider's real lineup.

Both wire formats golder speaks expose a plain GET for it:

  • OpenAI-compatible (and Responses) gateways: GET {base}/models
  • Anthropic Messages: GET {base}/v1/models

and both answer {"data":[{"id": ...}]}, so one parser serves both. Auth follows each protocol's request convention (Bearer for OpenAI-compatible, x-api-key + anthropic-version for Anthropic). A local endpoint that needs no key is fine: the key is sent only when non-empty.

This file implements the OpenAI-compatible streaming decoder (US-009): a stateful Decoder (see transport.go) for the OpenAI Chat Completions SSE stream, which is also the wire format spoken by most third-party gateways (OpenRouter, Groq, together, local servers, …). Selecting the base URL is a transport concern (NewRequest builds the *http.Request), so this decoder is base-URL agnostic and reused across every OpenAI-compatible provider.

OpenAI streams a sequence of chat.completion.chunk objects:

{"choices":[{"delta":{"role":"assistant"}}]}                 → first chunk
{"choices":[{"delta":{"content":"Hel"}}]}                    → text delta
{"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_1",
    "function":{"name":"f","arguments":"{\"a\":"}}}]}]}       → tool-call delta
{"choices":[{"finish_reason":"tool_calls"}]}                 → stop reason
{"usage":{"prompt_tokens":10,"completion_tokens":5}}         → final usage
[DONE]                                                        → transport-level

Per the dual failure model (FR-13) the decoder never panics: malformed payloads surface as a returned error which the transport turns into a terminal StreamErrorEvent.

This file defines the provider streaming abstraction (US-003/US-007 base): the StreamFn contract, the per-delta AssistantMessageEvent set, and the AssistantMessageEventStream (a specialization of EventStream) that a provider pushes deltas onto while yielding a final AssistantMessage.

Contract (FR-13): a StreamFn never expresses a request failure by returning an error. Runtime failures are encoded as an error event plus a terminal assistant message (stopReason=error/aborted + errorMessage). The returned error is reserved for the earliest "could not even build the stream" case.

This file defines the unified Provider interface and its dual failure model (US-007). A Provider turns a CompletionRequest into a stream of AssistantMessageEvents. Failures follow the same contract as StreamFn (FR-13):

  • "cannot even build the stream" (bad config, missing model) → returned error.
  • any runtime failure once streaming has begun → a terminal StreamErrorEvent carrying an assistant message with stopReason=error/aborted, after which the stream is closed. It is never a returned error.

Model carries provider-agnostic capability metadata so the loop and UI can reason about a model without knowing the concrete provider.

This file implements the first concrete Providers (US-013): Bedrock, OpenRouter, and Ollama. Each satisfies the Provider interface by building an *http.Request and delegating to the shared transport (StreamRequest) with a provider-appropriate Decoder — no bespoke HTTP/SSE handling per provider.

Two backing driver shapes cover all three:

  • openAICompatDriver — POSTs to {baseURL}/chat/completions in the OpenAI Chat Completions wire format, decoded by OpenAIDecoder. OpenRouter and Ollama are instances of it; it is the generic OpenAI-compatible layer, reusable for any gateway (Groq, together, local servers, …).
  • anthropicCompatDriver — POSTs the Anthropic Messages wire format, decoded by AnthropicDecoder. Bedrock rides this: Anthropic-on-Bedrock speaks the Messages API, so the decoder is reused wholesale.

Failures follow the dual failure model (FR-13): only the earliest "cannot build the stream" case (missing key, bad request construction) is a returned error; every runtime failure rides the stream as a terminal error event, which StreamRequest already guarantees.

Security (US-012 / US-026): API keys are referenced by provider name in any error; secret values are never logged or embedded in error text.

This file owns everything about reasoning effort: the mapping from golder's unified ThinkingLevel onto each gateway's wire effort value, and the models.dev catalog that says which levels a model actually advertises.

The mapping half is pure: no IO, no global state. The catalog half fetches and caches models.dev's metadata, because the gateways golder talks to do not expose it themselves (opencode-go's /models returns only id/object/created/owned_by). They live together because they are one decision: WireReasoningEffort reads the catalog and falls back to the hand-maintained ladders in this same file.

golder's unified ladder (off < minimal < low < medium < high < xhigh < max) is wider than any single gateway's. Every wire driver — Chat Completions (`reasoning_effort`) and Responses (`reasoning.effort`) — calls WireReasoningEffort, so a model is mapped identically no matter which protocol carries its request.

A model's accepted ladder resolves in layers:

  1. the models.dev catalog entry for (provider, model), when it has one,
  2. the provider's own ReasoningLadders (registry.go), for gateway quirks,
  3. sharedReasoningLadders, for model families that behave the same on every gateway,
  4. defaultReasoningLadder, the conservative common denominator.

A requested level above the ladder's top clamps down to its top rung (a model with a real max keeps it); a level below its bottom clamps up to the bottom rung. off/empty omits the field so the gateway keeps its own default, and a catalog model marked as non-reasoning gets no effort field at all.

The catalog is one JSON document keyed by provider. golder extracts only the providers it can route to (the built-in registry, using each spec's ModelsDevID) and keeps just each model's reasoning flag plus its effort-type values, so a full fetch is a few tens of KB on disk. It is cached at $GOLDER_HOME/reasoning-catalog.json (or ~/.golder/reasoning-catalog.json) with the shared catalogTTL: a session never blocks on the network for it, and the fetch happens at most once per TTL instead of once per session. Refreshes are conditional (If-None-Match), so an unchanged upstream catalog answers 304 and costs no body. Missing, stale, or corrupt caches degrade silently to the hand-maintained ladders.

This file defines the central provider registry (US-001): a single source of truth for every built-in provider — its name, the environment variables that carry its API key (in precedence order), its default base URL, wire protocol, auth scheme, extra headers, provider-specific base-URL override env vars, and its model-facing behavior (which model-id prefixes infer it, which models.dev entry describes it, and any reasoning-effort ladder quirks its gateway has).

Adding or supporting a provider should be ONE entry in providerRegistry: transport metadata first, then the optional behavior fields. Anything a provider does not declare falls back to the shared defaults — ResolveProvider builds the driver from the spec, InferProviderFromModel reads ModelPrefixes, the reasoning catalog reads ModelsDevID, and WireReasoningEffort consults ReasoningLadders before the model-family table in reasoning.go. The only per-provider code left is special-auth composition (special_auth.go), which needs request signing rather than metadata.

The registry is deliberately additive and self-contained: later nodes wire auth resolution (auth.go), the --provider flag (main.go), base_url overrides, and per-provider construction (providers.go) to READ from it. This node only introduces the data + a lookup, so it does not change existing behavior.

Data source: the PRD "Technical Considerations" table (tasks/prd-provider-env-parity.md), derived from pi's env-api-keys.ts and the per-provider *.models.ts files.

Security: the registry holds only env var NAMES, never secret values. Keys are resolved from the environment at request time (see auth.go) and never logged.

This file implements the OpenAI Responses API driver (US-003, #539), the backing for --protocol openai/resp_api. Unlike openAICompatDriver (which hand-rolls the Chat Completions wire format), this driver speaks the Responses API (POST {base_url}/responses) via the official github.com/openai/openai-go SDK.

This milestone covers streaming text: a plain prompt in, assistant text out, consumed from the Responses SSE stream and mapped into golder's AssistantMessage the same way OpenAIDecoder does (API/Provider tags, Usage, ResponseID/ResponseModel, StopReason=end_turn). Tools (#541) and images/reasoning (#542) layer on later.

Failure model (FR-13): only the earliest "cannot build the stream" case (missing API key) is a returned error. Every runtime failure — including a non-2xx from the endpoint — rides the returned stream as a terminal StreamErrorEvent, matching the chat driver's observable behavior.

Base URL + auth: the SDK client is pointed at the resolved base_url and given the resolved key via option.WithBaseURL / option.WithAPIKey rather than reading the environment, so ResolveBaseURL precedence is preserved. A custom *http.Client may be injected (option.WithHTTPClient) for tests.

This file implements parameter validation and endpoint construction for the "special auth" providers (US-007 / FR-12): Azure OpenAI, Amazon Bedrock, Google Vertex, and Cloudflare (Workers AI + AI Gateway). Unlike the generic bearer/x-api-key providers, each of these composes its endpoint from several environment variables and/or needs a non-standard credential path, so a dedicated resolver validates the required parameters and builds the concrete base URL before handing off to the shared OpenAI-/Anthropic-compatible driver.

Scope note (PRD Non-Goals): AWS SigV4 request signing is NOT implemented. Bedrock supports only the AWS_BEARER_TOKEN_BEDROCK bearer path; other AWS credential sources (AWS_PROFILE, AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY) are only *detected* so that a clear, actionable error is returned instead of an opaque auth failure.

Security: this file reads env var NAMES and composes URLs from non-secret parameters (region, resource name, account id, …). Secret values (API keys, bearer tokens) are never logged or embedded in error text — errors name the absent env var, never a value.

This file implements the shared transport driver (US-007 support): a provider-agnostic layer that turns an HTTP request into a stream of StreamEvents. Each provider degenerates to a stateful Decoder; the transport owns HTTP + SSE line parsing + retry + dual watchdogs + the dual failure model.

The design mirrors pi's providerio: the transport never returns a runtime failure as a Go error once streaming has begun — it rides the stream as a terminal StreamErrorEvent. Only the earliest "cannot build the stream" case (bad request construction) is a returned error.

Index

Constants

View Source
const (
	StreamEventStart    = "start"
	StreamEventText     = "text"
	StreamEventThinking = "thinking"
	StreamEventToolCall = "toolcall"
	StreamEventDone     = "done"
	StreamEventError    = "error"
)

AssistantMessageEvent kinds.

View Source
const (
	ProtocolOpenAI    = "openai"
	ProtocolAnthropic = "anthropic"
)

Protocol values.

View Source
const (
	AuthBearer  = "bearer"
	AuthXAPIKey = "x-api-key"
	AuthAWS     = "aws"
	AuthAzure   = "azure"
	AuthSpecial = "special"
)

AuthScheme values.

View Source
const ExtraSessionID = "session_id"

ExtraSessionID is the StreamConfig.Extra key carrying the golder session id (see RunConfig.SessionID) from the loop to the provider drivers. It is opaque to every other layer.

View Source
const OpencodeSessionHeader = "x-opencode-session"

OpencodeSessionHeader carries the client session id to opencode's Zen endpoints. The Go endpoint (modelList "lite") rejects requests without it (400 MissingSessionID: "cannot be routed efficiently"), using the value for sticky upstream routing. The full Zen endpoint tolerates its absence.

View Source
const ProtocolOpenAIResponses = "openai/resp_api"

ProtocolOpenAIResponses is the canonical selector for the OpenAI Responses API wire format (POST {base_url}/responses). It is distinct from ProtocolOpenAI (Chat Completions) so ResolveProvider can route to the SDK-based Responses driver.

Variables

View Source
var ProcessSessionID = sync.OnceValue(func() string {
	return fmt.Sprintf("golder-%d-%d", os.Getpid(), time.Now().Unix())
})

ProcessSessionID returns a stable per-process session identity for callers that need the opencode sticky-routing header but have no real session id (e.g. the risk reviewer, or a process-isolated sub-agent). Sharing one affinity bucket with the process's runs is the desired routing behavior there anyway.

Functions

func APIKeyEnvHint

func APIKeyEnvHint(name string) string

APIKeyEnvHint returns a human-readable hint naming the environment variable(s) that satisfy the provider's credential check, for embedding in missing-key errors (issue #564: "openrouter: missing API key" alone leaves the user guessing what to set). Unknown providers fall back to the generic <PROVIDER>_API_KEY convention.

func BaseURLEnvVars

func BaseURLEnvVars(spec ProviderSpec) []string

BaseURLEnvVars lists endpoint override variables in resolution order.

func CachedModelCatalog

func CachedModelCatalog(key string) (ids []string, fresh bool)

CachedModelCatalog returns the cached list for key and whether it is still fresh (younger than catalogTTL). A nil list with fresh=false means "no usable cache": either nothing was ever stored, or the stored entry is stale (callers may still use the list as a fallback when a refetch fails).

func CanonicalizeModel

func CanonicalizeModel(model string) string

CanonicalizeModel maps a bare built-in provider name (e.g. "zai", "DEEPSEEK") to that provider's DefaultModel (e.g. "glm-4.7"), so a user who types the provider alone gets its default model instead of the literal id being sent to OpenRouter (issue #564). Any other id — a real model id, an unknown name, a "provider/model" routed id — is returned unchanged.

The mapping is exact-match on the trimmed, lowercased id against the built-in provider registry; a model id that happens to equal a provider name is not a meaningful OpenRouter id, so the ambiguity is harmless.

func ContextWindowFor added in v1.2.13

func ContextWindowFor(providerName, model string) int

ContextWindowFor returns the context-token budget models.dev lists for (providerName, model), or 0 when it has no value. Lookups read the reasoning catalog's in-memory/disk copy, so they never block on the network; callers fall back to cli.DefaultContextWindow on 0 (unknown provider, custom base-URL model, or a catalog that has not been fetched yet). A case-only mismatch between the gateway's model id and models.dev's brand casing still matches.

func CredentialFilePath

func CredentialFilePath() string

CredentialFilePath returns the named-credential file location: $GOLDER_HOME/.credentials.yaml when GOLDER_HOME is set, else ~/.golder/.credentials.yaml. An unresolvable home returns "".

func CredentialFilePermissionsWarn

func CredentialFilePermissionsWarn(path string) bool

CredentialFilePermissionsWarn reports whether the credential file's mode is more permissive than 0600 (group/other access), so the caller can warn without failing.

func DisplayURL

func DisplayURL(raw string) string

DisplayURL omits credentials, query strings and fragments from UI output.

func EffectiveProtocol

func EffectiveProtocol(providerName, model, protocol string) string

EffectiveProtocol reports the wire protocol a run will actually speak for the given provider selection, model id, and explicit --protocol flag. It mirrors ResolveNamedProvider's effective-protocol computation without its driver construction or errors, so the startup banner can display the real wire format (a multi-protocol gateway such as opencode-go shows openai/resp_api for a deepseek model and openai/chat for anything else). An explicit protocol wins when it is valid and compatible with the provider; otherwise the provider's registry default applies, upgraded to Responses for Responses-family models on OpenAI-wired providers. Unknown providers, unknown protocols, and incompatible pairs yield "" so the caller falls back to its placeholder instead of displaying a misleading value.

func EnsureReasoningCatalog

func EnsureReasoningCatalog(ctx context.Context) error

EnsureReasoningCatalog refreshes the catalog when the in-memory or on-disk copy is missing or older than the TTL, and loads it into memory. Concurrent callers serialize; the loser of the race re-checks freshness and returns without a second fetch. Network errors leave the existing cache untouched.

func EnvCredentialVar

func EnvCredentialVar(provider string) (string, bool)

EnvCredentialVar reports the name of the environment variable holding a credential for a provider, without returning its value. Candidates come from the provider registry (EnvVars, in precedence order), then the generic <PROVIDER>_API_KEY fallback. A variable that is set but empty does not count. The name is safe to display; the value never leaves this call.

func FetchProviderModels

func FetchProviderModels(ctx context.Context, providerName, baseURL, protocol, apiKey string) ([]string, error)

FetchProviderModels uses the same provider identity for routing as chat requests, even when baseURL points at a user-configured relay.

func FetchRemoteModels

func FetchRemoteModels(ctx context.Context, baseURL, protocol, apiKey string) ([]string, error)

FetchRemoteModels queries baseURL (with protocol selecting the URL shape and auth header convention) for its model catalog and returns the ids sorted and deduplicated. protocol may be empty, a canonical value ("openai", "openai/resp_api", "anthropic"), or a raw alias — it is normalized first, and the Responses protocol shares the Chat Completions /models path. An empty apiKey sends no auth header (local gateways like Ollama need none).

func InferProviderFromModel

func InferProviderFromModel(model string) (string, bool)

InferProviderFromModel guesses the built-in provider name that serves a given model id, based on the id's well-known name prefix (e.g. "claude-*" → anthropic, "deepseek-*" → deepseek). This is what lets `golder -m claude-opus-4-8` reach the Anthropic API without the user spelling out the provider or its wire protocol (ResolveProvider step 3).

The mapping is deliberately conservative: only names that unambiguously identify a single built-in provider are inferred. Ambiguous families served by many gateways (notably "llama-*", "qwq-*", "gemma-*", "mixtral-*") are NOT inferred — they return ok=false so the caller falls through to its existing default (OpenRouter). The prefixes live in the registry (ProviderSpec.ModelPrefixes), in registry order; a test pins that no two providers claim overlapping prefixes, so the order only matters for determinism. Matching is case-insensitive.

func IsProviderName

func IsProviderName(id string) bool

IsProviderName reports whether id is exactly a built-in provider name (case-insensitive, surrounding whitespace tolerated).

func IsSpecialAuthProvider

func IsSpecialAuthProvider(spec ProviderSpec) bool

IsSpecialAuthProvider reports whether a provider spec needs the bespoke endpoint-construction / credential-validation handled by ResolveSpecialProvider, rather than the generic driver wiring. It matches the multi-parameter auth schemes (azure/aws/special) and the two Cloudflare providers (which keep a standard auth scheme but still compose their endpoint from env vars).

func KnownReasoningLevels

func KnownReasoningLevels(providerName, model string) []agentcore.ThinkingLevel

KnownReasoningLevels returns the catalog's explicit effort list for a model, or nil when the catalog has nothing definitive (unknown model, no effort option, or no reasoning). Unlike ReasoningLevels it never falls back to the family table, so callers can tell "models.dev says so" apart from "unknown" when annotating UI.

func LoadCredentialFile

func LoadCredentialFile(path string) (map[string]string, error)

LoadCredentialFile reads a named-credential file (YAML mapping of name → secret). The file is expected to be mode 0600; callers can use CredentialFilePermissionsWarn to check.

func ModelCatalogCacheKey

func ModelCatalogCacheKey(providerName, baseURL string) string

ModelCatalogCacheKey identifies a model catalog by provider and effective endpoint. The base URL is normalized (trimmed, trailing slash dropped) so the same endpoint always maps to one entry.

func NeedsProxy

func NeedsProxy(name, rawURL string) bool

func NewOpenAIResponsesProvider

func NewOpenAIResponsesProvider(name, baseURL string, models []Model) *responsesDriver

NewOpenAIResponsesProvider builds a Responses API provider targeting baseURL. baseURL must be the fully resolved endpoint (e.g. https://api.openai.com/v1); the SDK appends the /responses path.

func NormalizeProtocol

func NormalizeProtocol(raw string) (string, error)

NormalizeProtocol maps a raw --protocol / protocol value to a canonical internal selector. Input is trimmed and lower-cased before matching. An empty value stays empty (unset → model-id heuristics). Recognized values normalize to ProtocolOpenAI, ProtocolOpenAIResponses, or ProtocolAnthropic. Any other value is an error naming the accepted set, so a typo surfaces to the caller for exit-code mapping instead of silently falling through.

func PreferResponses

func PreferResponses(modelID string) bool

PreferResponses reports whether modelID belongs to a Responses-preferred family. It is a pure naming heuristic (mirroring opencode's own shouldUseResponsesApi): an explicit --protocol always wins over it, and it never applies to Anthropic-wired providers (ResolveNamedProvider guards that), so a Claude id can never be routed to the Responses driver here.

func ProtocolLabel

func ProtocolLabel(raw string) string

ProtocolLabel maps a raw --protocol value to the human-facing label shown in the startup banner's Protocol row, so the displayed wire format matches what golder actually speaks. It differs from NormalizeProtocol in one deliberate way: the bare "openai" input is surfaced as "openai/chat", making the Chat Completions variant explicit rather than ambiguous. "openai/resp_api" and "anthropic" pass through as themselves.

An empty input returns empty (the banner then falls back to "—" or the provider name, so an unset protocol on a named/inferred provider is not mislabeled). An unrecognized value returns the trimmed input verbatim — the label is presentation-only and must never fail; a real typo is already rejected upstream by NormalizeProtocol during resolution.

func ProviderNames

func ProviderNames() []string

ProviderNames returns all built-in provider names in registry order.

func ProxySettings

func ProxySettings() (config.ProxyConfig, error)

ProxySettings returns the user's provider selection, preserving the old OpenCode defaults until the first explicit selection is saved.

func ProxyStatus

func ProxyStatus(name, baseURL string) string

func ProxyURL

func ProxyURL() string

ProxyURL is an address only; setting it never enables every provider.

func ReasoningLevels

func ReasoningLevels(providerName, model string) []agentcore.ThinkingLevel

ReasoningLevels returns the selectable reasoning levels for a model: the models.dev catalog's effort list when it has one, else the family ladder. An empty (nil) return means the catalog says the model does not reason, so there is nothing to select and the wire sends no effort field. The result is never mutated; callers must not modify it.

func ResolveBaseURL

func ResolveBaseURL(spec ProviderSpec, flagBaseURL string, env func(string) string) string

ResolveBaseURL determines the effective base URL for a selected provider, applying the base_url override precedence (US-004 / FR-8, FR-9). The first non-empty source wins, in this order:

  1. flagBaseURL — the explicit --base-url/-u flag (highest).
  2. provider-specific base-url env var(s) from spec.BaseURLEnvVars, in the order the registry declares them (e.g. AZURE_OPENAI_BASE_URL).
  3. the generic <PROVIDER>_BASE_URL env var, where <PROVIDER> is the provider name uppercased with '-' rewritten to '_' (e.g. zai-coding-cn → ZAI_CODING_CN_BASE_URL).
  4. spec.DefaultBaseURL — the registry default (lowest).

Values are trimmed of surrounding whitespace before the non-empty check, so a whitespace-only env var does not shadow a lower-precedence source. Environment lookups go through the injected env func so callers control the environment.

func ResolveCredentialReference

func ResolveCredentialReference(path, name string) (string, error)

ResolveCredentialReference loads the named-credential file and returns the secret stored under name. Config files should carry the name only; the literal secret exists here alone.

func ScrubEnv

func ScrubEnv(parent []string, allow map[string]string) []string

ScrubEnv filters a parent environment for a child process: every credential-shaped variable and every GOLDER_* internal is dropped, then the allow entries are appended (so a parent can hand a child exactly the one credential it needs, under a name that would otherwise be scrubbed). Malformed parent entries (no "=") are dropped. The result is safe to assign to os/exec's cmd.Env.

func ScrubsCredentialVar

func ScrubsCredentialVar(name string) bool

ScrubsCredentialVar reports whether an environment variable name is credential-shaped and should be stripped from child-process environments. All GOLDER_* internals are scrubbed too (they are golder's own state, not a child's business); PATH/HOME and other ordinary names never match.

func SessionHeaderValue

func SessionHeaderValue(providerName string, extra map[string]any) string

SessionHeaderValue returns the x-opencode-session value for a request, or "" when no header should be sent. Only providers whose name starts with "opencode" ever send it (mirroring opencode's own client, which gates on model.providerID.startsWith("opencode")); every other provider ignores the session id entirely.

func StartBackgroundReasoningCatalogRefresh

func StartBackgroundReasoningCatalogRefresh()

StartBackgroundReasoningCatalogRefresh refreshes a stale or missing catalog in a goroutine so startup never blocks on models.dev. It returns immediately and swallows errors: a failed refresh leaves the previous cache in place.

func StoreModelCatalog

func StoreModelCatalog(key string, ids []string)

StoreModelCatalog records a freshly fetched list for key.

func ValidateProxyURL

func ValidateProxyURL(raw string) error

func WireReasoningEffort

func WireReasoningEffort(providerName, model string, level agentcore.ThinkingLevel) string

WireReasoningEffort maps the unified level onto the effort value to send for model. Both the Chat Completions and the Responses driver call it, so they agree by construction. providerName keys the models.dev catalog; an empty providerName simply skips the catalog lookup. An empty return means "omit the field": off/unset (or an unknown level) leaves the gateway's default behavior untouched, and a catalog model marked non-reasoning is never sent an effort at all.

func WithSessionExtra

func WithSessionExtra(extra map[string]any, sid string) map[string]any

WithSessionExtra returns a copy of extra carrying the session id under ExtraSessionID. It is a no-op (returns extra unchanged) when sid is empty or an id is already present, so an explicit value always wins and shared maps are never mutated.

Types

type APIKeyConfig

type APIKeyConfig struct {
	// Keys maps provider name → API key.
	Keys map[string]string `json:"keys"`
}

APIKeyConfig is the on-disk config-file shape: a map of provider name to API key. It is parsed from JSON and holds only static keys (OAuth lives in TokenSource). Values are secrets and must not be logged.

func LoadAPIKeyConfig

func LoadAPIKeyConfig(data []byte) (*APIKeyConfig, error)

LoadAPIKeyConfig parses an APIKeyConfig from JSON bytes (e.g. a config file).

func LoadAPIKeyConfigFile

func LoadAPIKeyConfigFile(path string) (*APIKeyConfig, error)

LoadAPIKeyConfigFile reads and parses an APIKeyConfig from a file path. A missing file is not an error — it returns an empty config so env/OAuth can still resolve keys.

type AnthropicDecoder

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

AnthropicDecoder is the stateful SSE decoder for the Anthropic Messages API. It implements the transport Decoder interface. It is not safe for concurrent use — the transport drives it from a single goroutine.

func NewAnthropicDecoder

func NewAnthropicDecoder() *AnthropicDecoder

NewAnthropicDecoder builds a fresh decoder for one streamed response.

func (*AnthropicDecoder) Decode

func (d *AnthropicDecoder) Decode(payload []byte) ([]StreamEvent, error)

Decode turns one Anthropic SSE data payload into zero or more StreamEvents.

func (*AnthropicDecoder) Finish

func (d *AnthropicDecoder) Finish() ([]StreamEvent, error)

Finish flushes a terminal done event if the stream ended without an explicit message_stop (e.g. a clean EOF mid-stream), so a partial response is still delivered rather than lost.

type AssistantMessageEvent

type AssistantMessageEvent interface {

	// EventKind returns the delta discriminant.
	EventKind() string
	// contains filtered or unexported methods
}

AssistantMessageEvent is the sealed interface for provider stream deltas. The loop dispatches on EventKind; the raw event is also surfaced to consumers via MessageUpdateEvent.AssistantMessageEvent.

type AssistantMessageEventStream

AssistantMessageEventStream is the provider-level stream: deltas of type AssistantMessageEvent with a final AssistantMessage result. isComplete fires on done/error; extractResult takes the terminal event's message.

func NewAssistantMessageEventStream

func NewAssistantMessageEventStream(buffer int) *AssistantMessageEventStream

NewAssistantMessageEventStream builds a provider stream wired with the done/error completion callbacks.

func StreamRequest

StreamRequest runs cfg as a transport stream. Per the dual failure model it returns an error only when the very first request cannot be built or the initial connection can never be established; every runtime failure once streaming begins rides the returned stream as a terminal StreamErrorEvent.

type CompletionRequest

type CompletionRequest struct {
	// Model is the provider-specific model id to complete against.
	Model string
	// Context is the shaped request (system prompt, LLM-bound messages, tools).
	Context LlmContext
	// Config carries per-request options (API key, thinking level, extras).
	Config StreamConfig
}

CompletionRequest is the provider-agnostic input to StreamCompletion: the model id, the shaped LLM context, and per-request options.

type CredentialStatus

type CredentialStatus struct {
	// OAuth reports a registered OAuth token source (auto-refreshing).
	OAuth bool
	// Override reports an explicit key (--api-key).
	Override bool
	// EnvVar names the environment variable holding a value, or "" for none.
	EnvVar string
	// Config reports a key in the config-file layer.
	Config bool
}

CredentialStatus reports which credential sources hold a value for a provider, in resolution priority order. It is a presence probe: no secret value is retrieved or returned, only which layer can supply one.

func (CredentialStatus) Available

func (s CredentialStatus) Available() bool

Available reports whether any layer can supply a credential.

func (CredentialStatus) Source

func (s CredentialStatus) Source() string

Source returns a short, value-free label for the highest-priority layer that holds a credential, or "" when none does. Env sources return the variable name (safe to display), never the value.

type CredentialStore

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

CredentialStore resolves API keys per provider from three layers, in order: OAuth token source (if registered), environment variable, config file. It implements the LoopConfig.GetAPIKey shape via GetAPIKey.

It is safe for concurrent use.

func NewCredentialStore

func NewCredentialStore(config *APIKeyConfig) *CredentialStore

NewCredentialStore builds a store over an optional config file. A nil config is treated as empty.

func (*CredentialStore) GetAPIKey

func (c *CredentialStore) GetAPIKey(ctx context.Context, provider string) string

GetAPIKey resolves the API key for a provider. Resolution order: OAuth token (refreshed on expiry) → explicit override (--api-key) → environment variable → config file. Returns "" when no credential is available. This matches LoopConfig.GetAPIKey so it can be assigned directly.

On OAuth refresh failure it falls back to override/env/config rather than returning a secret-bearing error; the empty return lets the caller fall back to a static key. It never logs secret values.

func (*CredentialStore) HasCredential

func (c *CredentialStore) HasCredential(ctx context.Context, provider string) bool

HasCredential reports whether any credential (OAuth/env/config) is available for a provider, without exposing the value.

func (*CredentialStore) RegisterOAuth

func (c *CredentialStore) RegisterOAuth(provider string, src *TokenSource)

RegisterOAuth registers an OAuth TokenSource for a provider. Once registered, GetAPIKey prefers the (auto-refreshing) OAuth token over static keys.

func (*CredentialStore) SetOverride

func (c *CredentialStore) SetOverride(provider, key string)

SetOverride records an explicit API key for a provider that wins over the environment variable and config file (but not a live OAuth token, which is auto-refreshed). It is the seam for a CLI --api-key flag: an empty key is ignored so a bare flag does not clobber env/config resolution.

func (*CredentialStore) Status

func (c *CredentialStore) Status(provider string) CredentialStatus

Status probes the credential layers for a provider without reading any secret into a caller-visible value. A nil store (or empty config) reports no config/override/OAuth source; the env layer is always probed.

type Decoder

type Decoder interface {
	// Decode turns one SSE data payload into zero or more StreamEvents. A
	// returned error is treated as a runtime stream failure (terminal error
	// event), never a panic.
	Decode(payload []byte) ([]StreamEvent, error)
	// Finish flushes any trailing state, returning a final batch of events.
	Finish() ([]StreamEvent, error)
}

Decoder is the per-provider stateful SSE payload decoder. The transport calls Decode for every complete SSE data payload (one event's worth of bytes) and Finish once the stream ends so the decoder can flush any buffered terminal event.

type LlmContext

type LlmContext struct {
	SystemPrompt string
	Messages     agentcore.MessageList
	Tools        []agentcore.AgentTool
}

LlmContext is the shaped request handed to a StreamFn: the system prompt, the LLM-bound messages (UI-only messages already filtered), and the tools.

type Model

type Model struct {
	// Provider is the provider name (e.g. "anthropic", "openai").
	Provider string `json:"provider"`
	// ID is the provider-specific model id (e.g. "claude-opus-4-8").
	ID string `json:"id"`
	// DisplayName is a human-friendly label; falls back to ID when empty.
	DisplayName string `json:"displayName,omitempty"`
	// ContextWindow is the maximum input+output token window, 0 if unknown.
	ContextWindow int `json:"contextWindow,omitempty"`
	// MaxOutputTokens is the max tokens the model may emit per response, 0 if
	// unknown.
	MaxOutputTokens int `json:"maxOutputTokens,omitempty"`
	// SupportsThinking reports whether the model exposes a reasoning/thinking
	// channel.
	SupportsThinking bool `json:"supportsThinking,omitempty"`
	// SupportsTools reports whether the model can call tools.
	SupportsTools bool `json:"supportsTools,omitempty"`
	// SupportsImages reports whether the model accepts image (multimodal) input.
	// When false, an image block in the request is reported as a hard error
	// rather than silently dropped, so the user learns the model cannot see it.
	SupportsImages bool `json:"supportsImages,omitempty"`
	// ThinkingLevels maps unified thinking levels to this model's wire values.
	// nil when the model does not support thinking (decision #10).
	ThinkingLevels agentcore.ThinkingLevelMap `json:"-"`
}

Model is provider-agnostic metadata describing a single model's identity and capabilities. Providers construct these; the loop/UI consume them.

type OAuthToken

type OAuthToken struct {
	AccessToken  string
	RefreshToken string
	Expiry       time.Time
}

OAuthToken is the result of an OAuth exchange/refresh. Values are secrets.

type OpenAIDecoder

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

OpenAIDecoder is the stateful SSE decoder for the OpenAI Chat Completions API and compatible gateways. It implements the transport Decoder interface and is not safe for concurrent use — the transport drives it from one goroutine.

func NewOpenAIDecoder

func NewOpenAIDecoder() *OpenAIDecoder

NewOpenAIDecoder builds a fresh decoder for one streamed response.

func (*OpenAIDecoder) Decode

func (d *OpenAIDecoder) Decode(payload []byte) ([]StreamEvent, error)

Decode turns one OpenAI SSE data payload into zero or more StreamEvents.

func (*OpenAIDecoder) Finish

func (d *OpenAIDecoder) Finish() ([]StreamEvent, error)

Finish flushes a terminal done event if the stream ended without an explicit terminator, so a partial response is still delivered rather than lost.

type Provider

type Provider interface {
	// Name returns the provider's identifier (matches Model.Provider).
	Name() string
	// Models lists the models this provider can serve.
	Models() []Model
	// StreamCompletion streams a completion for req. Per the dual failure model
	// it returns an error only for the earliest "cannot build the stream" case;
	// all runtime failures ride the returned stream as a terminal error event.
	StreamCompletion(ctx context.Context, req CompletionRequest) (*AssistantMessageEventStream, error)
}

Provider is the unified streaming interface implemented by every backend. It hides per-vendor differences behind a single AssistantMessageEvent stream.

func NewAnthropicProtocolProvider

func NewAnthropicProtocolProvider(name, baseURL, authScheme string, models []Model) Provider

NewAnthropicProtocolProvider builds a named Anthropic-Messages provider whose auth header follows the given registry AuthScheme. It is the target of an explicit --provider selection for any anthropic-protocol built-in (anthropic, minimax, minimax-cn, and — routed generically for now — bedrock/ cloudflare-ai-gateway): the driver identity is the provider's own name (so errors reference it), baseURL is the already-resolved endpoint (spec default or override), and authScheme selects the header shape (see anthropicAuthHeaderFor). Secret values are never logged.

func NewAnthropicProvider

func NewAnthropicProvider(baseURL string, models []Model) Provider

NewAnthropicProvider builds a provider that speaks the Anthropic Messages wire format directly (POST {baseURL}/messages), the target of an explicit --protocol=anthropic selection. baseURL defaults to the public Anthropic API when empty. Auth uses the Anthropic conventions: an x-api-key header plus the required anthropic-version header. The API key resolves by the "anthropic" provider name (ANTHROPIC_API_KEY / CLAUDE_API_KEY, or the --api-key override); secret values are never logged.

func NewBedrockProvider

func NewBedrockProvider(baseURL string, models []Model) Provider

NewBedrockProvider builds the Bedrock provider, reusing the Anthropic Messages decoder (Anthropic-on-Bedrock speaks the Messages wire format). baseURL defaults to a us-east-1 runtime endpoint when empty; real deployments pass the region-specific URL. Auth is a Bearer token (Bedrock API keys); SigV4 signing, when required, is layered by the caller's HTTP client.

func NewNvidiaProvider

func NewNvidiaProvider(baseURL string, models []Model) Provider

NewNvidiaProvider builds the NVIDIA provider (hosted NIM, OpenAI-compatible, Bearer auth). baseURL defaults to the public integrate endpoint when empty. The API key is resolved by the "nvidia" provider name (NVIDIA_API_KEY); secret values are never logged.

func NewOllamaProvider

func NewOllamaProvider(baseURL string, models []Model) Provider

NewOllamaProvider builds the Ollama provider (local, OpenAI-compatible, no auth). baseURL defaults to the local daemon when empty.

func NewOpenAICompatibleProvider

func NewOpenAICompatibleProvider(baseURL string, models []Model) Provider

NewOpenAICompatibleProvider builds a generic OpenAI-compatible provider for an arbitrary gateway reached by baseURL (Bearer auth). It is the target of an explicit --protocol=openai selection: unlike the preset constructors it has no default endpoint (baseURL must be supplied) and carries the neutral provider name "openai", so an API key resolves from OPENAI_API_KEY (or the --api-key override bound to that name). Secret values are never logged.

func NewOpenAICompatibleProviderAs

func NewOpenAICompatibleProviderAs(name, baseURL string, models []Model) Provider

NewOpenAICompatibleProviderAs builds the same generic driver carrying the given provider name, so errors and per-provider request decoration (e.g. the x-opencode-session header) reference the selected provider instead of the neutral "openai". ResolveNamedProvider uses it for every OpenAI-wired registry entry; API-key resolution still keys on the returned provider name upstream and is unaffected.

func NewOpenRouterProvider

func NewOpenRouterProvider(baseURL string, models []Model) Provider

NewOpenRouterProvider builds the OpenRouter provider — the reference OpenAI-compatible gateway. baseURL defaults to the public endpoint when empty.

func ResolveNamedProvider

func ResolveNamedProvider(name, model, baseURL, protocol string, env func(string) string) (Provider, string, error)

ResolveNamedProvider builds the driver for an explicit --provider selection. It looks the name up in the built-in registry and constructs the wire driver for the effective protocol: "openai" → an OpenAI-compatible (Bearer) driver, "anthropic" → an Anthropic-Messages driver, "openai/resp_api" → the Responses driver. An explicit --protocol wins; otherwise a Responses-family model id upgrades an OpenAI-wired provider (see PreferResponses). The base URL follows the override precedence in ResolveBaseURL (--base-url > provider-specific env > generic <PROVIDER>_BASE_URL > spec default). The returned provider-name string is the spec name, so downstream API-key resolution reads the provider's own env var (spec.EnvVars).

Special providers with bespoke auth (azure/bedrock/vertex/cloudflare — AuthScheme aws/azure/special, or the cloudflare-* names) are routed to ResolveSpecialProvider, which validates their required env vars and composes the concrete endpoint (node #188).

func ResolveProvider

func ResolveProvider(model, baseURL, protocol, providerName string, env func(string) string) (Provider, string, error)

ResolveProvider maps a model id to a built-in provider. An explicit --provider name wins over every other rule: it selects a built-in provider from the registry and constructs the matching wire driver (see ResolveNamedProvider). When provider is empty, protocol and model-id heuristics apply as before.

When protocol is a non-empty explicit selection ("openai" or "anthropic") it wins over the model-id heuristics: the provider is built directly for that wire format against baseURL, which is how a user points golder at a self-hosted or third-party endpoint and says which protocol it speaks. An "anthropic" selection with no baseURL targets the public Anthropic API.

When protocol is empty, resolution falls back to model-id heuristics:

  1. A bare provider name (e.g. "zai") is canonicalized to that provider's DefaultModel (see CanonicalizeModel, issue #564).
  2. An "ollama/" prefix (or a base URL on the Ollama port) → local Ollama.
  3. An "nvidia/" prefix → NVIDIA NIM (strips the prefix for the wire id).
  4. Model-name inference: with no --base-url, a well-known model-name prefix (e.g. "claude-*", "deepseek-*") selects its first-party built-in provider via ResolveNamedProvider (see InferProviderFromModel).
  5. A bare provider name with no DefaultModel is an error naming the mismatch, rather than a silent OpenRouter fallback that fails later with a misleading missing-API-key error.
  6. Everything else → OpenRouter, the reference OpenAI-compatible gateway.

An unknown protocol value is an error, surfaced to the caller for exit-code mapping rather than silently falling back.

func ResolveSpecialProvider

func ResolveSpecialProvider(spec ProviderSpec, model, flagBaseURL string, env func(string) string) (Provider, error)

ResolveSpecialProvider validates the required parameters for a special-auth provider and constructs the matching wire driver against the composed base URL. flagBaseURL is the explicit --base-url override (highest precedence, wins over any composed default); env resolves environment variables (os.Getenv in production, a fake in tests). A missing required parameter yields an error naming exactly which env var is absent; no network request is made here.

type ProviderSpec

type ProviderSpec struct {
	// Name is the canonical provider name (e.g. "deepseek", "zai-coding-cn").
	Name string
	// EnvVars lists the environment variables checked (in precedence order) for
	// this provider's API key. The first non-empty value wins.
	EnvVars []string
	// DefaultBaseURL is the provider's default API endpoint. It may be a template
	// (containing placeholders like {region}) for providers whose endpoint is
	// composed from additional parameters (Bedrock, Vertex, Cloudflare, Azure).
	DefaultBaseURL string
	// Protocol is the wire protocol the provider speaks: "openai" (OpenAI Chat
	// Completions) or "anthropic" (Anthropic Messages).
	Protocol string
	// AuthScheme names how credentials are attached: "bearer", "x-api-key",
	// "aws", "azure", or "special".
	AuthScheme string
	// ExtraHeaders are provider-specific headers attached to every request (may
	// be nil).
	ExtraHeaders map[string]string
	// BaseURLEnvVars lists provider-specific base-URL override environment
	// variables (e.g. AZURE_OPENAI_BASE_URL), in precedence order. May be empty;
	// the generic <PROVIDER>_BASE_URL convention is handled by callers.
	BaseURLEnvVars []string
	// ForceProxy is the legacy default selection before the user saves /proxy
	// settings. An explicit provider list replaces these defaults completely.
	ForceProxy bool

	// ModelsDevID is this provider's key in models.dev's api.json (used by the
	// reasoning ladder catalog). Empty means Name.
	ModelsDevID string

	// ModelPrefixes lists the lowercase model-id prefixes that identify this
	// provider when only --model is given (see InferProviderFromModel). Keep
	// entries precise: a prefix that another provider's ids also carry would
	// shadow that provider, and registry order decides ties.
	ModelPrefixes []string

	// ReasoningLadders overrides the shared model-family ladder table
	// (reasoning.go) for this provider's models: the first entry whose Match is
	// a case-insensitive substring of the model id wins, before the family
	// table is consulted. Declare a ladder here only when the gateway diverges
	// from the model family's usual ladder (e.g. a gateway that rejects a level
	// the upstream API accepts). Empty means "no quirks".
	ReasoningLadders []ReasoningLadder

	// DefaultModel is the model id a bare provider name resolves to (e.g.
	// "zai" → "glm-4.7", see CanonicalizeModel), so /model <provider> and
	// --model <provider> select a real wire id instead of sending the literal
	// provider name. Empty means the provider has no default; a bare name is
	// then reported as a provider/model mismatch instead of guessing.
	DefaultModel string
}

ProviderSpec is the metadata describing one built-in provider. It is the single source of truth consumed by auth resolution, the --provider flag, base_url override handling, and per-provider wiring.

func LookupProviderSpec

func LookupProviderSpec(name string) (ProviderSpec, bool)

LookupProviderSpec returns the ProviderSpec for a provider name and whether it is a known built-in provider. The returned spec is a copy; mutating its slice or map fields is discouraged as they are shared with the registry.

func ProviderSpecs

func ProviderSpecs() []ProviderSpec

ProviderSpecs returns all built-in provider specs in registry (display) order. Callers must not mutate the returned specs' slice/map fields.

type ReasoningLadder

type ReasoningLadder struct {
	Match  string
	Ladder []agentcore.ThinkingLevel
}

ReasoningLadder is one model-family reasoning-effort rule: Match is a case-insensitive substring of the model id, Ladder the effort levels the gateway accepts for it, lowest to highest.

type StreamConfig

type StreamConfig struct {
	APIKey        string
	ThinkingLevel agentcore.ThinkingLevel
	// Extra holds provider-specific options; opaque to the loop.
	Extra map[string]any
}

StreamConfig carries per-request settings for a StreamFn.

type StreamDoneEvent

type StreamDoneEvent struct{ Message agentcore.AssistantMessage }

StreamDoneEvent is the terminal success event; Message is the final response.

func (StreamDoneEvent) EventKind

func (StreamDoneEvent) EventKind() string

type StreamErrorEvent

type StreamErrorEvent struct {
	Message agentcore.AssistantMessage
	Err     error
}

StreamErrorEvent is the terminal failure event; Message carries the terminal assistant message (stopReason=error/aborted + errorMessage).

func (StreamErrorEvent) EventKind

func (StreamErrorEvent) EventKind() string

type StreamEvent

type StreamEvent = AssistantMessageEvent

StreamEvent is the transport-level alias for AssistantMessageEvent. Decoders produce these; the transport forwards them onto the stream. (Decision #25: reuse AssistantMessageEvent rather than a parallel event type.)

type StreamFn

type StreamFn func(ctx context.Context, model string, llm LlmContext, cfg StreamConfig) (*AssistantMessageEventStream, error)

StreamFn produces a provider stream for a model + shaped context. Per the contract it returns an error only for early "cannot build the stream" failures; all runtime failures ride the returned stream as error events.

func StreamFnFromProvider

func StreamFnFromProvider(p Provider) StreamFn

StreamFnFromProvider adapts a Provider to the loop's StreamFn contract so a Provider can drive streamAssistantResponse directly. The two failure models are identical, so the adaptation is a straight delegation.

type StreamStartEvent

type StreamStartEvent struct{ Partial agentcore.AssistantMessage }

StreamStartEvent carries the initial (usually empty) partial message.

func (StreamStartEvent) EventKind

func (StreamStartEvent) EventKind() string

type StreamTextEvent

type StreamTextEvent struct{ Partial agentcore.AssistantMessage }

StreamTextEvent carries the partial message after a text delta.

func (StreamTextEvent) EventKind

func (StreamTextEvent) EventKind() string

type StreamThinkingEvent

type StreamThinkingEvent struct{ Partial agentcore.AssistantMessage }

StreamThinkingEvent carries the partial after a thinking delta.

func (StreamThinkingEvent) EventKind

func (StreamThinkingEvent) EventKind() string

type StreamToolCallEvent

type StreamToolCallEvent struct{ Partial agentcore.AssistantMessage }

StreamToolCallEvent carries the partial after a tool-call delta.

func (StreamToolCallEvent) EventKind

func (StreamToolCallEvent) EventKind() string

type TokenSource

type TokenSource struct {

	// Refresh exchanges the current refresh token for a new access token. It
	// returns the new access token, its expiry, and (optionally) a rotated
	// refresh token. Required for a TokenSource to refresh; nil means the token
	// is static and never refreshed.
	Refresh func(ctx context.Context, refreshToken string) (OAuthToken, error)
	// Now is injectable for testing; defaults to time.Now.
	Now func() time.Time
	// Leeway refreshes the token this long before its actual expiry to avoid
	// racing the boundary. Defaults to 30s.
	Leeway time.Duration
	// contains filtered or unexported fields
}

TokenSource yields an access token, refreshing it when expired. It models an OAuth credential whose access token is short-lived (FR-15: getApiKey refreshes on expiry). It is safe for concurrent use.

func NewTokenSource

func NewTokenSource(initial OAuthToken, refresh func(ctx context.Context, refreshToken string) (OAuthToken, error)) *TokenSource

NewTokenSource builds a TokenSource seeded with an initial token and a refresh function. refresh may be nil for a static (never-expiring) token.

func (*TokenSource) Token

func (t *TokenSource) Token(ctx context.Context) (string, error)

Token returns a valid access token, refreshing it when expired. It errors if a refresh is needed but no Refresh func is set, or if Refresh fails. The returned error never contains the token value.

type TransportConfig

type TransportConfig struct {
	// Client is the HTTP client; defaults to http.DefaultClient when nil.
	Client *http.Client
	// NewRequest builds a fresh *http.Request for each connection attempt. It is
	// called once per connect (initial + reconnects) so retries never replay a
	// consumed body — the caller owns idempotent request construction.
	NewRequest func(ctx context.Context) (*http.Request, error)
	// Decoder converts SSE payloads to StreamEvents (required).
	Decoder Decoder
	// MaxConnectRetries bounds connect-only retries (default 2).
	MaxConnectRetries int
}

TransportConfig configures a single StreamRequest run.

Jump to

Keyboard shortcuts

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