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
- Variables
- func GuardContext(system string, tools []ToolSpec, msgs []Message, window int) ([]Message, GuardReport, error)
- func HTTPStatusFor(err error) int
- func ModelsCacheTTL() time.Duration
- func NewHTTPClient(allowPrivate bool) *http.Client
- func Scrub(s, key string) string
- func TruncateToolOutput(s string, limit int) string
- func ValidateTimeout() time.Duration
- func ValidateToolArgs(schema, args json.RawMessage) error
- func WithAttemptObserver(ctx context.Context, fn func(Attempt)) context.Context
- func WithTurnCredential(ctx context.Context, c TurnCredential) context.Context
- type AdapterConfig
- type AnthropicProvider
- type AssistantSettings
- type Attempt
- type AttributedSink
- type Backend
- type BackendFactory
- type BaseURLCheck
- type ChainEntry
- type ChainProvider
- type ChatRequest
- type ContentBlock
- type Engine
- func (e *Engine) IsConfigured(ctx context.Context) (bool, error)
- func (e *Engine) ResolveConfirmation(ctx context.Context, sessionID, confirmationID string, approve bool, sink Sink) error
- func (e *Engine) RunTurn(ctx context.Context, sessionID, userContent string, sink Sink) error
- func (e *Engine) SetLimits(l Limits)
- func (e *Engine) SetLogger(l *slog.Logger)
- func (e *Engine) SetToolFactory(f ToolFactory)
- func (e *Engine) SetUsage(u UsageStore)
- type ErrorClass
- type ErrorSink
- type Gemini
- type GuardReport
- type HostResolver
- type Kind
- type Limits
- type Message
- type ModelInfo
- type OpenAICompat
- type Provider
- type ProviderError
- type ProviderInfo
- type ProviderResolver
- type ProviderView
- type RetryPolicy
- type RetryProvider
- type SaveError
- type SetInput
- type SetResult
- type Sink
- type Store
- type TestResult
- type TextDeltaFunc
- type ToolAttribution
- type ToolCaller
- type ToolExecutor
- type ToolFactory
- type ToolSpec
- type ToolTraits
- type ToolUseCall
- type TurnCredential
- type TurnErrorInfo
- type TurnResult
- type Usage
- type UsageStore
- type Vault
- func (v *Vault) Chain(ctx context.Context) (Provider, error)
- func (v *Vault) Configured(ctx context.Context) (bool, error)
- func (v *Vault) Get(ctx context.Context, id string) (ProviderView, error)
- func (v *Vault) List(ctx context.Context) ([]ProviderView, error)
- func (v *Vault) Models(ctx context.Context, id string) ([]ModelInfo, error)
- func (v *Vault) Remove(ctx context.Context, id string) error
- func (v *Vault) Set(ctx context.Context, id string, in SetInput, privateAllowed bool) (ProviderView, SetResult, error)
- func (v *Vault) SetChoice(ctx context.Context, active *store.AIModelRef, fallback *[]store.AIModelRef) error
- func (v *Vault) Settings(ctx context.Context) (AssistantSettings, error)
- func (v *Vault) Test(ctx context.Context, id string) (TestResult, error)
- type VaultOption
- type VaultSecrets
- type VaultStore
Constants ¶
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.
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.
const ( ContentBlockText = "text" ContentBlockToolUse = "tool_use" ContentBlockToolResult = "tool_result" )
ContentBlock kinds a Message can carry.
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.
const MaxFallback = 5
MaxFallback bounds the fallback chain length.
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 ¶
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") )
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.
var ErrBaseURLRejected = errors.New("ai: provider base URL is not allowed")
ErrBaseURLRejected means a provider base URL failed the SSRF policy.
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.
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 ¶
HTTPStatusFor maps a Vault error to an HTTP status for the API layer.
func ModelsCacheTTL ¶
ModelsCacheTTL is how long a provider's model list is reused.
func NewHTTPClient ¶
NewHTTPClient returns a provider-traffic client that refuses private IPs unless allowPrivate, and refuses cross-host redirects.
func TruncateToolOutput ¶
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 ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
RunTurn appends userContent and runs the turn loop until the model stops or a mutating call needs confirmation.
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 ¶
ListModels implements Backend via GET /models, keeping models that can generate content.
type GuardReport ¶
GuardReport says what GuardContext changed.
type HostResolver ¶
HostResolver resolves a hostname for the SSRF policy check; swappable in tests.
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.
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 ¶
SaveError is a typed, user-presentable failure from Vault.Set. It never contains the API key.
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
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 ¶
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.
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 ¶
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 ¶
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 ¶
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 ¶
Configured reports whether the active provider can serve a turn.
func (*Vault) List ¶
func (v *Vault) List(ctx context.Context) ([]ProviderView, error)
List returns every catalog provider with its saved state.
func (*Vault) Remove ¶
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.
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.