Documentation
¶
Index ¶
- Variables
- func DetectBase64MediaType(data string) string
- func SetEgressPolicy(m EgressMode)
- func SyncEgressFromSettings()
- func WithSecretVault(ctx context.Context, v *SecretVault) context.Context
- type AnthropicProvider
- type ChatMessage
- type EgressFinding
- type EgressMode
- type Manager
- func (m *Manager) BestProvider(preferProvider, preferModel string) (provider, model string)
- func (m *Manager) Config(id string) (ProviderConfig, bool)
- func (m *Manager) DefaultModel(id string) string
- func (m *Manager) Get(id string) Provider
- func (m *Manager) IDs() []string
- func (m *Manager) InjectConfigs(cfgs []ProviderConfig)
- func (m *Manager) ListForUI() []map[string]any
- func (m *Manager) OllamaAPIKey() string
- func (m *Manager) Reload()
- func (m *Manager) ResolveProviderID(providerOrType string) (string, bool)
- func (m *Manager) SaveConfigs(cfgs []ProviderConfig) error
- func (m *Manager) SyncFromLiteKV()
- type ModelEntry
- type ModelInfo
- type OllamaProvider
- type OpenAIProvider
- type Provider
- type ProviderConfig
- type SecretVault
- type StreamEvent
- type StreamRequest
- type ThinkingBlock
- type ToolCall
- type ToolDef
- type ToolResult
Constants ¶
This section is empty.
Variables ¶
var EgressSink = func(findings []EgressFinding, model string) { parts := make([]string, len(findings)) for i, f := range findings { parts[i] = fmt.Sprintf("%s×%d", f.Type, f.Count) } fmt.Fprintf(os.Stderr, "[egress-guardrail] outbound request to model %q contains potential secrets/PII: %s (mode=%s)\n", model, strings.Join(parts, ", "), CurrentEgressPolicy()) }
EgressSink receives the aggregated findings for one outbound request. The default logs a masked summary to stderr; the app may override it to route findings to the UI, the audit log, or a metrics counter.
var Global = &Manager{}
Functions ¶
func DetectBase64MediaType ¶
DetectBase64MediaType returns the MIME type for a raw base64-encoded image by inspecting the magic bytes encoded at the start of the string.
func SetEgressPolicy ¶
func SetEgressPolicy(m EgressMode)
SetEgressPolicy sets the active guardrail mode.
func SyncEgressFromSettings ¶
func SyncEgressFromSettings()
SyncEgressFromSettings applies the guardrail mode from the persisted sidecar settings to EgressPolicy. Called at startup and whenever settings change so a stored or updated mode takes effect on the next request. (ai already depends on config, so this keeps the wiring out of the startup callers.)
The TOLLECODE_EGRESS environment variable overrides the persisted setting when set to off/log/redact — convenient for a one-off CLI run, e.g. `TOLLECODE_EGRESS=off tollecode`.
func WithSecretVault ¶
func WithSecretVault(ctx context.Context, v *SecretVault) context.Context
WithSecretVault attaches a vault to ctx. The agent executor does this once per run; the egress guardrail populates it as it redacts outbound requests, and the executor drains it when rehydrating tool input.
Types ¶
type AnthropicProvider ¶
type AnthropicProvider struct {
// contains filtered or unexported fields
}
AnthropicProvider implements Provider using the official Anthropic Go SDK.
func NewAnthropicProvider ¶
func NewAnthropicProvider(apiKey string) *AnthropicProvider
func (*AnthropicProvider) DiscoverModels ¶
func (p *AnthropicProvider) DiscoverModels(ctx context.Context) ([]ModelInfo, error)
func (*AnthropicProvider) Stream ¶
func (p *AnthropicProvider) Stream(ctx context.Context, req StreamRequest) (<-chan StreamEvent, error)
type ChatMessage ¶
type ChatMessage struct {
Role string // "user" | "assistant"
Content string
// For assistant turns that called tools:
ToolCalls []ToolCall
// Assistant turns with Anthropic extended thinking: the thinking blocks that
// led to this turn, replayed on the next request in the same tool-use turn.
ThinkingBlocks []ThinkingBlock
// For tool-result turns (role=="user" with tool content):
ToolResults []ToolResult
// User-attached images (base64-encoded, vision models only).
// Not persisted to disk — set only on the current turn's in-memory message.
Images []string
}
ChatMessage is one turn in a conversation history.
type EgressFinding ¶
type EgressFinding struct {
Type string `json:"type"`
Count int `json:"count"`
Sample string `json:"sample"`
}
EgressFinding aggregates one detector's hits in a request. Sample is masked and never contains the raw secret.
type EgressMode ¶
type EgressMode string
Egress guardrail: scans outbound LLM requests for secrets/PII before they leave the machine. Every adapter built by buildAdapter is wrapped by scanningProvider, so the check is uniform and cannot be bypassed by choosing a provider.
Posture is observe-first: the default mode is EgressLog, which flags what WOULD be redacted without altering the request, so operators can tune detectors before switching to EgressRedact. EgressOff disables scanning entirely.
const ( EgressOff EgressMode = "off" EgressLog EgressMode = "log" EgressRedact EgressMode = "redact" )
func CurrentEgressPolicy ¶
func CurrentEgressPolicy() EgressMode
CurrentEgressPolicy returns the active guardrail mode.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager loads and caches provider adapters from config.json.
func (*Manager) BestProvider ¶
BestProvider returns the best available provider ID and its default model. Priority: preferProvider (if set and available) > anthropic > openai > ollama-cloud > ollama > custom > other, alphabetical within a tier. Within the same tier, providers with a non-empty default model are preferred over those without one, so we never pick an Ollama instance that has no model configured.
func (*Manager) Config ¶
func (m *Manager) Config(id string) (ProviderConfig, bool)
Config returns the raw config for a provider.
func (*Manager) DefaultModel ¶
DefaultModel returns the default model ID for a provider. Priority: first model with IsDefault=true > cfg.DefaultModel > first model in the list. This intentionally prefers an explicit DefaultModel over the first entry in the Models array so that YAML `default_model` takes effect even when a `models:` list is also present.
func (*Manager) InjectConfigs ¶
func (m *Manager) InjectConfigs(cfgs []ProviderConfig)
InjectConfigs merges server-mode provider configs into the in-memory maps without writing to disk. Used by StartAPI to load providers from tollecode.yaml so they are available alongside the user's ~/.tollecode/config.json providers. Existing providers with the same ID are NOT overwritten. Injected configs are saved so that Reload() can re-apply them after rebuilding from config.json — YAML providers survive Reload.
func (*Manager) OllamaAPIKey ¶
OllamaAPIKey returns the first Ollama API key found across all providers. Prefers ollama-cloud type, falls back to ollama type. Returns "" if none.
func (*Manager) Reload ¶
func (m *Manager) Reload()
Reload re-reads config.json and rebuilds adapters. After rebuilding from disk, it re-applies any previously injected YAML configs so that server-mode providers (from tollecode.yaml) survive a Reload.
func (*Manager) ResolveProviderID ¶
ResolveProviderID resolves a provider string that could be either a literal provider ID (e.g. "prov-1780434925223") or a type alias (e.g. "anthropic", "ollama-cloud"). If it's a type alias, it returns the best provider ID of that type (preferring those with a default model). Returns ("", false) if no matching provider is found.
func (*Manager) SaveConfigs ¶
func (m *Manager) SaveConfigs(cfgs []ProviderConfig) error
SaveConfigs writes provider configs to disk and reloads.
func (*Manager) SyncFromLiteKV ¶
func (m *Manager) SyncFromLiteKV()
SyncFromLiteKV reconciles providers between the Lite app's shared KV store (~/.tollecode/lite_kv.json → "lite_providers", which the desktop/web UI reads) and config.json (which the CLI and agent runtime read), so a provider configured on ANY surface — CLI, Lite desktop, Lite web — is visible on all of them.
Reconciliation is deliberately ADD-ONLY in both directions: a provider present in one store but missing from the other is copied over; an entry already present in a store is never overwritten. This is what keeps it safe —
- It never clobbers a provider (or its API key) a surface already has.
- It never fights the Lite app's own `save_providers` push (which remains the live path for edits Lite makes to config.json), nor a `tollecode configure` edit to config.json.
Matching is by provider ID. Writes happen only when something is actually added, so a normal already-in-sync start touches nothing.
type ModelEntry ¶
type ModelEntry struct {
ID string `json:"id"`
Name string `json:"name"`
IsDefault bool `json:"isDefault"`
// contains filtered or unexported fields
}
ModelEntry is either a string or an object in the models array.
func (*ModelEntry) UnmarshalJSON ¶
func (m *ModelEntry) UnmarshalJSON(b []byte) error
type ModelInfo ¶
type ModelInfo struct {
ID string
Name string
ContextWindow int
MaxOutputTokens int
SupportsStreaming bool
SupportsFunctionCall bool
SupportsVision bool
SupportsThinking bool
}
ModelInfo describes one model from a provider's catalog.
func AnthropicModelInfo ¶
AnthropicModelInfo returns known capability metadata for a Claude model ID.
func OpenAIModelInfo ¶
OpenAIModelInfo maps a model ID to known capability metadata (exported for use in handlers).
type OllamaProvider ¶
type OllamaProvider struct {
// contains filtered or unexported fields
}
OllamaProvider streams from Ollama's native /api/chat endpoint. Using the native endpoint (not the OpenAI-compat /v1 layer) lets us set num_ctx explicitly and receive complete tool-call objects rather than accumulating streamed argument deltas.
func NewOllamaProvider ¶
func NewOllamaProvider(endpoint, apiKey string) *OllamaProvider
func (*OllamaProvider) DiscoverModels ¶
func (p *OllamaProvider) DiscoverModels(ctx context.Context) ([]ModelInfo, error)
func (*OllamaProvider) GetModelInfo ¶
func (p *OllamaProvider) GetModelInfo(ctx context.Context, model string) ModelInfo
GetModelInfo calls /api/show to retrieve context window, vision, and tool capabilities for a specific model. Falls back to safe defaults on error.
func (*OllamaProvider) Stream ¶
func (p *OllamaProvider) Stream(ctx context.Context, req StreamRequest) (<-chan StreamEvent, error)
type OpenAIProvider ¶
type OpenAIProvider struct {
// contains filtered or unexported fields
}
OpenAIProvider implements Provider for OpenAI-compatible APIs (OpenAI, Ollama-cloud, custom endpoints).
func NewOpenAIProvider ¶
func NewOpenAIProvider(apiKey, endpoint string) *OpenAIProvider
func (*OpenAIProvider) DiscoverModels ¶
func (p *OpenAIProvider) DiscoverModels(ctx context.Context) ([]ModelInfo, error)
func (*OpenAIProvider) Stream ¶
func (p *OpenAIProvider) Stream(ctx context.Context, req StreamRequest) (<-chan StreamEvent, error)
type Provider ¶
type Provider interface {
// Stream sends messages and streams events. Caller must drain the channel.
Stream(ctx context.Context, req StreamRequest) (<-chan StreamEvent, error)
// DiscoverModels lists models available from this provider.
DiscoverModels(ctx context.Context) ([]ModelInfo, error)
}
Provider is the interface all LLM adapters must satisfy.
func BuildAdapter ¶
func BuildAdapter(cfg ProviderConfig) Provider
BuildAdapter creates a provider adapter from a config without registering it.
type ProviderConfig ¶
type ProviderConfig struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
APIKey string `json:"apiKey"`
Endpoint string `json:"endpoint"`
Models []ModelEntry `json:"models"`
DefaultModel string `json:"defaultModel"`
}
ProviderConfig is one entry in ~/.tollecode/config.json.
func LoadAllConfigs ¶
func LoadAllConfigs() []ProviderConfig
LoadAllConfigs reads all provider configs from disk (including disabled ones).
type SecretVault ¶
type SecretVault struct {
// contains filtered or unexported fields
}
SecretVault maps aliases to the plaintext they stand for, for the lifetime of one agent run. It is never persisted; conversation history on disk keeps the raw values and is re-redacted on each outbound request.
func NewSecretVault ¶
func NewSecretVault() *SecretVault
func SecretVaultFrom ¶
func SecretVaultFrom(ctx context.Context) *SecretVault
SecretVaultFrom returns the vault on ctx, or nil when there is none. A nil vault is safe for every method here, so callers outside the agent loop (model discovery, one-off completions) need no special casing — they simply get non-reversible redaction, which is the correct behaviour when nothing on the far side will ever execute a tool call.
func (*SecretVault) Reveal ¶
func (v *SecretVault) Reveal(s string) string
Reveal substitutes real secrets back into s for every alias the vault knows. Unknown aliases are left intact: a handle from an earlier session has no plaintext here, and failing visibly beats silently sending the literal.
func (*SecretVault) RevealInput ¶
func (v *SecretVault) RevealInput(input map[string]any) map[string]any
RevealInput walks tool-call input and reveals aliases in every string it contains, at any depth — a secret may arrive as a bare argument, an element of an args array, or a nested header map.
type StreamEvent ¶
type StreamEvent struct {
Type string // "token" | "thinking" | "thinking_block" | "tool_call" | "done" | "error"
// token / thinking
Text string
// thinking_block (Anthropic extended thinking): the completed block, emitted at
// content_block_stop so it can be replayed verbatim on the next request in the
// same tool-use turn. Signature authenticates a normal block; Redacted carries
// the opaque data of a redacted_thinking block instead.
Signature string
Redacted string
// tool_call (complete, ready to execute)
ToolID string
ToolName string
ToolInput map[string]any
// done
InputTokens int
OutputTokens int
FinishReason string // "end_turn" | "tool_use" | "max_tokens" | "stop"
// error
Err error
}
StreamEvent is one event from a streaming LLM response.
type StreamRequest ¶
type StreamRequest struct {
Model string
System string
Messages []ChatMessage
Tools []ToolDef
MaxTokens int
ThinkingBudget int // Anthropic only: extended thinking budget tokens
ThinkLevel string // Ollama: "", "true", "false", "low", "medium", "high"
}
StreamRequest bundles all parameters for a single streaming call.
type ThinkingBlock ¶
type ThinkingBlock struct {
Thinking string // the thinking text (may be empty when display=omitted)
Signature string // opaque signature authenticating a normal thinking block
Redacted string // opaque data of a redacted_thinking block (set instead of the above)
}
ThinkingBlock is one extended-thinking block from an assistant turn. Anthropic requires the thinking that preceded a tool_use to be passed back unmodified on the next request within the same turn (with its signature), or it rejects the request. Blocks are only replayed for the current tool-use turn — the API auto-filters older turns, so they are not persisted to disk.
type ToolResult ¶
type ToolResult struct {
ToolUseID string
Name string // tool name — required by native Ollama "tool" role messages
Content string
IsError bool
ImageData string // base64-encoded image; non-empty means include an image content block
ImageMediaType string // e.g. "image/jpeg"; defaults to "image/jpeg" when empty
}
ToolResult is a resolved tool output sent back to the LLM.