chat¶
A unified, multi-provider AI chat client for Go: Anthropic Claude, a
locally installed claude CLI, OpenAI (and any OpenAI-compatible endpoint),
Google Gemini on either the Gemini API or Vertex AI, AWS Bedrock, and Azure
OpenAI, 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, Gemini or AWS SDKs:
| Provider | Module (blank-import to activate) | Underlying SDK |
|---|---|---|
claude, claude-local |
gitlab.com/phpboyscout/go/chat-anthropic |
anthropics/anthropic-sdk-go |
openai, openai-compatible, codex-local |
gitlab.com/phpboyscout/go/chat-openai |
openai/openai-go |
gemini, gemini-vertex, agy-local |
gitlab.com/phpboyscout/go/chat-gemini |
google.golang.org/genai |
bedrock |
gitlab.com/phpboyscout/go/chat-bedrock |
aws-sdk-go-v2/service/bedrockruntime |
azure-openai |
gitlab.com/phpboyscout/go/chat-openai-azure |
openai-go/v3 plus azure-sdk-for-go/sdk/azcore |
Each vendor module registers its local CLI alongside its API, so one blank
import gives you both routes: chat-anthropic adds claude-local,
chat-openai adds codex-local, chat-gemini adds agy-local. Those
providers drive a CLI you have already signed in, which is valuable where
outbound HTTPS to the vendor is blocked but the binary is permitted. They link
no extra SDK.
None is the default: leave Config.Provider unset and the client asks for
claude, which fails with unsupported provider: claude unless
chat-anthropic is blank-imported.
A local-CLI provider is not a sandbox. Each of these CLIs is an agentic tool
with tools of its own, and only claude can be told to give up command
execution. See
what the model on the far side can reach.
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.