chat¶
A unified, multi-provider AI chat client for Go: Anthropic Claude, a
locally installed claude CLI, OpenAI (and any OpenAI-compatible endpoint), and
Google Gemini, behind one small interface, as a light, dependency-inverted
library.
chat is the same client behind gtb ai, extracted so any project can embed a
production chat client, with a ReAct tool-calling loop, streaming,
cross-provider fallback, token accounting, and encrypted conversation
persistence, without pulling in the
go-tool-base framework, and
without linking every vendor AI SDK.
The light-footprint promise¶
The core module graph is deliberately tiny. go.mod declares
phpboyscout/go/errors,
invopop/jsonschema,
google/uuid, and
spf13/afero. No vendor AI SDK, no web
framework, no go-tool-base. A depfootprint_test.go guard fails the build if any
forbidden dependency (a vendor SDK, Viper/Cobra, Charm, OpenTelemetry, the AWS
SDK, or go-tool-base) ever enters the graph.
Each vendor SDK lives in its own module, activated by a blank import, so a Claude-only tool never compiles the OpenAI or Gemini SDKs:
| Provider | Module (blank-import to activate) | Underlying SDK |
|---|---|---|
claude-local |
gitlab.com/phpboyscout/go/chat (in the core) |
none (the local claude CLI) |
claude |
gitlab.com/phpboyscout/go/chat-anthropic |
anthropics/anthropic-sdk-go |
openai, openai-compatible |
gitlab.com/phpboyscout/go/chat-openai |
openai/openai-go |
gemini |
gitlab.com/phpboyscout/go/chat-gemini |
google.golang.org/genai |
The SDK-free claude-local provider ships in the core and needs no adapter
module. It is not the default provider: leave Config.Provider unset and the
client asks for claude, which fails with unsupported provider: claude unless
chat-anthropic is blank-imported. Set Config.Provider to
chat.ProviderClaudeLocal to use it.
The library leans on the standard library at every seam:
*slog.Loggerfor logging (nil⇒ a discard logger),*http.Clientfor provider transport (nil⇒ a plain bounded stdlib client),- a
KeychainLookupfunc for credential resolution (nil⇒ the keychain step is skipped), afero.Fsfor conversation persistence.
See Dependency inversion for why.
Who it is for¶
- CLI and service authors who want an agentic chat client (tool calling, structured output, streaming) without adopting a framework.
- Any non-framework Go project that needs to talk to an LLM behind a stable, provider-neutral interface.
- Cost-conscious builds that want exactly one vendor SDK linked, not four.
Quick start¶
Blank-import one provider module to activate it, then construct a client from
package-owned Settings:
import (
"context"
"gitlab.com/phpboyscout/go/chat"
_ "gitlab.com/phpboyscout/go/chat-anthropic" // activate the Claude provider
)
func summarise(ctx context.Context, apiKey, changelog string) (string, error) {
client, err := chat.New(ctx, chat.Settings{
Config: chat.Config{Provider: chat.ProviderClaude, Token: apiKey},
})
if err != nil {
return "", err
}
return client.Chat(ctx, "Summarise this changelog in one line:\n"+changelog)
}
With no Token set, the client resolves a credential from config references or
the well-known ANTHROPIC_API_KEY env var. See
Choose & configure a provider.
Where to go next¶
The documentation follows the Diátaxis framework:
- Tutorials, learning-oriented walkthroughs:
- Getting started: your first
Chatand structuredAsk, end to end. - Add a slash command: a chat loop with
/helpand/clear, then a command of your own.
- Getting started: your first
- How-to guides. Task-oriented recipes:
- Choose & configure a provider
- Call tools (the ReAct loop)
- Stream responses
- Fail over across providers
- Persist conversations
- Process a batch of independent documents
- Bound a long conversation
- Let people run commands in a conversation
- Control sampling & reasoning effort
- Cache a large stable prompt
- Check what a model supports
- Handle a partially-applied configuration
- Register a custom provider
- Verify a provider against the conformance suite
- Reference, the facts looked up:
- Configuration fields: every field, its default, and its failure mode
- Defaults and limits: the constants, and the caps you cannot change
- Errors and sentinels: what each error means and what fixes it
- Environment variables: the four the module reads
- API signatures, with runnable
Exampletests, live on pkg.go.dev
- Explanation. Understanding-oriented background:
Using go-tool-base? The framework ships a thin adapter that maps its Props/Viper config (the
ai.*,ai.fallback.*, and providerapisections) into thechat.Settingsthis module owns. That adapter lives in go-tool-base, not here; this module is config-system-agnostic.
Further reading¶
The blog carries a curated route through this subject: Building with AI collects everything written about it, ordered so you can start at the beginning rather than newest-first.
Ask phpbotscout

He answers questions about the projects over on the Discord, citing the docs where they already cover it, and offering to raise an issue where they don't. Bring a bug, an idea, or a questionable engineering decision.