Documentation
¶
Overview ¶
Package providers wires LLM providers declared in baifo.yaml into ready to use ADK models.
The package exposes a Registry that owns every configured provider and returns model.LLM instances on demand. Each concrete provider lives in its own subpackage (openai, anthropic, gemini, ...).
Index ¶
- Constants
- Variables
- func ExpandSecrets(in []config.ProviderEntry, store *secrets.Store) ([]config.ProviderEntry, error)
- func Register(providerType string, b Builder)
- func RegisterAuthFlow(providerType string, f AuthFlow)
- func Reset()
- func StripStaleThoughts(req *model.LLMRequest) *model.LLMRequest
- func SupportedTypes() []string
- func WrapStripThoughts(m model.LLM) model.LLM
- type AuthFlow
- type Builder
- type ModelOptions
- type Option
- type Registry
- type RetryPolicy
- type Spec
Constants ¶
const ( StrategyBackoff = "backoff" StrategyRetryAfter = "retry-after" )
Retry strategy identifiers. They mirror config.RetryStrategy* but are duplicated here so the providers package stays free of a config import.
Variables ¶
var ErrUnknownProvider = errors.New("unknown provider")
ErrUnknownProvider is returned when a config references a provider name that was not declared in providers[].
var ErrUnsupportedAuth = errors.New("unsupported auth mode for provider type")
ErrUnsupportedAuth is returned when a provider entry sets auth to a mode its type cannot honour, e.g. auth: oauth on a type with no registered OAuth flow (only anthropic has one today). Failing at load time avoids the silent trap where a provider ignores the field and the user believes OAuth is active when it is not.
var ErrUnsupportedType = errors.New("unsupported provider type")
ErrUnsupportedType is returned when a provider entry uses a type that has no Builder registered.
Functions ¶
func ExpandSecrets ¶
func ExpandSecrets(in []config.ProviderEntry, store *secrets.Store) ([]config.ProviderEntry, error)
ExpandSecrets walks every ProviderEntry and substitutes ${secret:NAME} placeholders in api_key and headers values with the real secret. Returns a NEW slice; the input is not mutated so a concurrent ReloadFromDisk does not observe a half-expanded list.
Behaviour mirrors the MCP header expansion:
- store nil (no secrets store wired): placeholders pass through.
- secret missing: returns a clear error so boot fails loudly instead of silently sending an empty api_key to the provider.
- no placeholder: value copied verbatim.
Why provider config gets early expansion, unlike tool args: providers are built ONCE at boot (and again on reload), and the resulting model client caches the api key inside its SDK. There's no per-call hook where we could expand later, so the substitution has to happen here, at config-load time.
func Register ¶
Register associates a provider type with its Builder. Called from the init() of each provider subpackage. Panics on duplicate registration to surface conflicts at startup rather than at runtime.
func RegisterAuthFlow ¶
RegisterAuthFlow associates a provider type with its interactive login. Panics on duplicate registration, mirroring Register.
func Reset ¶
func Reset()
Reset clears every registration. Intended for tests that need to install a fake builder without colliding with init() registrations from other packages. NOT safe for production code paths.
func StripStaleThoughts ¶
func StripStaleThoughts(req *model.LLMRequest) *model.LLMRequest
StripStaleThoughts returns a copy of req whose history carries no stale reasoning parts (genai.Part with Thought=true).
Sessions in baifo are persisted provider-agnostically, so after a mid-session provider switch the history can hold reasoning parts whose opaque ThoughtSignature was minted by another provider. Gemini rejects those with a 400 "Corrupted thought signature", and even a provider's own reasoning from closed turns is dead weight that gets billed again as plain input tokens on every call.
Reasoning from the turn still in flight must survive, because providers such as Anthropic and Gemini expect it echoed back while a tool-call loop is running. The ADK may persist one logical model turn as several consecutive model-role contents (text in one event, the function call in the next), so the active turn is defined as the contiguous trailing run of model-role and tool-response contents after the last real user message. Thoughts on model-role contents inside that run are kept; thoughts anywhere else, including ones carried under a user role (foreign agent context), are dropped.
req itself is never mutated: contents are shallow-cloned with their parts filtered, so the ADK session history stays intact.
func SupportedTypes ¶
func SupportedTypes() []string
SupportedTypes returns the list of provider types that have a Builder registered. Useful for diagnostics and for the TUI /providers command.
func WrapStripThoughts ¶
WrapStripThoughts decorates m so every GenerateContent call first runs StripStaleThoughts on the request. Every provider build function must wrap its model with this before returning it: the strip is a correctness fix (stale cross-provider thought signatures are rejected with a 400), so it lives on the inevitable path — the model itself — rather than in a runner plugin that not every consumer goes through. See .agents/TODO.md for the deferred plugin-migration discussion.
Types ¶
type AuthFlow ¶
AuthFlow runs an interactive login for a provider type and persists the resulting credentials for the provider named name under dir.
func AuthFlowFor ¶
AuthFlowFor returns the auth flow registered for a provider type, or false when the type has none.
type Builder ¶
type Builder func(ctx context.Context, spec Spec, modelName string, opts ModelOptions) (model.LLM, error)
Builder constructs a model.LLM from a Spec, a model name and the per-build ModelOptions. Each provider type (openai, anthropic, gemini, ...) registers its Builder via Register so the Registry can dispatch by Spec.Type.
type ModelOptions ¶
type ModelOptions struct {
// ThinkingBudgetTokens, when > 0, asks the provider to enable
// extended reasoning with this many output tokens reserved for the
// model's internal thinking. Anthropic requires it >= 1024 and
// strictly less than the response's max output tokens.
ThinkingBudgetTokens int
// ThinkingEffort, when non-empty, sets the reasoning effort level
// for models that use the newer effort-based reasoning API
// (e.g. Anthropic Claude Opus 4.5+).
ThinkingEffort string
// ThinkingMode selects which Anthropic reasoning API shape to use:
// "enabled" (classic budget-based, for Claude 3.7 / Sonnet 4 / Opus 4)
// or "adaptive" (effort-based, for Opus 4.5+). Empty lets the adapter
// deduce it from the other fields. Only the anthropic adapter reads it.
ThinkingMode string
// MaxOutputTokens, when > 0, sets the response token ceiling. The
// anthropic adapter needs this to exceed ThinkingBudgetTokens; the
// builder sets it accordingly when a budget is requested.
MaxOutputTokens int
}
ModelOptions carries per-build knobs that are NOT part of the static provider Spec; they vary per agent, not per configured provider. Today the only knob is reasoning, and only the anthropic adapter consumes it (its extended-thinking budget is a construction-time setting). The openai and gemini adapters take reasoning request-side via GenerateContentConfig instead, so they ignore these fields.
type Option ¶
type Option func(*Registry)
Option configures a Registry at construction. Kept variadic so the many existing NewRegistry call sites (including throw-away validation registries) need no changes when they don't care about retries.
func WithConfigDir ¶
WithConfigDir sets the configuration directory used to locate auxiliary files (like OAuth tokens) for the providers.
func WithRetry ¶
func WithRetry(p RetryPolicy) Option
WithRetry installs the retry policy applied to every model the Registry hands out. A disabled policy (MaxAttempts <= 1) is a no-op: models are returned unwrapped.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry resolves provider names declared in baifo.yaml into ready model.LLM instances. It validates the configuration eagerly (so bad entries surface at startup) and caches built models per (name, modelID) pair to avoid repeated client construction.
func NewRegistry ¶
func NewRegistry(entries []config.ProviderEntry, opts ...Option) (*Registry, error)
NewRegistry builds a Registry from the providers section of the config. Each entry is validated: a non-empty Name and a Type that has a Builder registered. Optional Options configure cross-cutting behaviour such as the retry policy.
func (*Registry) Model ¶
func (r *Registry) Model(ctx context.Context, providerName, modelID string, opts ...ModelOptions) (model.LLM, error)
Model returns (and caches) the model.LLM for the given provider name and model id. Optional ModelOptions carry per-agent knobs (today: reasoning budget) that some providers apply at construction time; they are folded into the cache key so distinct options yield distinct cached clients. The Builder of the provider type is invoked the first time; subsequent calls with the same arguments return the cached instance.
func (*Registry) Names ¶
Names returns the list of registered provider names, suitable for listing in the TUI.
func (*Registry) StreamingEnabled ¶
StreamingEnabled reports whether agents backed by the named provider should run in streaming mode. Unknown providers default to true so a misconfigured reference never silently disables streaming (which Anthropic requires for long turns); the unknown-name error surfaces later at Model resolution.
type RetryPolicy ¶
type RetryPolicy struct {
// MaxAttempts is the TOTAL number of tries including the first.
// A value <= 1 disables retrying (the decorator is a no-op wrap).
MaxAttempts int
// InitialBackoff is the wait before the first retry. Each later
// wait multiplies by Multiplier, capped at MaxBackoff.
InitialBackoff time.Duration
// MaxBackoff caps a single wait so exponential growth stays bounded.
MaxBackoff time.Duration
// Multiplier is the exponential growth factor (> 1 to grow).
Multiplier float64
// Jitter randomises each wait in [delay/2, delay] to avoid
// synchronised retry storms across concurrent workers.
Jitter bool
// Strategy selects how each wait is computed. StrategyBackoff (the
// default / empty value) always uses the exponential backoff above.
// StrategyRetryAfter honours a provider-supplied Retry-After header
// when present and falls back to the backoff otherwise.
Strategy string
}
RetryPolicy is the resolved, ready-to-use retry configuration the decorator consumes. The config package owns the user-facing YAML shape and its defaults; this struct is the already-validated form so the providers package has no dependency on config parsing.
type Spec ¶
type Spec struct {
Name string
Type string
URL string
Auth string
APIKey string
Headers map[string]string
// ConfigDir is the path to the baifo configuration directory. It is
// injected at Registry creation time so providers can locate auxiliary
// files (like OAuth tokens). Empty when not provided.
ConfigDir string
// Streaming is the resolved streaming setting for this provider
// (default ON). When false, agents backed by this provider run in
// non-streaming mode for endpoints that do not support SSE.
Streaming bool
}
Spec is the minimal description of a provider derived from config.ProviderEntry. Validation (required fields, supported type) lives in the provider implementation, not in the loader.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package allproviders blank-imports every built-in provider so that init() side effects register them with the providers registry.
|
Package allproviders blank-imports every built-in provider so that init() side effects register them with the providers registry. |
|
Package anthropic adapts the adk-utils-go Anthropic model into baifo's provider registry.
|
Package anthropic adapts the adk-utils-go Anthropic model into baifo's provider registry. |
|
Package gemini adapts the upstream google.golang.org/adk Gemini model into baifo's provider registry.
|
Package gemini adapts the upstream google.golang.org/adk Gemini model into baifo's provider registry. |
|
Package openai adapts the adk-utils-go OpenAI model into baifo's provider registry.
|
Package openai adapts the adk-utils-go OpenAI model into baifo's provider registry. |