Choose & configure a provider¶
This guide covers the chat.Config fields, how each provider is activated and
credentialed, and the two providers that need extra setup: openai-compatible
(a required BaseURL and Model) and claude-local (a local CLI binary).
The Config fields¶
chat.Config carries provider behaviour. It is passed inside chat.Settings
to chat.New. Only Provider is effectively required (and even that defaults);
everything else has a sane zero value.
| Field | Type | Description |
|---|---|---|
Provider |
Provider |
The provider constant. Defaults to ProviderClaude when unset and AI_PROVIDER is not set. |
Model |
string |
Model name. Falls back to a per-provider default when empty. Required for ProviderOpenAICompatible. |
Token |
string |
API key. Optional when resolvable from config references or a well-known env var. |
Credentials |
CredentialConfig |
Credential references (env-var name, keychain ref, literal) supplied by the host. Token wins when both are set. |
BaseURL |
string |
API endpoint override. Required for ProviderOpenAICompatible. Validated by ValidateBaseURL. |
SystemPrompt |
string |
Initial system prompt. Placed in each provider's native system slot (Claude's system field, OpenAI's first system message, Gemini's config + history). |
ResponseSchema |
any |
JSON schema forcing structured output for Ask. |
SchemaName / SchemaDescription |
string |
Name and description for the response schema. |
MaxSteps |
int |
Maximum ReAct loop iterations in Chat. Zero uses the default (20). |
MaxTokens |
int |
Maximum tokens per response. Zero uses the provider default (OpenAI 4096, Claude 8192, Gemini 8192). |
RequestTimeout |
time.Duration |
Per-request timeout. Zero uses DefaultChatRequestTimeout (5m). |
HTTPClient |
*http.Client |
Injected transport used verbatim. Nil builds a plain bounded client from RequestTimeout. |
ParallelTools |
bool |
Run multiple tool calls in one ReAct step concurrently. Off by default. |
MaxParallelTools |
int |
Concurrency cap for parallel tools. Zero uses the default (5). |
Seed |
*int64 |
Optional sampling seed for OpenAI / OpenAI-compatible. Nil (default) omits the seed so the model samples normally. |
UsageObserver |
func(Usage) |
Opt-in hook fired once per provider round-trip with that round-trip's token usage. |
AllowInsecureBaseURL |
bool |
Test-only; permits http BaseURLs for httptest.Server. Tagged json:"-" so config files cannot set it. |
See the pkg.go.dev reference for the full field list, including the test-only exec/genai override hooks.
Activate a provider by blank import¶
Each API provider lives in its own module. Blank-import it once (typically in
main) to register it with the core; then reference it by its Provider
constant.
| Provider constant | String | Blank import | Credential env var |
|---|---|---|---|
chat.ProviderClaude |
claude |
_ "gitlab.com/phpboyscout/go/chat-anthropic" |
ANTHROPIC_API_KEY (chatanthropic.EnvClaudeKey) |
chat.ProviderOpenAI |
openai |
_ "gitlab.com/phpboyscout/go/chat-openai" |
OPENAI_API_KEY (chatopenai.EnvOpenAIKey) |
chat.ProviderOpenAICompatible |
openai-compatible |
_ "gitlab.com/phpboyscout/go/chat-openai" |
backend-dependent (Token) |
chat.ProviderGemini |
gemini |
_ "gitlab.com/phpboyscout/go/chat-gemini" |
GEMINI_API_KEY (chatgemini.EnvGeminiKey) |
chat.ProviderClaudeLocal |
claude-local |
none — ships in the core | none — the claude binary is pre-authenticated |
The openai and openai-compatible providers are both registered by the single
chat-openai import.
Resolve a credential¶
Every API provider resolves its key through one shared five-step cascade
(chat.ResolveAPIKey). The first non-empty source wins; each step is trimmed so
a half-configured value cannot mask a fully-configured one below it:
- Direct token —
Config.Token. - Env-var reference —
Config.Credentials.Envnames an env var; the value is read withos.Getenv. The secret stays out of any config file. - Keychain reference —
Config.Credentials.Keychainis a"service/account"pair resolved through the host-injectedConfig.Credentials.Lookupfunc. With no lookup wired, this step is skipped. - Literal —
Config.Credentials.Key, an inline value (legacy; least preferred). - Well-known env var —
ANTHROPIC_API_KEY/OPENAI_API_KEY/GEMINI_API_KEY, the ecosystem fallback used by most SDKs and CI platforms.
The simplest path is to export the well-known env var and leave Token and
Credentials empty:
import (
"gitlab.com/phpboyscout/go/chat"
_ "gitlab.com/phpboyscout/go/chat-anthropic"
)
// ANTHROPIC_API_KEY is set in the environment; nothing else needed.
client, err := chat.New(ctx, chat.Settings{
Config: chat.Config{Provider: chat.ProviderClaude},
})
A host that keeps secrets in a config file or keychain populates
Config.Credentials instead — see credential security.
Using go-tool-base? Its adapter maps the framework's
ai.provider,AI_PROVIDER, and providerapiconfig sections onto these fields for you; that adapter lives in go-tool-base, not this module.
OpenAI-compatible endpoints¶
ProviderOpenAICompatible targets any backend that speaks the OpenAI API —
Ollama, Groq, Fireworks AI, Together AI, LM Studio, vLLM, and others. It is
registered by the chat-openai import. Both BaseURL and Model are
required (there is no default model — names are backend-specific), and the
BaseURL must pass endpoint validation.
import (
"gitlab.com/phpboyscout/go/chat"
_ "gitlab.com/phpboyscout/go/chat-openai"
)
// Ollama (local)
ollama, err := chat.New(ctx, chat.Settings{Config: chat.Config{
Provider: chat.ProviderOpenAICompatible,
BaseURL: "http://localhost:11434/v1",
Model: "llama3.2",
Token: "ollama", // Ollama ignores the token; any non-empty value works
// A localhost HTTP endpoint is non-HTTPS; permitted only in tests.
AllowInsecureBaseURL: true,
}})
// Groq (cloud, HTTPS)
groq, err := chat.New(ctx, chat.Settings{Config: chat.Config{
Provider: chat.ProviderOpenAICompatible,
BaseURL: "https://api.groq.com/openai/v1",
Model: "llama-3.3-70b-versatile",
Token: os.Getenv("GROQ_API_KEY"),
}})
Non-HTTPS endpoints.
ValidateBaseURLrejectshttp://unlessAllowInsecureBaseURLis set, which is intended forhttptest.Servertargets only. For a plaintext local model, prefer an HTTPS proxy in production; the flag exists so tests can point at a local server, not as a way to disable TLS generally.
Token chunking falls back to the cl100k_base encoding for model names the
tokenizer does not recognise, so Ollama and other non-OpenAI model names are
handled gracefully.
Claude Local (the claude CLI)¶
ProviderClaudeLocal routes requests through a locally installed, pre-authenticated
claude CLI binary instead of the Anthropic API. It is valuable in secure or
air-gapped environments where outbound HTTPS to api.anthropic.com is blocked
but the binary is permitted. It ships in the core — no blank import and no API
key required.
Requirements:
- The
claudeCLI installed and onPATH. - Authenticated once (
claude login).
import "gitlab.com/phpboyscout/go/chat" // no provider import needed
client, err := chat.New(ctx, chat.Settings{Config: chat.Config{
Provider: chat.ProviderClaudeLocal,
Model: "claude-sonnet-4-6", // optional; uses claude's default if empty
SystemPrompt: "You are a helpful assistant.",
}})
Multi-turn continuity is maintained via session IDs captured from the CLI's JSON
output and passed back with --resume. Two limitations set claude-local apart:
it does not support tool calling (SetTools returns an error; MCP-based
tools are planned) and it does not stream. It also reports no reliable token
usage — see Providers.
Related¶
- Architecture & the seams — how
chat.Newdispatches to a provider. - Providers — the full capability matrix and per-provider notes.
- Provider-endpoint & credential security — BaseURL validation and credential handling.