ai

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package ai implements the BYOK assistant chat: a provider-agnostic turn engine. Mutating tool calls pause for explicit human confirmation; no AI runs in the reconciliation path.

Index

Constants

View Source
const (
	EnvStepTimeout        = "APP_AI_STEP_TIMEOUT"
	EnvMaxSteps           = "APP_AI_MAX_STEPS"
	EnvToolOutputMaxBytes = "APP_AI_TOOL_OUTPUT_MAX_BYTES"
	EnvDefaultContext     = "APP_AI_DEFAULT_CONTEXT_TOKENS"
	EnvMonthlyTokenBudget = "APP_AI_MONTHLY_TOKEN_BUDGET" //nolint:gosec // an env var name, not a credential
	EnvModelsCacheTTL     = "APP_AI_MODELS_CACHE_TTL"
	EnvRetryMaxAttempts   = "APP_AI_RETRY_MAX_ATTEMPTS"
	EnvRetryBaseDelay     = "APP_AI_RETRY_BASE_DELAY"
	EnvRetryMaxDelay      = "APP_AI_RETRY_MAX_DELAY"
	EnvValidateTimeout    = "APP_AI_VALIDATE_TIMEOUT"
)

Environment variables that tune the assistant harness.

View Source
const (
	RoleUser      = "user"
	RoleAssistant = "assistant"
)

There is no tool role: a tool result is a tool_result block inside a user-role message, as the Messages API requires.

View Source
const (
	ContentBlockText       = "text"
	ContentBlockToolUse    = "tool_use"
	ContentBlockToolResult = "tool_result"
)

ContentBlock kinds a Message can carry.

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

Stop reasons TurnResult.StopReason can carry, a small subset of Anthropic's own stop_reason values relevant to this engine's loop.

View Source
const MaxFallback = 5

MaxFallback bounds the fallback chain length.

View Source
const SecretsAPIKeyEnvKey = "api_key" //nolint:gosec // an envKey name, not a credential value

SecretsAPIKeyEnvKey is the internal/secrets envKey every provider's API key is stored under, shared by the vault and the API handlers.

Variables

View Source
var (
	// ErrNotConfigured means no provider has been configured yet.
	// Callers (internal/api) map this to 501, the same "not configured"
	// shape WithEmailSecrets' absence produces.
	ErrNotConfigured = errors.New("ai: assistant is not configured")
	// ErrPendingConfirmation is returned while a mutating tool call awaits confirmation, so no tool_result is ever missing.
	ErrPendingConfirmation = errors.New("ai: a previous tool call is still awaiting confirmation")
	// ErrConfirmationSessionMismatch means the confirmation id exists but
	// belongs to a different session than the URL's {id} named.
	ErrConfirmationSessionMismatch = errors.New("ai: confirmation does not belong to this session")
	// ErrConfirmationAlreadyResolved means POST .../confirmations/{id}
	// was called a second time for an id that already left "pending".
	ErrConfirmationAlreadyResolved = errors.New("ai: confirmation already resolved")
)
View Source
var (
	ErrUnknownProvider = errors.New("ai: unknown provider")
	ErrNoUsableChain   = errors.New("ai: active provider has no usable key")
)

Vault errors the API maps to status codes.

View Source
var ErrBaseURLRejected = errors.New("ai: provider base URL is not allowed")

ErrBaseURLRejected means a provider base URL failed the SSRF policy.

View Source
var ErrMaxSteps = errors.New("ai: turn stopped at the maximum number of tool steps")

ErrMaxSteps means a turn used its whole tool step budget without a final answer.

View Source
var ErrNoTurnCredential = errors.New("ai: no turn credential: tools run only as the human who sent the message")

ErrNoTurnCredential means a turn reached the tool layer without the short-lived credential of the human who sent it.

Functions

func GuardContext

func GuardContext(system string, tools []ToolSpec, msgs []Message, window int) ([]Message, GuardReport, error)

GuardContext shrinks msgs to fit window without touching the system prompt, tools, or latest user turn, and errors with context_too_long if that is not enough.

func HTTPStatusFor

func HTTPStatusFor(err error) int

HTTPStatusFor maps a Vault error to an HTTP status for the API layer.

func ModelsCacheTTL

func ModelsCacheTTL() time.Duration

ModelsCacheTTL is how long a provider's model list is reused.

func NewHTTPClient

func NewHTTPClient(allowPrivate bool) *http.Client

NewHTTPClient returns a provider-traffic client that refuses private IPs unless allowPrivate, and refuses cross-host redirects.

func Scrub

func Scrub(s, key string) string

Scrub removes the key and key-shaped tokens from provider-supplied text.

func TruncateToolOutput

func TruncateToolOutput(s string, limit int) string

TruncateToolOutput caps s at limit bytes on a rune boundary and appends a marker saying how many bytes were cut. limit <= 0 disables the cap.

func ValidateTimeout

func ValidateTimeout() time.Duration

ValidateTimeout bounds the key check on save and test.

func ValidateToolArgs

func ValidateToolArgs(schema, args json.RawMessage) error

ValidateToolArgs checks model-supplied tool arguments against a JSON Schema subset before the tool runs.

func WithAttemptObserver

func WithAttemptObserver(ctx context.Context, fn func(Attempt)) context.Context

WithAttemptObserver makes chains report every attempt to fn.

func WithTurnCredential added in v0.3.0

func WithTurnCredential(ctx context.Context, c TurnCredential) context.Context

WithTurnCredential attaches the turn's credential to ctx.

Types

type AdapterConfig

type AdapterConfig struct {
	ProviderID string
	BaseURL    string
	APIKey     string
	HTTP       *http.Client
}

AdapterConfig is what every adapter needs to talk to one provider.

type AnthropicProvider

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

AnthropicProvider implements Backend against the Anthropic Messages API, using the streaming (server-sent events) variant so TextDeltaFunc gets real incremental chunks.

func NewAnthropic

func NewAnthropic(cfg AdapterConfig) *AnthropicProvider

NewAnthropic builds the adapter from cfg; an empty BaseURL means the public API.

func NewAnthropicProvider

func NewAnthropicProvider(apiKey string, httpClient *http.Client) *AnthropicProvider

NewAnthropicProvider builds a provider using apiKey and httpClient (guarded default if nil), pointed at the public API.

func (*AnthropicProvider) Complete

func (p *AnthropicProvider) Complete(ctx context.Context, req ChatRequest, onText TextDeltaFunc) (TurnResult, error)

Complete implements Provider by POSTing req to the Messages API with stream:true and parsing the server-sent event stream, forwarding every text delta to onText as it arrives.

func (*AnthropicProvider) ListModels

func (p *AnthropicProvider) ListModels(ctx context.Context) ([]ModelInfo, error)

ListModels implements Backend via GET /v1/models.

func (*AnthropicProvider) Validate

func (p *AnthropicProvider) Validate(ctx context.Context) error

Validate implements Backend with one model-list call.

type AssistantSettings

type AssistantSettings struct {
	Configured bool
	Active     *store.AIModelRef
	Fallback   []store.AIModelRef
}

AssistantSettings is the API shape of GET /settings/ai-assistant.

type Attempt

type Attempt struct {
	Provider   string
	Model      string
	Usage      Usage
	Latency    time.Duration
	ErrorClass ErrorClass
}

Attempt describes one provider call inside a chain, for metering.

type AttributedSink added in v0.3.0

type AttributedSink interface {
	ToolResultAttributed(toolUseID, toolName string, result json.RawMessage, isError bool, attr ToolAttribution)
}

AttributedSink is a Sink that also streams a tool result's attribution.

type Backend

type Backend interface {
	Provider
	ListModels(ctx context.Context) ([]ModelInfo, error)
	// Validate makes one cheap authenticated call and returns a *ProviderError on failure.
	Validate(ctx context.Context) error
}

Backend is a Provider that can also list its models and validate its key.

func DefaultBackendFactory

func DefaultBackendFactory(info ProviderInfo, cfg AdapterConfig) Backend

DefaultBackendFactory picks the adapter by catalog kind.

type BackendFactory

type BackendFactory func(info ProviderInfo, cfg AdapterConfig) Backend

BackendFactory builds an adapter; tests swap it to point at fake servers.

type BaseURLCheck

type BaseURLCheck struct {
	URL     string
	Private bool
}

BaseURLCheck is the verdict on one provider base URL.

func CheckBaseURL

func CheckBaseURL(ctx context.Context, raw string, kind Kind, privateAllowed bool, resolve HostResolver) (BaseURLCheck, error)

CheckBaseURL validates a provider base URL: absolute http(s), no credentials, and https unless private hosts are allowed for openai_compat.

type ChainEntry

type ChainEntry struct {
	Provider      string
	Model         string
	Backend       Provider
	ContextWindow int
}

ChainEntry is one provider:model the chain may call.

type ChainProvider

type ChainProvider struct {
	Entries        []ChainEntry
	DefaultContext int
	Logger         *slog.Logger
	Now            func() time.Time
}

ChainProvider tries entries in order and falls back only on key- and content-independent failures before any output streams.

func (*ChainProvider) Complete

func (c *ChainProvider) Complete(ctx context.Context, req ChatRequest, onText TextDeltaFunc) (TurnResult, error)

Complete implements Provider. req.Model is ignored: each entry names its own.

type ChatRequest

type ChatRequest struct {
	System   string
	Messages []Message
	Tools    []ToolSpec
	Model    string
}

ChatRequest is one call to Provider.Complete: the full conversation so far (system prompt separate, matching Anthropic's own top-level `system` field), the tool catalog, and which model to use.

type ContentBlock

type ContentBlock struct {
	Type string

	// Text is set when Type is ContentBlockText.
	Text string

	// ToolUseID, ToolName, ToolInput are set when Type is
	// ContentBlockToolUse: a tool the model wants to invoke.
	ToolUseID string
	ToolName  string
	ToolInput json.RawMessage

	// ToolResultForID, ToolResultContent, ToolResultIsError are set when
	// Type is ContentBlockToolResult: an already-executed tool's outcome,
	// keyed back to the ContentBlockToolUse.ToolUseID it answers.
	ToolResultForID   string
	ToolResultContent string
	ToolResultIsError bool
}

ContentBlock is one block of a Message's content, mirroring the Anthropic Messages API's own content-block union closely enough that building a request is a direct translation, not a redesign.

type Engine

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

Engine runs the chat turn loop, auto-running read-only tools and pausing mutating ones for confirmation.

func NewEngine

func NewEngine(s Store, resolver ProviderResolver, tools ToolExecutor, brandName string) *Engine

NewEngine builds an Engine. brandName names the platform in the system prompt (the caller's own brand.Brand.Name, per section 3 of CLAUDE.md); "" falls back to defaultPlatformName rather than a hardcoded product name.

func (*Engine) IsConfigured

func (e *Engine) IsConfigured(ctx context.Context) (bool, error)

IsConfigured reports whether a provider chain is usable, so the API layer can return 501 before starting an SSE response.

func (*Engine) ResolveConfirmation

func (e *Engine) ResolveConfirmation(ctx context.Context, sessionID, confirmationID string, approve bool, sink Sink) error

ResolveConfirmation executes or rejects a proposed mutating call and continues the turn once all its calls resolve.

func (*Engine) RunTurn

func (e *Engine) RunTurn(ctx context.Context, sessionID, userContent string, sink Sink) error

RunTurn appends userContent and runs the turn loop until the model stops or a mutating call needs confirmation.

func (*Engine) SetLimits

func (e *Engine) SetLimits(l Limits)

SetLimits overrides the harness limits (default: from the environment).

func (*Engine) SetLogger

func (e *Engine) SetLogger(l *slog.Logger)

SetLogger sets the engine logger.

func (*Engine) SetToolFactory added in v0.3.0

func (e *Engine) SetToolFactory(f ToolFactory)

SetToolFactory makes every turn build its tools from the sender's own turn credential; a turn without one is refused rather than run with e.tools.

func (*Engine) SetUsage

func (e *Engine) SetUsage(u UsageStore)

SetUsage enables metering and the monthly budget.

type ErrorClass

type ErrorClass string

ErrorClass names why a provider call failed, in terms an operator can act on.

const (
	ClassInvalidKey      ErrorClass = "invalid_key"
	ClassQuotaExhausted  ErrorClass = "quota_exhausted"
	ClassRateLimited     ErrorClass = "rate_limited"
	ClassOverloaded      ErrorClass = "overloaded"
	ClassContextTooLong  ErrorClass = "context_too_long"
	ClassModelNotFound   ErrorClass = "model_not_found"
	ClassNetwork         ErrorClass = "network"
	ClassContentRefused  ErrorClass = "content_refused"
	ClassStreamCut       ErrorClass = "stream_interrupted"
	ClassBadRequest      ErrorClass = "bad_request"
	ClassBlockedAddress  ErrorClass = "blocked_address"
	ClassBudgetExhausted ErrorClass = "budget_exhausted"
)

Error classes a provider call can end in.

type ErrorSink

type ErrorSink interface {
	TurnError(info TurnErrorInfo)
}

ErrorSink is an optional Sink extension that receives a typed turn failure.

type Gemini

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

Gemini implements Backend against the native Gemini generateContent API.

func NewGemini

func NewGemini(cfg AdapterConfig) *Gemini

NewGemini builds the adapter; an empty BaseURL means the public API.

func (*Gemini) Complete

func (p *Gemini) Complete(ctx context.Context, req ChatRequest, onText TextDeltaFunc) (TurnResult, error)

Complete implements Provider via streamGenerateContent with alt=sse.

func (*Gemini) ListModels

func (p *Gemini) ListModels(ctx context.Context) ([]ModelInfo, error)

ListModels implements Backend via GET /models, keeping models that can generate content.

func (*Gemini) Validate

func (p *Gemini) Validate(ctx context.Context) error

Validate implements Backend with one model-list call.

type GuardReport

type GuardReport struct {
	DroppedToolResults int
	DroppedMessages    int
}

GuardReport says what GuardContext changed.

type HostResolver

type HostResolver func(ctx context.Context, host string) ([]netip.Addr, error)

HostResolver resolves a hostname for the SSRF policy check; swappable in tests.

type Kind

type Kind string

Kind selects the wire adapter a provider uses.

const (
	KindAnthropic    Kind = "anthropic"
	KindOpenAICompat Kind = "openai_compat"
	KindGemini       Kind = "gemini"
)

Adapter kinds.

type Limits

type Limits struct {
	StepTimeout        time.Duration
	MaxSteps           int
	ToolOutputMaxBytes int
	DefaultContext     int
	MonthlyTokenBudget int64
}

Limits are the harness bounds, resolved from the environment.

func LimitsFromEnv

func LimitsFromEnv() Limits

LimitsFromEnv reads the harness limits; unset or invalid values use defaults, and a budget of 0 means unlimited.

type Message

type Message struct {
	Role    string
	Content []ContentBlock
}

Message is one turn in a conversation, provider-agnostic. Engine reconstructs this from store.AIChatMessage rows on every turn; Provider implementations translate it into their own wire format.

type ModelInfo

type ModelInfo struct {
	ID            string `json:"id"`
	Label         string `json:"label"`
	ContextWindow int    `json:"context_window,omitempty"`
	SupportsTools *bool  `json:"supports_tools,omitempty"`
}

ModelInfo is one model a provider offers, normalized across adapters.

type OpenAICompat

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

OpenAICompat implements Backend for any server speaking OpenAI Chat Completions: OpenAI, Azure OpenAI, OpenRouter, Groq, Ollama, vLLM and so on.

func NewOpenAICompat

func NewOpenAICompat(cfg AdapterConfig) *OpenAICompat

NewOpenAICompat builds the adapter; BaseURL must be set.

func (*OpenAICompat) Complete

func (p *OpenAICompat) Complete(ctx context.Context, req ChatRequest, onText TextDeltaFunc) (TurnResult, error)

Complete implements Provider with a streaming Chat Completions call.

func (*OpenAICompat) ListModels

func (p *OpenAICompat) ListModels(ctx context.Context) ([]ModelInfo, error)

ListModels implements Backend via GET {base}/models.

func (*OpenAICompat) Validate

func (p *OpenAICompat) Validate(ctx context.Context) error

Validate lists models; a server without a models route (404 or 405) still counts as reachable, since only auth and transport failures matter here.

type Provider

type Provider interface {
	Complete(ctx context.Context, req ChatRequest, onText TextDeltaFunc) (TurnResult, error)
}

Provider sends one turn to an LLM and streams its text back. Tests substitute a fake with no network.

type ProviderError

type ProviderError struct {
	Provider   string
	Class      ErrorClass
	Status     int
	RetryAfter time.Duration
	Detail     string
}

ProviderError is a classified provider failure. It never carries the API key.

func AsProviderError

func AsProviderError(err error) (*ProviderError, bool)

AsProviderError unwraps a ProviderError from err.

func ClassifyHTTP

func ClassifyHTTP(provider string, status int, body []byte, h http.Header, key string, now time.Time) *ProviderError

ClassifyHTTP maps a non-2xx provider response to a ProviderError.

func ClassifyTransport

func ClassifyTransport(provider string, err error, key string) *ProviderError

ClassifyTransport maps a transport failure (dial, TLS, timeout) to a ProviderError.

func (*ProviderError) Error

func (e *ProviderError) Error() string

func (*ProviderError) Explain

func (e *ProviderError) Explain() (message, fix string)

Explain returns a plain-language cause and a suggested fix for the UI.

func (*ProviderError) FallbackEligible

func (e *ProviderError) FallbackEligible() bool

FallbackEligible reports whether another provider may serve the call: only failures that do not depend on this request's key or content.

func (*ProviderError) Retryable

func (e *ProviderError) Retryable() bool

Retryable reports whether repeating the same call may succeed.

type ProviderInfo

type ProviderInfo struct {
	ID              string
	Label           string
	Kind            Kind
	DefaultBaseURL  string
	BaseURLEditable bool
	KeyURL          string
	DefaultModel    string
	Brand           string
	// KeyRequired is false for self-hosted servers that run without auth.
	KeyRequired bool
}

ProviderInfo is one static catalog entry.

func Catalog

func Catalog() []ProviderInfo

Catalog returns every supported provider in display order. Default models are suggestions from the catalog author, not live-verified.

func LookupProvider

func LookupProvider(id string) (ProviderInfo, bool)

LookupProvider returns the catalog entry for id.

type ProviderResolver

type ProviderResolver interface {
	Configured(ctx context.Context) (bool, error)
	Chain(ctx context.Context) (Provider, error)
}

ProviderResolver supplies the provider chain for a turn.

type ProviderView

type ProviderView struct {
	ID              string `json:"id"`
	Label           string `json:"label"`
	Kind            Kind   `json:"kind"`
	DefaultBaseURL  string `json:"default_base_url"`
	BaseURLEditable bool   `json:"base_url_editable"`
	KeyURL          string `json:"key_url"`
	DefaultModel    string `json:"default_model"`
	Brand           string `json:"brand"`
	HasKey          bool   `json:"has_key"`
	BaseURL         string `json:"base_url,omitempty"`
	Status          string `json:"status"`
	StatusDetail    string `json:"status_detail,omitempty"`
	LastCheckedAt   string `json:"last_checked_at,omitempty"`
}

ProviderView is the API shape of one provider.

type RetryPolicy

type RetryPolicy struct {
	MaxAttempts int
	BaseDelay   time.Duration
	MaxDelay    time.Duration
	// Jitter returns a value in [0,1); nil uses math/rand.
	Jitter func() float64
	// Sleep waits d or until ctx ends; nil uses a real timer.
	Sleep func(ctx context.Context, d time.Duration) error
}

RetryPolicy controls retry of retryable provider failures. Sleep and Jitter are injectable so tests run on a fake clock.

func RetryPolicyFromEnv

func RetryPolicyFromEnv() RetryPolicy

RetryPolicyFromEnv reads the retry knobs.

type RetryProvider

type RetryProvider struct {
	Inner  Provider
	Policy RetryPolicy
}

RetryProvider wraps a Provider, retrying retryable failures that happened before any output reached the caller (retrying later would duplicate text).

func (*RetryProvider) Complete

func (r *RetryProvider) Complete(ctx context.Context, req ChatRequest, onText TextDeltaFunc) (TurnResult, error)

Complete implements Provider.

type SaveError

type SaveError struct {
	Code    string
	Message string
	Fix     string
}

SaveError is a typed, user-presentable failure from Vault.Set. It never contains the API key.

func (*SaveError) Error

func (e *SaveError) Error() string

type SetInput

type SetInput struct {
	APIKey       string
	BaseURL      string
	DefaultModel string
}

SetInput is what PUT /providers/{id} accepts. Empty fields are unchanged.

type SetResult

type SetResult struct {
	PrivateBaseURL bool
}

SetResult reports facts the API layer audits.

type Sink

type Sink interface {
	TextDelta(text string)
	ToolCallProposed(confirmationID, toolUseID, toolName string, arguments json.RawMessage)
	ToolResult(toolUseID, toolName string, result json.RawMessage, isError bool)
}

Sink receives Engine events; the caller maps them to SSE types and sends the final done event itself.

type Store

type Store interface {
	ListAIChatMessages(ctx context.Context, sessionID string) ([]store.AIChatMessage, error)
	SaveAIChatMessage(ctx context.Context, m store.AIChatMessage) error
	UpdateAIChatMessageToolCalls(ctx context.Context, messageID string, toolCalls []store.AIChatToolCall) error
	GetAIChatMessage(ctx context.Context, id string) (store.AIChatMessage, error)
	SaveAIChatConfirmation(ctx context.Context, c store.AIChatConfirmation) error
	GetAIChatConfirmation(ctx context.Context, id string) (store.AIChatConfirmation, error)
	ResolveAIChatConfirmation(ctx context.Context, id, status string, result json.RawMessage, resolvedAt time.Time) error
	ListPendingAIChatConfirmations(ctx context.Context, messageID string) ([]store.AIChatConfirmation, error)
}

Store is the narrow internal/store surface Engine needs, satisfied structurally by *store.DB, the same "narrow interface at the boundary" shape internal/secrets.Store already establishes for that package.

type TestResult

type TestResult struct {
	OK           bool   `json:"ok"`
	Status       string `json:"status"`
	StatusDetail string `json:"status_detail,omitempty"`
	LatencyMS    int64  `json:"latency_ms"`
}

TestResult is the outcome of a key test.

type TextDeltaFunc

type TextDeltaFunc func(delta string)

TextDeltaFunc receives each streamed text chunk, so the SSE handler can forward it immediately.

type ToolAttribution added in v0.3.0

type ToolAttribution struct {
	ConfirmationID string
	ActingAs       string
	Abilities      []string
}

ToolAttribution says who a tool call ran as and which abilities it used.

type ToolCaller

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

ToolCaller runs tool calls against the platform's MCP tool surface in-process, over in-memory transports.

func NewToolCaller

func NewToolCaller(ctx context.Context, client *apiclient.Client) (*ToolCaller, error)

NewToolCaller builds a ToolCaller whose tools call back into the control plane's own REST API through client, exactly like an external MCP client (or cmd/levelrail-cli) would.

func (*ToolCaller) Call

func (tc *ToolCaller) Call(ctx context.Context, name string, args json.RawMessage) (result string, isError bool, err error)

Call invokes the named tool with args and returns its text result and whether the call errored.

func (*ToolCaller) Close

func (tc *ToolCaller) Close() error

Close ends the underlying MCP session.

func (*ToolCaller) ListTools

func (tc *ToolCaller) ListTools(ctx context.Context) ([]ToolSpec, error)

ListTools returns every tool the platform exposes, translated to the provider-agnostic ToolSpec shape.

type ToolExecutor

type ToolExecutor interface {
	ListTools(ctx context.Context) ([]ToolSpec, error)
	Call(ctx context.Context, name string, args json.RawMessage) (result string, isError bool, err error)
}

ToolExecutor is the narrow ToolCaller surface Engine needs; *ToolCaller satisfies this structurally, a fake satisfies it for tests without a real in-process MCP session.

type ToolFactory added in v0.3.0

type ToolFactory func(ctx context.Context, token string) (ToolExecutor, error)

ToolFactory builds the tool surface for one turn, authenticated with token.

type ToolSpec

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

ToolSpec is one tool's name, description, and input JSON Schema, as ToolCaller.ListTools returns it.

type ToolTraits

type ToolTraits struct {
	Known           bool
	ReadOnly        bool
	Destructive     bool
	OpenWorld       bool
	Sensitive       bool
	UntrustedOutput bool
}

ToolTraits is what the assistant knows about a tool from its MCP annotations. The zero value (Known false) is a tool with no annotations, which always requires confirmation.

func TraitsFromMCP

func TraitsFromMCP(a *mcp.ToolAnnotations, meta map[string]any) ToolTraits

TraitsFromMCP derives ToolTraits from a tool's annotations and _meta. Absent annotations mean unknown; an absent OpenWorldHint means open world, per the MCP default.

func (ToolTraits) IngestsUntrusted

func (t ToolTraits) IngestsUntrusted() bool

IngestsUntrusted reports whether running the tool puts untrusted text in the conversation. An unknown tool is assumed to.

func (ToolTraits) RequiresConfirmation

func (t ToolTraits) RequiresConfirmation(tainted bool) bool

RequiresConfirmation reports whether a call must pause for a human click; unknown tools always pause.

type ToolUseCall

type ToolUseCall struct {
	ID    string
	Name  string
	Input json.RawMessage
}

ToolUseCall is one tool invocation the model requested in a single turn, returned as part of TurnResult.

type TurnCredential added in v0.3.0

type TurnCredential struct {
	// Token is a short-lived bearer minted for this turn only, capped to the sender.
	Token string
	// ActingAs names the human principal the token stands in for.
	ActingAs string
	// AbilitiesUsed drains the abilities the API checked since its last call; nil is allowed.
	AbilitiesUsed func() []string
}

TurnCredential is the authority one chat turn's tool calls run with.

func TurnCredentialFrom added in v0.3.0

func TurnCredentialFrom(ctx context.Context) (TurnCredential, bool)

TurnCredentialFrom returns the turn credential attached to ctx, if any.

type TurnErrorInfo

type TurnErrorInfo struct {
	Code      string
	Message   string
	Fix       string
	Provider  string
	Retryable bool
}

TurnErrorInfo is a failure explained for the operator.

func DescribeError

func DescribeError(err error) TurnErrorInfo

DescribeError turns any turn error into a TurnErrorInfo.

type TurnResult

type TurnResult struct {
	Text       string
	ToolCalls  []ToolUseCall
	StopReason string
	Usage      Usage
}

TurnResult is the assistant's full text and requested tool calls for one model turn.

type Usage

type Usage struct {
	PromptTokens     int
	CompletionTokens int
}

Usage is the token accounting a provider reported for one call. Zero means the provider reported nothing.

type UsageStore

type UsageStore interface {
	RecordAIUsage(ctx context.Context, r store.AIUsageRecord) error
	SumAITokensSince(ctx context.Context, since time.Time) (int64, error)
}

UsageStore records metering rows and answers the monthly budget check.

type Vault

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

Vault manages per-provider keys and settings and builds the provider chain.

func NewVault

func NewVault(s VaultStore, sec VaultSecrets, logger *slog.Logger, opts ...VaultOption) *Vault

NewVault builds a Vault.

func (*Vault) Chain

func (v *Vault) Chain(ctx context.Context) (Provider, error)

Chain builds the provider chain for one turn: the active choice first, then each usable fallback. A fallback with a missing key is skipped; a missing key on the active choice fails closed.

func (*Vault) Configured

func (v *Vault) Configured(ctx context.Context) (bool, error)

Configured reports whether the active provider can serve a turn.

func (*Vault) Get

func (v *Vault) Get(ctx context.Context, id string) (ProviderView, error)

Get returns one provider's view.

func (*Vault) List

func (v *Vault) List(ctx context.Context) ([]ProviderView, error)

List returns every catalog provider with its saved state.

func (*Vault) Models

func (v *Vault) Models(ctx context.Context, id string) ([]ModelInfo, error)

Models lists a provider's models, cached for ModelsCacheTTL.

func (*Vault) Remove

func (v *Vault) Remove(ctx context.Context, id string) error

Remove deletes a provider's key and settings, and drops it from the active choice and fallback chain.

func (*Vault) Set

func (v *Vault) Set(ctx context.Context, id string, in SetInput, privateAllowed bool) (ProviderView, SetResult, error)

Set validates and stores a provider's key, base URL and default model; an invalid key is never stored.

func (*Vault) SetChoice

func (v *Vault) SetChoice(ctx context.Context, active *store.AIModelRef, fallback *[]store.AIModelRef) error

SetChoice replaces the active choice and/or fallback chain; nil leaves a part unchanged.

func (*Vault) Settings

func (v *Vault) Settings(ctx context.Context) (AssistantSettings, error)

Settings reports the active choice and fallback chain.

func (*Vault) Test

func (v *Vault) Test(ctx context.Context, id string) (TestResult, error)

Test re-validates the saved key and records the result.

type VaultOption

type VaultOption func(*Vault)

VaultOption configures a Vault.

func WithBackendFactory

func WithBackendFactory(f BackendFactory) VaultOption

WithBackendFactory overrides adapter construction.

func WithClock

func WithClock(now func() time.Time) VaultOption

WithClock overrides the vault clock.

func WithHostResolver

func WithHostResolver(r HostResolver) VaultOption

WithHostResolver overrides DNS resolution in the SSRF check.

func WithRetryPolicy

func WithRetryPolicy(p RetryPolicy) VaultOption

WithRetryPolicy overrides the retry policy used for chain calls.

type VaultSecrets

type VaultSecrets interface {
	SetValue(ctx context.Context, serviceName, envKey, plaintext string) error
	Resolve(ctx context.Context, serviceName, envKey string) (string, error)
	Exists(ctx context.Context, serviceName, envKey string) (bool, error)
	DeleteAll(ctx context.Context, serviceName string) error
}

VaultSecrets is the internal/secrets.Manager surface Vault needs. Keys are written here and read back only to build a provider call.

type VaultStore

type VaultStore interface {
	GetAIAssistantSettings(ctx context.Context) (store.AIAssistantSettings, error)
	UpdateAIAssistantSettings(ctx context.Context, s store.AIAssistantSettings) error
	GetAIProviderConfig(ctx context.Context, id string) (store.AIProviderConfig, error)
	UpsertAIProviderConfig(ctx context.Context, c store.AIProviderConfig) error
	ListAIProviderConfigs(ctx context.Context) ([]store.AIProviderConfig, error)
	DeleteAIProviderConfig(ctx context.Context, id string) error
	SetAIProviderStatus(ctx context.Context, id, status, detail string, at time.Time) error
}

VaultStore is the internal/store surface Vault needs.

Jump to

Keyboard shortcuts

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