Skip to content

title: Configuration fields description: Every chat.Config and chat.Settings field: what it is, what it defaults to when you leave it alone, and what happens when the value cannot be applied. tags: [reference, configuration, defaults, config]


Configuration fields

Every field of chat.Config and chat.Settings, what it defaults to when left alone, and what happens when the value cannot be applied. chat.Config is the whole of the module's configuration surface: the module reads no config file and has no flags of its own, because a host application owns config schema and maps it into these fields.

Only Provider is meaningful on its own, and even that defaults. Every other field has a working zero value.

How to read the "if it cannot be applied" column

chat.New classifies a problem into one of three outcomes, and the tables below name which one each field produces:

Outcome client err Meaning
Applied usable nil the value reached the provider
Dropped usable non-nil the value was not applied, the provider's own default is used, and the error names the field
Fatal nil non-nil, wraps chat.ErrUnableToConstruct nothing was built

A dropped setting is never clamped or approximated. Temperature: 3.0 samples at the provider's default, not at 2.0. The split between dropped and fatal is ownership, not severity: a parameter the module forwards to a vendor degrades, and a feature the module implements itself is fatal. See Handle a partially-applied configuration for the code that tells the three apart.

Provider and model fields

Field Type Default when unset If it cannot be applied
Provider Provider the AI_PROVIDER environment variable, then ProviderClaude Fatal. A name with no registered factory yields unsupported provider: <name>, which usually means the provider module's blank import is missing
Model string the provider's own default (claude-opus-5, gpt-5.6-sol, gemini-3.7-flash); the claude CLI's own default for claude-local; none for openai-compatible Fatal when empty on ProviderOpenAICompatible, which has no default to fall back on. Otherwise the name is not validated here: a model the capability table knows refuses a generation control causes that control to be dropped, and a name the vendor does not recognise fails at request time
BaseURL string empty (each provider uses its vendor endpoint) Fatal. A URL failing any of the seven endpoint rules yields chat.ErrInvalidBaseURL. Empty is also fatal for ProviderOpenAICompatible, which requires it
Project string empty, then GOOGLE_CLOUD_PROJECT Addressing for ProviderGeminiVertex, which is fatal when both are empty. Ignored by every other provider
Location string empty, then GOOGLE_CLOUD_LOCATION Addressing for ProviderGeminiVertex, which is fatal when both are empty. Named for the concept, so a future region-addressed provider reuses it
SystemPrompt string empty, and no system instruction is sent Applied by every provider; there is no failure mode

ProviderOpenAICompatible is the one provider with two required fields: BaseURL (no default endpoint exists) and Model (model names are backend-specific, so the module has no default to offer).

Credential fields

Field Type Default when unset If it cannot be applied
Token string resolution falls through to Credentials, then the provider's well-known environment variable Fatal when the whole cascade resolves to nothing: each API provider refuses to construct, with Anthropic API key is required but not provided, OpenAI token is required but not provided or Gemini API key is required but not provided. A key that resolves but is wrong is not detected here and surfaces as the vendor's authentication error on the first call
Credentials.Env string unused Skipped when the named variable is unset or empty; resolution continues down the cascade
Credentials.Keychain string unused Skipped when Credentials.Lookup is nil, the reference is not service/account shaped, or the lookup errors; resolution continues down the cascade
Credentials.Key string unused Used verbatim; this is the legacy literal-in-config path
Credentials.Lookup KeychainLookup nil, so the keychain step is skipped Never decoded from config (mapstructure:"-"); a host injects it

The five sources are tried in order and the first non-empty one wins: TokenCredentials.EnvCredentials.KeychainCredentials.Key → the provider's well-known variable. Every value is whitespace-trimmed, so a half-configured source cannot mask a working one below it. ProviderClaudeLocal uses none of this, because the claude binary carries its own authentication.

Structured-output fields

Field Type Default when unset If it cannot be applied
ResponseSchema *jsonschema.Schema nil (Ask unmarshals the model's raw text into the target, which must then be a *string or implement json.Unmarshaler) Not checked at construction. There is no marker interface for structured output, so a provider that ignores the field does so silently, and see What this module does not do
SchemaName string empty, and Claude then names its structured-output tool submit_response Provider-owned. Claude uses it as the tool name, OpenAI sends it as the JSON-schema name, Gemini ignores it
SchemaDescription string empty Provider-owned. Claude and OpenAI attach it to the schema; Gemini ignores it

Build a schema with chat.GenerateSchema[T](), which returns *jsonschema.Schema directly (assignable to both Config.ResponseSchema and Tool.Parameters with no type assertion. It returned any before v0.8.0; code carrying a .(*jsonschema.Schema) assertion no longer compiles.

Generation-control fields

Field Type Default when unset If it cannot be applied
Temperature *float64 nil (the provider samples at its own default) Dropped when outside 0–2, or when the selected model reports SupportNo. Fatal when the provider has no sampling concept at all (claude-local). chat.ErrModelRejectedParameter at request time when the model was not in the capability table
TopP *float64 nil, and the provider samples at its own default Dropped when outside 0–1, or when the model reports SupportNo. Fatal on a provider with no sampling concept
Effort Effort empty, so the provider's own reasoning default Dropped when the value is not one of low, medium, high, xhigh, max, or when the model reports SupportNo. Fatal when the provider module does not implement chat.EffortCapable

Config.Seed was removed in v0.10.0

It was OpenAI-only, and the core never read it. Config carried it purely as a pass-through. Set a seed through chat-openai's own option instead. See spec 0011 D10.

The 0–2 bound on Temperature is the module's outer limit, wider than any one provider, because provider ranges differ (Claude 0–1, OpenAI and Gemini 0–2) and the adapters narrow it further. A value inside 0–2 but outside the selected provider's range is refused by the adapter, not here.

Tool-loop fields

Field Type Default when unset If it cannot be applied
MaxSteps int chat.DefaultMaxSteps (20) ReAct iterations per Chat/StreamChat call A run that exhausts the cap fails, with <Provider> reached maximum ReAct steps (20) without a final answer (no partial text is returned, and any streamed deltas have already reached your callback)
ParallelTools bool false, and tool calls in one step run sequentially Has no effect on claude-local, which refuses tools outright
MaxParallelTools int 5 when zero or negative; effective only while ParallelTools is true No failure mode

Parallel execution engages only when a single step returns more than one call. A lone call always takes the sequential path.

Transport and timeout fields

Field Type Default when unset If it cannot be applied
RequestTimeout time.Duration chat.DefaultChatRequestTimeout (5 minutes) per request Ignored entirely when HTTPClient is set. An injected client keeps its own timeouts. Also unused by claude-local, whose subprocess is bounded only by the context you pass
HTTPClient *http.Client nil, so the module builds a plain stdlib client whose overall and response-header timeouts are RequestTimeout Used verbatim when non-nil; the module neither wraps nor tunes it
MaxTokens int the provider's default (4096 on OpenAI, 8192 on Claude and Gemini) Provider-owned; the core never reads it, and claude-local passes no token cap to the CLI
MaxRetries *int nil, and the provider's own default, which is 2 on Claude and OpenAI and matched by Gemini 0 disables retry, which is the setting that makes a fallback composite advance on the first throttle rather than waiting the primary out

Nothing bounds a retry sequence as a whole. RequestTimeout bounds one request, so a high MaxRetries against a provider returning long Retry-After hints can run for a while, so cancel the context to bound it.

Caching fields

Field Type Default when unset If it cannot be applied
CacheTTL time.Duration zero, so the provider's own retention default, which is 5 minutes on Claude Fatal when the provider module does not implement chat.CachingChatClient, with a hint naming the upgrade. Providers pick the nearest retention they offer rather than honouring an arbitrary duration

Setting CacheTTL does not cache anything on its own. Content is cached only when it is added through CachingChatClient.AddCached, and every provider silently declines to cache blocks below a per-model minimum. Confirm with Usage().CachedTokens.

Conversation-mode fields

Field Type Default when unset If it cannot be applied
Stateless bool false (the client is a conversation, and every call re-sends the history) Fatal when the provider module does not implement chat.StatelessCapable. This is deliberately not a drop: a silently ignored flag would bill the caller for the history they asked not to send
HistoryPolicy HistoryPolicy nil, and the conversation grows without bound, as it always did Never dropped. A policy that returns an invalid edit fails the call rather than corrupting the transcript. chat.CompactOldest also calls the model, so it can fail for the ordinary reasons a call fails

Stateless and HistoryPolicy answer different problems and are not alternatives. Stateless is for calls that were never a conversation (batch classification, extraction, judging) and sends no history at all. HistoryPolicy is for a conversation that genuinely accumulates and needs bounding. chat.TruncateOldest drops the oldest turns, chat.CompactOldest summarises them instead. See Bound a long conversation.

Observability fields

Field Type Default when unset If it cannot be applied
UsageObserver func(Usage) nil, so usage is still accumulated for Usage(), but nothing is emitted per round-trip Called synchronously on the calling goroutine once per provider round-trip, including with a Known == false value from providers that report no counts. A slow observer slows the call

ChatClient.History() is the other half of this, and needs no configuration: it reports the turns a client will re-send and the provider's own input-token count for the last call it made. See Bound a long conversation.

Test-only fields

Field Type Default when unset Notes
AllowInsecureBaseURL bool false Permits an http:// BaseURL for an httptest.Server. Production callers leave it false

AllowInsecureBaseURL is excluded from every decoder (json:"-", mapstructure:"-" and yaml:"-"), so a host decoding chat.Config wholesale from its own config file cannot relax the HTTPS requirement from input it does not control. Every other field tagged json:"-" carries the same three exclusions, and a test enforces that pairing so the next one cannot drift.

The provider test seams moved in v0.10.0

ExecLookPath, ExecCommand and GenaiNewClient have left Config. Each was readable by exactly one provider, and GenaiNewClient was an any that the Gemini adapter type-asserted at runtime, a mistake to repeat rather than to generalise.

The two claude-local seams are now typed options on that provider's own constructor:

client, err := chat.NewClaudeLocal(ctx, settings,
    chat.WithClaudeBinaryLookup(fakeLookPath),
    chat.WithClaudeCommand(fakeCommand),
)

NewClaudeLocal runs the same validation chat.New does, so a setting the CLI cannot express (Config.Temperature, which it has no flag for at any version) is still a construction error rather than a silent miss.

GenaiNewClient moves to chat-gemini's own construction option when that module next adopts a core release.

chat.Settings: the construction dependencies

Settings is what chat.New takes. It carries the Config above plus the one dependency that is not configuration:

Field Type Default when unset
Config Config the zero Config, which resolves to the default provider
Logger *slog.Logger nil ⇒ a slog.DiscardHandler logger; nothing is logged and nothing panics

Host-config shapes and their mapstructure keys

The module owns typed shapes so a host can decode its own config into them without the module depending on a config library. The mapstructure tags are the key names a decoder will look for:

Shape Field Key Notes
RuntimeConfig Provider provider mapped onto Config.Provider
RuntimeConfig RequestTimeout request_timeout mapped onto Config.RequestTimeout
RuntimeConfig Fallback fallback a nested FallbackConfig
FallbackConfig Enabled enabled false builds a single client, not a composite
FallbackConfig Providers providers ordered; providers[0] is the primary and overrides Config.Provider, which is logged at WARN
CredentialConfig Env env the name of an environment variable
CredentialConfig Keychain keychain a service/account reference
CredentialConfig Key key a literal secret
CredentialConfig Lookup - never decoded; injected in code

These are shapes, not a config system. Nothing in this module reads a file, watches for changes, or applies precedence between sources. A host does that and passes the result in. go-tool-base ships such an adapter, and it lives there rather than here.

The order chat.New checks things in

The order matters because it determines which problems you learn about together. Config-only checks all run before anything is built, so a caller with three mistakes learns all three from one call:

  1. Default the provider from AI_PROVIDER, then to ProviderClaude.
  2. Validate BaseURL, and require it for ProviderOpenAICompatible, which is fatal.
  3. Validate Effort, Temperature and TopP values, which is dropped.
  4. Check the selected model's known capabilities, which is dropped.
  5. Stop here if anything so far was fatal, returning no client.
  6. Remove every dropped setting from the config, so the provider never sees a value the module has already reported as unapplied, and log each one at WARN.
  7. Look up the provider factory, which is fatal if it is unregistered.
  8. Call the factory, which is fatal if it errors.
  9. Assert the built client implements the marker interface for every capability the config asked for, which is fatal if it cannot.
  10. Log the endpoint host at INFO and return the client, plus an error listing anything that was not applied.