chatanthropic

package module
v0.17.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: MIT Imports: 14 Imported by: 8

README

chat-anthropic

Anthropic Claude provider for the chat multi-provider AI client

Go Reference Pipeline phpboyscout Go toolkit

Part of the phpboyscout Go toolkit. Docs: chat.go.phpboyscout.uk


gitlab.com/phpboyscout/go/chat-anthropic registers the Anthropic Claude provider (claude) for gitlab.com/phpboyscout/go/chat. It is a thin adapter over anthropics/anthropic-sdk-go — too tightly coupled to the core to warrant its own docs site, so all narrative documentation lives on the core chat docs site; this README plus the runnable Example tests are the module's reference.

Usage

Activate the provider with a blank import, then talk to Claude through the core chat API:

import (
    "gitlab.com/phpboyscout/go/chat"
    _ "gitlab.com/phpboyscout/go/chat-anthropic" // registers provider "claude"
)

client, err := chat.New(ctx, chat.Settings{
    Config: chat.Config{Provider: chat.ProviderClaude, Token: apiKey},
})
if err != nil {
    return err
}
answer, err := client.Chat(ctx, "Summarise this diff in one line.")

The blank import runs this module's init(), which registers the claude provider factory and an HTTP-status extractor so the core's cross-provider fallback can classify Anthropic errors. Importing it links anthropics/anthropic-sdk-go — and nothing else beyond the core.

Capabilities

  • ReAct tool-calling loop with automatic tool dispatch (sequential or parallel)
  • Streaming via StreamChat (text deltas, tool-call start/end, completion)
  • Structured output through Ask, constrained by the API via output_config.format
  • Image and PDF media blocks on the user turn
  • Conversation Save/Restore snapshots
  • Token-usage accounting via client.Usage()
  • Stateless one-shot calls via Config.Stateless — each call carries only its own turns, so one client can serve a batch of independent documents across goroutines instead of re-sending the accumulated prefix every time

Keeping up with Anthropic

Two generators, both run deliberately rather than automatically applied.

internal/genmodels regenerates the per-model capability table. It reads /v1/models and probes for the one thing that endpoint will not tell you — whether a model still accepts temperature — then you commit the result:

ANTHROPIC_API_KEY=... go run ./internal/genmodels > models_generated.go

internal/detectmodels watches for Anthropic shipping something chat.DefaultModelClaude does not reflect, and raises an issue in this project when it does. It reports candidates with their capability data; it never picks one, because nothing Anthropic publishes ranks models. The constant itself lives in go/chat, and the issue body says so.

ANTHROPIC_API_KEY=... go run ./internal/detectmodels -dry-run

It runs weekly — 03:00 Europe/London on Mondays — from a DETECT_MODELS=true scheduled pipeline. Neither -dry-run nor -update-snapshot touches the forge — only a plain run raises. internal/detectmodels/snapshot.json is the committed baseline, and it moves when a human resolves the issue, not when CI runs.

Credentials

The API key resolves through the core's cascade: Config.Token → Config.Credentials (env-var ref / OS keychain / literal) → the well-known ANTHROPIC_API_KEY environment variable.

Only the core chat module and anthropics/anthropic-sdk-go. A depfootprint_test.go guard fails the build if go-tool-base, the sibling provider SDKs (OpenAI, Gemini), the MCP SDK, or any CLI/observability weight leaks into the dependency graph.

claude-local's tools need the MCP tool bridge, which a program links itself with import _ "gitlab.com/phpboyscout/go/chat-mcptools", so a program that never offers it tools carries no MCP code.

Version compatibility

chat-anthropic requires the core version named in its own go.mod, and is built and tested against exactly that version. Do not compare the two version numbers — each module releases when that module changes, so they move independently.

Install this module and let it bring the core with it:

go get gitlab.com/phpboyscout/go/chat-anthropic
go mod tidy

See version compatibility for what a mismatch does, and why requiring chat directly at @latest is the way to break it.

Licence

MIT — see LICENSE.

Documentation

Index

Constants

View Source
const EnvClaudeKey = "ANTHROPIC_API_KEY"

EnvClaudeKey is the well-known unprefixed environment variable used as the ecosystem fallback when no explicit credential is configured.

Variables

View Source
var DefaultClaudeEnvironment = []string{"PATH", "HOME"}

DefaultClaudeEnvironment is what the subprocess inherits unless a caller adds to it with WithClaudeEnvironment. Measured against claude 2.1.260: these two are enough for the CLI to run and authenticate against its own stored login.

It is an allowlist rather than a denylist because the binary reads over seven hundred environment variables, around thirty of which route authentication or billing, and that set grows between versions. Naming the ones to remove would fail open in the direction of spending somebody else's money, which is the same reason the fallback composite carries credentials by allowlist. See spec 0021 D5.

Functions

func NewClaudeLocal added in v0.13.0

func NewClaudeLocal(ctx context.Context, settings chat.Settings, opts ...ClaudeLocalOption) (chat.ChatClient, error)

NewClaudeLocal builds a client backed by a locally installed, already authenticated claude CLI. No API key is required, and none is passed: the subprocess receives DefaultClaudeEnvironment only, so the binary authenticates with its own stored login rather than with whatever this process happens to have exported.

Reach for it directly when you need one of the ClaudeLocalOption knobs; otherwise [New] with Provider set to chat.ProviderClaudeLocal does the same job through the registry.

It runs the same validation [New] does, so a setting this provider cannot honour — chat.Config.Temperature, say, which the CLI has no flag for at any version — is still a construction error rather than a silent miss.

Types

type Claude

type Claude struct {
	chat.UsageTracker
	// contains filtered or unexported fields
}

Claude implements the chat.ChatClient interface using Anthropic's official Go SDK.

func (*Claude) Add

func (c *Claude) Add(ctx context.Context, prompt string, media ...chat.Media) error

func (*Claude) AddCached added in v0.5.0

func (c *Claude) AddCached(ctx context.Context, prompt string, media ...chat.Media) error

AddCached appends a user turn carrying a cache_control marker.

The marker goes on this block and no other. In particular it never lands on a per-call turn, which would write a cache entry keyed to content that changes every request — paying the write premium for something that can never be read back.

func (*Claude) ApplyPolicyNow added in v0.11.0

func (c *Claude) ApplyPolicyNow(ctx context.Context, policy chat.HistoryPolicy) (int, error)

ApplyPolicyNow bounds the retained conversation immediately, rather than waiting for the next request to do it.

It is the same three steps this client already runs before every request — describe the turns, ask the policy, rewrite from the result — with the difference that there is no pending turn to hold out of the budget. Every retained turn is a candidate, because nothing is about to be answered.

A nil policy means the configured one. With none configured there is nothing to apply and it returns zero rather than an error: a caller typing /compact on a client with no policy has asked for something reasonable that happens to be a no-op.

func (*Claude) Ask

func (c *Claude) Ask(ctx context.Context, question string, target any, media ...chat.Media) error

Ask sends a question to the Claude chat client and expects a structured response.

func (*Claude) Chat

func (c *Claude) Chat(ctx context.Context, prompt string, media ...chat.Media) (string, error)

func (*Claude) History added in v0.9.0

func (c *Claude) History() chat.History

History reports the conversation this client will re-send, counting both the committed turns and any buffered by Add and not yet sent.

Known is true: this provider owns its transcript, so the count is authoritative. A stateless client retains nothing between calls and reports only what is currently buffered.

func (*Claude) Model added in v0.6.0

func (c *Claude) Model() string

Model implements chat.ModelIdentifier, reporting the model this client will actually use — the configured one, or the default it fell back to.

func (*Claude) Provider added in v0.6.0

func (c *Claude) Provider() chat.Provider

Provider implements chat.ModelIdentifier.

func (*Claude) Restore

func (c *Claude) Restore(snapshot *chat.Snapshot) error

Restore replaces the current conversation state with a previously saved snapshot.

func (*Claude) Save

func (c *Claude) Save() (*chat.Snapshot, error)

Save captures the current Claude conversation state as a snapshot.

A stateless client retains no conversation, so its snapshot carries an empty message list. That is honest rather than useless: the provider, model and system prompt still round-trip.

func (*Claude) SetTools

func (c *Claude) SetTools(tools []chat.Tool) error

SetTools configures the tools available to the AI.

func (*Claude) StreamChat

func (c *Claude) StreamChat(ctx context.Context, prompt string, callback chat.StreamCallback, media ...chat.Media) (string, error)

StreamChat implements StreamingChatClient.

func (*Claude) SupportsEffort added in v0.3.0

func (c *Claude) SupportsEffort()

SupportsEffort marks Claude as able to carry Config.Effort, via output_config.effort. The API's ladder is low/medium/high/xhigh/max, which is exactly the neutral ladder, so nothing is clamped or invented.

func (*Claude) SupportsSampling added in v0.3.0

func (c *Claude) SupportsSampling()

SupportsSampling marks Claude as able to carry Config.Temperature and TopP.

Structural only. Whether the selected *model* accepts them is a separate question that cannot be answered here — claude-opus-4-8 reports temperature as deprecated while claude-sonnet-4-5 accepts it — and surfaces as chat.ErrModelRejectedParameter when the request is made.

func (*Claude) SupportsStateless added in v0.2.0

func (c *Claude) SupportsStateless()

SupportsStateless marks Claude as honouring chat.Config.Stateless.

type ClaudeLocal added in v0.13.0

type ClaudeLocal struct {
	chat.UsageTracker
	// contains filtered or unexported fields
}

ClaudeLocal implements the chat.ChatClient interface using a locally installed claude CLI binary. This provider is useful in environments where direct API access to api.anthropic.com is blocked but the pre-authenticated claude binary is permitted.

func (*ClaudeLocal) Add added in v0.13.0

func (c *ClaudeLocal) Add(_ context.Context, prompt string, media ...chat.Media) error

Add buffers a user message to be prepended to the next Chat or Ask call.

func (*ClaudeLocal) Ask added in v0.13.0

func (c *ClaudeLocal) Ask(ctx context.Context, question string, target any, media ...chat.Media) error

Ask sends a question to the local claude binary and unmarshals the structured response into the target using --json-schema for schema-enforced output.

func (*ClaudeLocal) Chat added in v0.13.0

func (c *ClaudeLocal) Chat(ctx context.Context, prompt string, media ...chat.Media) (string, error)

Chat sends a message to the local claude binary and returns the text response.

func (*ClaudeLocal) History added in v0.13.0

func (c *ClaudeLocal) History() chat.History

chat.History reports what this client is carrying.

Turns counts only the locally buffered Add() turns, and Known is false: the conversation itself lives in the claude CLI's own session, resumed by ID, so the transcript the far side will replay is not something this provider can see or count. Reporting a confident number for the buffer alone would be worse than admitting the gap — a caller watching for growth would see a conversation that never grows.

func (*ClaudeLocal) SetTools added in v0.13.0

func (c *ClaudeLocal) SetTools(tools []chat.Tool) error

SetTools registers the tools the model may call.

The handlers stay in this process. Each call publishes them on a loopback MCP server for the life of that subprocess and points the CLI at it, because the claude binary runs in its own process and cannot reach a Go closure any other way. Registering an empty set turns the feature off again. Spec 0009.

func (*ClaudeLocal) SupportsEffort added in v0.13.0

func (c *ClaudeLocal) SupportsEffort()

SupportsEffort marks ClaudeLocal as honouring chat.Config.Effort. The CLI's --effort flag takes the same five levels as the Anthropic API it wraps, so the neutral ladder maps across without clamping.

Note the absence of SupportsSampling: the claude CLI exposes no temperature or top-p flag at any version, so ClaudeLocal is the provider that holds one generation-capability marker and not the other.

func (*ClaudeLocal) SupportsStateless added in v0.13.0

func (c *ClaudeLocal) SupportsStateless()

SupportsStateless marks ClaudeLocal as honouring chat.Config.Stateless.

Statelessness here means not resuming: a stateless call omits --resume and discards the session ID the CLI returns, so each invocation starts a fresh session and the transcript never accumulates on the far side.

type ClaudeLocalOption added in v0.13.0

type ClaudeLocalOption func(*claudeLocalOptions)

ClaudeLocalOption configures a chat.ProviderClaudeLocal client at construction.

These knobs live here rather than on chat.Config because only one provider reads them. chat.Config carries what every provider understands; anything a single provider owns belongs to that provider's own constructor, where the compiler can see it. See https://gitlab.com/phpboyscout/go/chat/-/wikis/specs/0011-chat-provider-conformance D10.

func WithClaudeBinaryLookup added in v0.13.0

func WithClaudeBinaryLookup(lookPath func(string) (string, error)) ClaudeLocalOption

WithClaudeBinaryLookup overrides how the claude binary is located, replacing exec.LookPath. It is the seam for testing the not-installed path without a machine that lacks the binary.

func WithClaudeCommand added in v0.13.0

func WithClaudeCommand(command func(context.Context, string, ...string) *exec.Cmd) ClaudeLocalOption

WithClaudeCommand overrides how the claude subprocess is built, replacing exec.CommandContext. It is the seam that lets the provider be driven against a fake subprocess — the provider-conformance suite uses it to supply backends that hang, fail or return a scripted result.

func WithClaudeEnvironment added in v0.13.0

func WithClaudeEnvironment(names ...string) ClaudeLocalOption

WithClaudeEnvironment names further environment variables to pass to the subprocess, on top of DefaultClaudeEnvironment.

Reach for it when the CLI needs something the default two do not carry: a proxy variable, ANTHROPIC_CONFIG_DIR, or an ANTHROPIC_* route the caller genuinely wants. A name whose variable is unset in this process is skipped.

func WithToolServer added in v0.17.0

func WithToolServer(server chat.ToolServer) ClaudeLocalOption

WithToolServer gives this client its own server for the tools passed to SetTools, in place of the program's registered one. It works with none registered, so a test fake or a wrapper that audits one client's tool calls needs no program-wide change. Spec 0028 D8.

func WithUnrestrictedCLI added in v0.13.1

func WithUnrestrictedCLI() ClaudeLocalOption

WithUnrestrictedCLI gives the model back the claude CLI's own tools: the ones that read and write files, run commands and code, WebFetch, the user, project and local settings files, and any MCP server the machine has configured.

It is off by default because a chat client's prompt is frequently not trusted and the CLI is an agentic tool: without the restrictions, a prompt reached a shell as the calling user (chat#22, measured against 2.1.260). By default the model is offered no CLI tool at all, only the caller's own tools and, for Ask, the CLI's structured-output tool (chat-anthropic#7, measured against 2.1.288). Every other provider in this family sends text to somebody else's API, where the worst case is a bad answer.

Reach for it when you want claude-local to behave like the CLI does in a terminal, and you control the prompt.

Directories

Path Synopsis
internal
detectmodels command
Command detectmodels reports when Anthropic has shipped something the module's default model does not reflect.
Command detectmodels reports when Anthropic has shipped something the module's default model does not reflect.
genmodels command
Command genmodels regenerates the per-model capability table.
Command genmodels regenerates the per-model capability table.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL