Showcase · AI client · Go

chat

A light, framework-free multi-provider AI chat client with provider support kept opt-in.

v0.33.0, released Pre-1.0Covers 8 projects

DocsInstallRepository

The problem it exists for

Talking to one AI provider from Go is easy. Talking to several is where it goes wrong: every vendor SDK has its own idea of a conversation, a tool call, a stream and an error, and pulling three of them into a project drags in three dependency trees whether you use them or not. The usual answer is a big framework, and for AI integration the de facto standard is LangChain (in my opinion, anyway). It’s capable, and it exists for a real need, but it’s a lot to swallow when what you want is “send this, get an answer back”.

go/chat is the client behind gtb ai, pulled out so a project can use it without go-tool-base and without linking every vendor’s SDK. How heavy an unused dependency gets came up early: the MCP SDK alone was 1.35 MB and eight modules for a consumer that never touched it (spec 0021), and making it opt-in (spec 0028) is the same thinking applied to everything else.

How it works

One conversation, whichever provider

You get one small client: add a message, chat, ask for a structured answer straight into a Go value, hand it some tools, and read back the history and the usage. Streaming, persistence and caching are there when a provider can do them, and absent rather than faked when it can’t. Around that sit the things every real use ends up needing anyway: a tool loop, fallback to another provider when one falls over, history that stays within bounds and compacts itself, and encrypted persistence.

Opt in to the providers you use

Every vendor lives in a module of its own, switched on with a single import, and the core depends on almost nothing (a test fails the build if a vendor SDK, a CLI framework or OpenTelemetry ever sneaks into its graph). Ten providers across five modules today, and a sixth on the way:

ModuleProviders
chat-anthropicClaude through the API, and Claude Code running locally
chat-openaiOpenAI, any OpenAI-compatible server, and Codex running locally
chat-geminiGemini, Gemini on Vertex, and Antigravity running locally
chat-bedrockAmazon Bedrock
chat-openai-azureAzure OpenAI
chat-vllmA self-hosted vLLM server (in progress, not released yet)

The local ones are the interesting bit for me: they drive a coding agent’s own CLI, so a tool uses whatever that CLI is already signed in with, instead of needing an API key of its own. chat-mcptools bridges your tools into those local agents over a loopback MCP server, so they can call them too.

What a model can do is data, and “don’t know” is an answer

Each provider reports what it supports (streaming, tools, structured output, caching and the rest) and every answer is one of three values: yes, no, or unknown. Unknown is the default, and it means “go ahead and find out”, because the alternative is worse. As the code puts it, reporting a provider that can’t be asked as unsupported “is a lie which suppresses working features”. Only a confident no changes behaviour.

The answers come from the caller first, then a live probe, then a catalogue generated from models.dev and refreshed on a schedule. The catalogue is built ahead of time and never fetched while your program runs.

Every provider sits the same exam

A conformance suite ships with the core, and every provider module runs it to prove it behaves like the others: capabilities, structured answers, history, timeouts, streams, errors, concurrency and refusals, nine groups of them. It’s how ten providers from five vendors end up feeling like one client.

Decisions and what they cost

  • Not LangChain. Before a line of it was written, LangChain Go, go-openai, Vercel’s AI SDK and about ten others were weighed up, and none of them fitted: the aim was something much lighter and simpler, an interface small enough to fit on one screen (the design notes, and the post that argued it). What it cost: owning it, and it has grown a long way since. It started as four methods and five providers inside go-tool-base, and it’s now its own module family with ten providers, a tool loop, fallback, history compaction and capability catalogues. It’s still a long way short of what LangChain does, which is rather the point, but “small” has had to stretch.
  • Three-valued capabilities. A fixed vocabulary of capabilities, each yes, no or unknown, instead of a bool (spec 0006), because almost nothing a caller needs to know is really a bool. What it cost: Claude Code locally and an arbitrary OpenAI-compatible server can only ever say unknown, and unknown can’t warn you about anything.
  • Degrade, don’t refuse. A misconfigured setting doesn’t stop you getting a client: you get one that works, plus a list of the settings it had to drop (spec 0007). Failing hard, dropping them quietly into a log, and a strict-mode switch were all turned down. What it cost: that list lives in the error you get back, and nowhere on the client afterwards. The post on it has the whole argument.
  • Conformance ships in the core. The suite lives inside go/chat itself instead of a separate module (spec 0011), because a fourth thing to version would have been one more thing to drift. What it cost: the dependency test had to be narrowed to just the packages that reach a consumer.
  • Structured output. Structured answers use each provider’s own structured-output feature instead of a forced tool call wrapped in instructions (spec 0024). The spike behind it had 15 of 15 models accept it, and 29 of 29 replies came back valid against the schema. What it cost: about 35 billable calls to find out, and a change to how those answers sit in the history.

Proof in use

  • Five projects in the estate use it: go-tool-base for its AI commands, keryx to write social copy, krites as the critic that reviews a photo cull, phpbotscout to answer support questions, and Scout.
  • 39 releases of the core since 12 July 2026, and between them the provider modules have shipped another 120. Each provider’s release merge request runs cicd’s go-core-currency check, so a provider can’t ship against a core that’s moved on unnoticed.
  • The conformance suite runs in six suites across five provider modules, and the family carries 836 test, fuzz and example functions.

Use it when, and when not to

Use it if you want an AI provider (or several, with fallback between them) in a Go program without inheriting every vendor’s SDK, and you’d like structured answers, tools and streaming to look the same whichever one you’re talking to. Install a provider module and let it pick the core version for you.

Don’t use it for anything beyond chat. There’s no image generation, embeddings or audio, no rate limiting or spend cap, and one client talks to one provider at a time (fallback is for when one falls over, not a router). Claude Code locally can’t stream, and a refusal is only spotted where the provider marks it as one. The limitations page has the full list. And it’s pre-1.0, so the provider modules move with the core’s minor version.

There’s a Rust one too, if that’s your language: rtb-chat, part of rust-tool-base.

Where it’s going

vLLM is next. Spec 0035, approved on 10 October 2026, adds chat-vllm for a self-hosted vLLM server, as a module of its own with its own HTTP client rather than a layer over the OpenAI provider. It ships two things the other providers will get later, once a live run has confirmed them: token log-probabilities and tool calls handed back without running them. It streams, but every capability that depends on the model answers unknown, which is the right answer for a server that can run whatever model you load into it. Its first release waits on that live run against a GPU server.

Providers and the MCP bridge

The story in posts

Last reviewed .