Skip to content

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.

import "gitlab.com/phpboyscout/go/chat"

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.Logger for logging (nil ⇒ a discard logger),
  • *http.Client for provider transport (nil ⇒ a plain bounded stdlib client),
  • a KeychainLookup func for credential resolution (nil ⇒ the keychain step is skipped),
  • afero.Fs for 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:

Using go-tool-base? The framework ships a thin adapter that maps its Props/Viper config (the ai.*, ai.fallback.*, and provider api sections) into the chat.Settings this 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

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.

Join the Discord