provider

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: May 22, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package provider abstracts the LLM behind a tool-use loop. The agent in internal/agent is provider-agnostic: it builds Requests in the shape defined here and feeds them through whichever Provider implementation the caller selected on the command line. Each provider package (anthropic.go, openai.go, gemini.go, compat.go) converts to and from the upstream SDK's own request/response types.

Why a shared shape: every modern tool-use API converges on the same abstract flow -- a system prompt, a conversation of typed content blocks, a tool registry, and a stop reason that signals whether the model wants another tool call or is done. Mapping into this shape at the adapter boundary keeps the agent loop a few dozen lines and makes it trivial to add or swap providers without rewriting the orchestration.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Anthropic

type Anthropic struct {
	// contains filtered or unexported fields
}

Anthropic is the reference Provider implementation. It maps the provider.Request/Response abstraction directly to Anthropic's native tool-use protocol, which is the shape the abstraction was designed around -- the conversion is mostly field-renaming.

func NewAnthropic

func NewAnthropic(apiKey string) *Anthropic

NewAnthropic constructs a Provider backed by Anthropic's Messages API. apiKey is required (empty string surfaces an error on the first Complete call rather than at construction; this lets callers build the provider once and resolve credentials lazily). Model names are passed through verbatim -- the SDK accepts every current Claude model string ("claude-opus-4-7", "claude-sonnet-4-6", ...) without us mapping them.

func (*Anthropic) Complete

func (a *Anthropic) Complete(ctx context.Context, req Request) (*Response, error)

func (*Anthropic) Name

func (a *Anthropic) Name() string

type BlockType

type BlockType string

BlockType discriminates the union in ContentBlock. We do not try to encode every provider-specific block type (image, thinking, citations) because the audit agent does not need them; if a future feature requires it, add a new BlockType here and a fresh case in the adapter conversions.

const (
	BlockText       BlockType = "text"
	BlockToolUse    BlockType = "tool_use"
	BlockToolResult BlockType = "tool_result"
)

type ContentBlock

type ContentBlock struct {
	Type BlockType

	// Type == BlockText
	Text string

	// Type == BlockToolUse (assistant turn)
	ToolUseID string
	ToolName  string
	ToolInput json.RawMessage

	// Type == BlockToolResult (user turn following a tool_use)
	ToolResultID string
	ToolResult   string
	IsError      bool
}

ContentBlock is the tagged union for one piece of message content. Only the fields relevant to Type are populated; consumers must switch on Type before reading provider-specific fields.

type Gemini

type Gemini struct {
	// contains filtered or unexported fields
}

Gemini maps the provider abstraction onto google.golang.org/genai. Each Complete call constructs a fresh client; the SDK is cheap to reinitialise and statelessness avoids leaking a long-lived HTTP connection across audit runs that may use different credentials.

func NewGemini

func NewGemini(apiKey string) *Gemini

NewGemini constructs a Provider backed by Google's Gemini API. apiKey lands in genai.ClientConfig.APIKey; we always force BackendGeminiAPI rather than letting the SDK auto-detect Vertex -- the audit agent is firmly a Gemini API consumer and an accidental switch to Vertex would silently change auth semantics.

Network errors and bad credentials surface only at the first Complete call, not here; the underlying genai.Client also does lazy authentication.

func (*Gemini) Complete

func (g *Gemini) Complete(ctx context.Context, req Request) (*Response, error)

func (*Gemini) Name

func (g *Gemini) Name() string

type Message

type Message struct {
	Role    Role
	Content []ContentBlock
}

Message is a single turn in the conversation. Content carries one or more typed blocks so a single assistant turn can interleave natural-language reasoning with tool_use blocks the way Anthropic and the OpenAI Responses API natively do.

type OpenAI

type OpenAI struct {
	// contains filtered or unexported fields
}

OpenAI maps provider.Request/Response to the Responses API. It covers the canonical openai.com endpoint and every OpenAI- compatible service that speaks the same wire shape.

func NewOpenAI

func NewOpenAI(apiKey string) *OpenAI

NewOpenAI constructs a Provider backed by the OpenAI Responses API. apiKey is the only required parameter; pass an empty apiBase to hit api.openai.com. The same factory powers NewOpenAICompatible, which simply forwards a non-empty apiBase so DeepSeek, Groq, Mistral, vLLM, Ollama, and any other OpenAI-compatible endpoint can use the identical Request/Response conversion code.

func NewOpenAICompatible

func NewOpenAICompatible(apiKey, apiBase string) (*OpenAI, error)

NewOpenAICompatible builds a Provider for any service that speaks the OpenAI Responses API at a different base URL: DeepSeek, Groq, Mistral's la-plateforme, vLLM, Ollama, llama.cpp's server, etc.

The implementation is the OpenAI adapter with the base URL pinned to apiBase and the reported provider name overridden so logs and error messages distinguish "openai-compatible" from the canonical "openai" path -- otherwise a misconfigured api-base would look like a real OpenAI failure.

apiBase is required; an empty base would silently fall back to api.openai.com, which is almost certainly not what the operator asked for when they selected --provider openai-compatible.

func NewOpenAIWithBase

func NewOpenAIWithBase(apiKey, apiBase string) *OpenAI

NewOpenAIWithBase mirrors NewOpenAI but lets the caller pin the API base URL. Empty apiBase means "use the SDK default", which is api.openai.com.

func (*OpenAI) Complete

func (o *OpenAI) Complete(ctx context.Context, req Request) (*Response, error)

func (*OpenAI) Name

func (o *OpenAI) Name() string

type Provider

type Provider interface {
	// Name returns a short identifier ("anthropic", "openai",
	// "gemini", "openai-compatible") used in logs and error messages.
	Name() string

	// Complete runs one round-trip against the model. The agent loop
	// is responsible for repeated calls when StopReasonToolUse comes
	// back; the provider only owns the single network call.
	Complete(ctx context.Context, req Request) (*Response, error)
}

Provider is the single seam between the agent and an LLM. An implementation must be safe to call concurrently from independent goroutines; the agent does not assume any per-call sticky state on the provider side beyond what the underlying SDK already provides (HTTP keep-alive, prompt cache, etc.).

type Request

type Request struct {
	System    string
	Messages  []Message
	Tools     []ToolSpec
	Model     string
	MaxTokens int
}

Request is the abstract conversation handed to the model. System goes into whichever first-class field the provider exposes (system, system instruction, developer role) rather than being stuffed into the message list -- providers cache the system prompt differently from user turns and we want them to.

type Response

type Response struct {
	Content    []ContentBlock
	StopReason StopReason
	Usage      Usage
}

Response is what a single Complete call produced. Content carries the same ContentBlock union, this time populated by the provider (an assistant turn). Usage is best-effort -- some providers do not report per-call usage at all.

type Role

type Role string

Role enumerates who produced a Message. Tool results travel under RoleUser because every provider treats them as user-supplied input to the model regardless of MCP's "tool" role concept.

const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
)

type StopReason

type StopReason string

StopReason tells the agent loop why the model stopped. EndTurn and ToolUse are the two important cases; MaxTokens and Other surface so the agent can record a useful error rather than retrying blindly.

const (
	StopReasonEndTurn   StopReason = "end_turn"
	StopReasonToolUse   StopReason = "tool_use"
	StopReasonMaxTokens StopReason = "max_tokens"
	StopReasonOther     StopReason = "other"
)

type ToolSpec

type ToolSpec struct {
	Name        string
	Description string
	InputSchema json.RawMessage
}

ToolSpec is the schema we advertise for one tool. InputSchema is the raw JSON Schema object the provider will forward to the model; each adapter knows how to embed it in its native request shape.

type Usage

type Usage struct {
	InputTokens         int
	OutputTokens        int
	CacheCreationTokens int
	CacheReadTokens     int
}

Usage is best-effort token accounting. Zero values mean "provider did not report"; the agent must not treat zero as "free".

Jump to

Keyboard shortcuts

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