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
cockroachdb/errors,
invopop/jsonschema,
google/uuid, and
spf13/afero — and 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 (+ tiktoken) |
gemini |
gitlab.com/phpboyscout/go/chat-gemini |
google.golang.org/genai |
The SDK-free claude-local provider ships in the core as a zero-weight default.
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:
- Getting started — a learning-oriented walkthrough:
your first
Chatand structuredAsk, end to end. - How-to guides — task-oriented recipes:
- Explanation — understanding-oriented background:
- Reference — the full API, with runnable
Exampletests, lives on pkg.go.dev.
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.