llmconfig

package
v1.0.0-beta.26 Latest Latest
Warning

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

Go to latest
Published: Nov 10, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
const (
	FieldAPIKey             = "api_key"
	FieldAuthToken          = "auth_token"
	FieldAuthTokenExpiresAt = "auth_token_expires_at" //nolint:gosec // a field name, not a credential
)

Credential fields: the path components of llmconfig://<provider>/<field>, named after the engine.LLMProviderConfig fields they fill.

View Source
const (
	ConfigFileName = userconfig.FileName
)

Variables

View Source
var (
	ConfigRoot = filepath.Join(xdg.ConfigHome, "dagger")
	ConfigFile = userconfig.Path()
)
View Source
var ErrAborted = errors.New("setup cancelled")

ErrAborted is returned when the user cancels a setup form (e.g. ctrl+c).

View Source
var ErrMalformed = errors.New("malformed LLM configuration")

ErrMalformed marks an assembly error caused by a value that cannot be parsed. Unlike an unreadable source, sending the config without it would silently misconfigure the router.

View Source
var ErrOAuthReauthenticationRequired = errors.New("OAuth reauthentication required")

ErrOAuthReauthenticationRequired marks refresh failures that cannot succeed again until the user replaces the OAuth grant.

Functions

func Assemble

func Assemble(cwd string) (cfg *engine.LLMConfig, warnings []error, err error)

Assemble builds the LLM configuration to send to the engine from, lowest to highest precedence: the config file's [llm] section, the process environment, and cwd/.env.

A source that cannot be read (an unparseable config file or .env) is left out and reported in warnings; the rest still applies, as it did when the engine read these itself. A value that cannot be parsed is an error wrapping ErrMalformed, and no config is returned.

Literal credentials read from .env are remembered for ResolveSecret, which serves them as llmconfig:// secrets; the last Assemble call wins.

func ConfigExists

func ConfigExists() bool

ConfigExists checks if config file exists.

func DefaultModelForProvider

func DefaultModelForProvider(provider string) string

DefaultModelForProvider returns the default model ID for the given provider, or empty string if the provider is unknown.

func FetchSubscriptionType

func FetchSubscriptionType(ctx context.Context, accessToken string) (string, error)

FetchSubscriptionType queries the Anthropic OAuth profile endpoint to determine the user's subscription type (pro, max, team, enterprise).

func GenerateOAuthURL

func GenerateOAuthURL() (authURL, verifier string, err error)

GenerateOAuthURL generates a PKCE-protected OAuth authorization URL. Returns the URL the user should visit and the PKCE verifier for later use.

func GenerateOpenAIOAuthURL

func GenerateOpenAIOAuthURL() (authURL, verifier, state string, err error)

GenerateOpenAIOAuthURL generates a PKCE-protected OAuth authorization URL for OpenAI Codex (ChatGPT subscription). Returns the URL, the PKCE verifier, and the state parameter.

func InteractiveSetup

func InteractiveSetup(ctx context.Context, promptHandler PromptHandler) (bool, error)

InteractiveSetup guides user through LLM configuration. It is additive: already-configured providers are shown with a summary and can be reconfigured, while new providers can be added alongside them. Returns (configured bool, error) - configured is true if setup completed.

func IsProvider

func IsProvider(name string) bool

IsProvider reports whether name is a provider the engine can route to.

func IsSecretURI

func IsSecretURI(v string) bool

IsSecretURI reports whether v is a reference to a secret (op://…, vault://…, env://…) rather than the secret itself.

func IsTerminalOAuthRefreshError

func IsTerminalOAuthRefreshError(err error) bool

IsTerminalOAuthRefreshError reports a refresh failure that requires the user to authenticate again instead of retrying the same credential.

func IsTokenExpired

func IsTokenExpired(provider *Provider) bool

IsTokenExpired reports whether the OAuth access token has expired, or is close enough to expiring to be worth replacing now. An unknown expiry is not expired: the refresher runs on every secret resolution, so guessing here would rotate the refresh token continuously.

func LLMConfigured

func LLMConfigured() bool

LLMConfigured checks if the config file exists and has LLM providers configured.

func RefreshOAuthTokensIfNeeded

func RefreshOAuthTokensIfNeeded(ctx context.Context) error

RefreshOAuthTokensIfNeeded checks all OAuth providers in the config and refreshes any expired tokens. This should be called client-side before connecting to the engine.

func Remove

func Remove() error

Remove deletes the [llm] section from the config file. Other sections are preserved; the file itself is only removed once nothing else remains.

func ResolveSecret

func ResolveSecret(ctx context.Context, path string) ([]byte, error)

ResolveSecret serves llmconfig://<provider>/<field> secrets. It is registered as the scheme's resolver by the CLI.

A credential Assemble took from ./.env is served as read. Anything else is read from the config file at every resolution:

  • api_key: the provider's stored API key.
  • auth_token: the subscription OAuth token, refreshed first if it is due or if the context carries the RejectedSecretValue fingerprint of the current token; the rotated token is persisted.
  • auth_token_expires_at: the token's true expiry, RFC 3339 UTC, or empty when unknown. The engine reads it right after auth_token, so it sees the expiry of a token that read just rotated.

A provider that is absent or disabled is a not-found error.

func SubscriptionLabel

func SubscriptionLabel(subType string) string

SubscriptionLabel returns a human-readable label for a subscription type.

func UpdateFile

func UpdateFile(fn func(existing []byte) ([]byte, error)) error

UpdateFile applies fn to the current config file contents under the cross-process lock and writes the result back atomically with 0600 permissions. fn receives nil when the file does not exist yet. The file is shared between subsystems ([llm], [workspaces], ...), so fn must preserve sections it does not own.

Types

type AbovePrinter

type AbovePrinter interface {
	PrintAbove(text string)
}

AbovePrinter is an optional interface that PromptHandlers may implement to print text above the TUI into the terminal scrollback buffer. This is useful for content that must not be word-wrapped (e.g. clickable URLs).

type BrowserOpener

type BrowserOpener interface {
	OpenBrowser(url string) error
}

BrowserOpener is an optional interface that PromptHandlers may implement to open a URL in the user's browser. OAuth flows use it to launch the authorization page automatically; the printed URL stays the fallback. It is client-side (the Frontend implements it), so core/llmconfig does not depend on a browser package.

type Config

type Config struct {
	LLM LLMConfig `toml:"llm"`
}

Config represents the top-level dagger config file. Only the [llm] section is managed here; other sections are preserved as-is.

func Load

func Load() (*Config, error)

Load reads config from disk, returns nil if not exists.

func (*Config) Save

func (c *Config) Save() error

Save writes the [llm] section to disk with proper permissions (0600). The config file is shared with other subsystems, so the section is merged into the existing document rather than replacing the whole file.

type LLMConfig

type LLMConfig struct {
	DefaultProvider string              `toml:"default_provider"`
	DefaultModel    string              `toml:"default_model,omitempty"`
	Providers       map[string]Provider `toml:"providers"`
}

LLMConfig represents the [llm] section.

type ModelInfo

type ModelInfo struct {
	// ID is the model identifier sent in API requests.
	ID string
	// Label is a short human-readable name for display in selection UIs.
	Label string
	// CanReason indicates that the model supports extended reasoning / thinking.
	CanReason bool
	// ReasoningLevels lists the reasoning effort levels the model supports
	// (e.g. "low", "medium", "high"). Empty if reasoning is not supported.
	ReasoningLevels []string
	// DefaultReasoningEffort is the default reasoning level for the model.
	DefaultReasoningEffort string
	// Default indicates this is the recommended default for its provider.
	Default bool
}

ModelInfo describes a model available from a provider.

func ModelByID

func ModelByID(provider, modelID string) (ModelInfo, bool)

ModelByID looks up a specific model by provider and model ID. Returns the model and true if found, zero value and false otherwise.

func ModelsForProvider

func ModelsForProvider(provider string) []ModelInfo

ModelsForProvider returns the model list for a given provider key. Returns nil for unknown providers.

type OAuthCallbackServer

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

OAuthCallbackServer listens on localhost for an OAuth callback.

func StartOAuthCallbackServer

func StartOAuthCallbackServer(port int, expectedState string) (*OAuthCallbackServer, error)

StartOAuthCallbackServer starts a local HTTP server to receive OAuth callbacks. It listens on the given port and validates the state parameter.

func (*OAuthCallbackServer) Close

func (s *OAuthCallbackServer) Close()

Close shuts down the callback server.

func (*OAuthCallbackServer) WaitForCode

func (s *OAuthCallbackServer) WaitForCode(ctx context.Context) string

WaitForCode waits for the OAuth callback to deliver a code, or for the context to be cancelled. Returns empty string if timed out or cancelled.

type OAuthTokenResponse

type OAuthTokenResponse struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	ExpiresIn    int    `json:"expires_in"`
}

OAuthTokenResponse represents the token endpoint response.

type OpenAITokenResponse

type OpenAITokenResponse struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	ExpiresIn    int    `json:"expires_in"`
}

OpenAITokenResponse represents the OpenAI token endpoint response.

type PromptHandler

type PromptHandler interface {
	HandleForm(ctx context.Context, form *huh.Form) error
}

PromptHandler is the minimal interface needed for interactive prompts

type Provider

type Provider struct {
	APIKey           string `toml:"api_key"`
	BaseURL          string `toml:"base_url,omitempty"`
	Model            string `toml:"model,omitempty"`
	SmallModel       string `toml:"small_model,omitempty"`
	AzureVersion     string `toml:"azure_version,omitempty"`
	DisableStreaming bool   `toml:"disable_streaming,omitempty"`
	Enabled          bool   `toml:"enabled"`

	// OAuth fields for Claude Code subscription auth
	AuthType     string `toml:"auth_type,omitempty"`     // "oauth" for Claude Code OAuth
	AuthToken    string `toml:"auth_token,omitempty"`    // OAuth access token
	RefreshToken string `toml:"refresh_token,omitempty"` // OAuth refresh token
	// TokenExpiresAt is when the access token truly expires, in unix
	// milliseconds, with no safety margin baked in. The margin is applied when
	// the value is checked (IsTokenExpired), so an absent or short expires_in
	// can't persist an already-expired value.
	TokenExpiresAt int64 `toml:"token_expires_at,omitempty"`
	// TokenExpiry is the legacy expiry: the same instant with the safety margin
	// already subtracted. Still written so that an older CLI sharing this config
	// file keeps working — it reads only this field and treats 0 as expired,
	// which would put it in a refresh loop rotating the token out from under
	// this one. TokenExpiresAt wins when both are present.
	TokenExpiry      int64  `toml:"token_expiry,omitempty"`
	SubscriptionType string `toml:"subscription_type,omitempty"` // "pro", "max", "team", "enterprise"

	// ReasoningEffort is the reasoning level for the provider's model, taken
	// from the model's catwalk reasoning_levels (e.g. "low", "medium", "high").
	// Empty disables reasoning.
	ReasoningEffort string `toml:"reasoning_effort,omitempty"`

	// APICompat selects which API protocol to use for custom/local endpoints.
	// Values: "openai" (OpenAI-compatible) or "anthropic" (Anthropic-compatible).
	// When set, BaseURL is used as the endpoint and the model name is passed through.
	APICompat string `toml:"api_compat,omitempty"`

	// ClaudeCodeVersion is the Claude Code release (a bare X.Y.Z) to present
	// when authenticating Anthropic with a subscription OAuth token, overriding
	// the engine's built-in default.
	ClaudeCodeVersion string `toml:"claude_code_version,omitempty"`
}

Provider represents a single LLM provider's configuration.

func ExchangeOAuthCode

func ExchangeOAuthCode(ctx context.Context, authCode, verifier string) (*Provider, error)

ExchangeOAuthCode exchanges an authorization code for tokens. The authCode should be in the format "code#state" as provided by the callback.

func ExchangeOpenAIOAuthCode

func ExchangeOpenAIOAuthCode(ctx context.Context, code, verifier string) (*Provider, error)

ExchangeOpenAIOAuthCode exchanges an authorization code for OpenAI tokens.

func RefreshOAuthProviderAfterRejection

func RefreshOAuthProviderAfterRejection(ctx context.Context, name, rejected string) (*Provider, error)

RefreshOAuthProviderAfterRejection reloads the provider from disk and refreshes if due, or if rejected is the SHA-256 hex fingerprint of its current access token. A different token on disk wins without forcing another rotation: a concurrent process may already have recovered the credential for us.

func RefreshOAuthProviderIfNeeded

func RefreshOAuthProviderIfNeeded(ctx context.Context, name string) (*Provider, error)

RefreshOAuthProviderIfNeeded refreshes a single OAuth provider by name if its token has expired, persisting the result. It returns the provider as it now stands (refreshed or not), or nil if it is absent, disabled, or not an OAuth provider. Used to keep a long-running session's bearer token fresh: the client re-resolves the token on demand rather than only at startup.

func RefreshOAuthToken

func RefreshOAuthToken(ctx context.Context, provider *Provider) (*Provider, error)

RefreshOAuthToken refreshes an expired OAuth token.

func RefreshOpenAIOAuthToken

func RefreshOpenAIOAuthToken(ctx context.Context, provider *Provider) (*Provider, error)

RefreshOpenAIOAuthToken refreshes an expired OpenAI OAuth token.

func (*Provider) IsOAuth

func (p *Provider) IsOAuth() bool

IsOAuth returns true if this provider uses OAuth authentication.

func (*Provider) TokenExpiresAtRFC3339

func (p *Provider) TokenExpiresAtRFC3339() string

TokenExpiresAtRFC3339 formats the true access-token expiry as RFC 3339 in UTC — the wire format of the llmconfig://<provider>/auth_token_expires_at secret the engine reads alongside the token — or "" when the expiry is unknown.

func (*Provider) TokenExpiresAtTime

func (p *Provider) TokenExpiresAtTime() time.Time

TokenExpiresAtTime returns the true access-token expiry, or the zero time when it is unknown. Configs written before token_expires_at existed carry only token_expiry, which had the margin subtracted at write time — add it back to recover the real instant.

type ProviderEntry

type ProviderEntry struct {
	// Label is the human-readable name shown in the selector.
	Label string
	// Value is the choice identifier used in the setup switch.
	Value string
	// ConfigKey is the key in Config.LLM.Providers (may differ from Value
	// for OAuth variants, e.g. "anthropic-oauth" → "anthropic").
	ConfigKey string
	// IsOAuth is true if this entry expects OAuth authentication.
	IsOAuth bool
}

ProviderEntry describes a provider option for the setup UI.

func ProviderEntries

func ProviderEntries() []ProviderEntry

ProviderEntries returns the ordered list of provider options for setup UIs.

Jump to

Keyboard shortcuts

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