ai

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: AGPL-3.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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.

View Source
var Global = &Manager{}

Functions

func DetectBase64MediaType

func DetectBase64MediaType(data string) string

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

func (m *Manager) BestProvider(preferProvider, preferModel string) (provider, model string)

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

func (m *Manager) DefaultModel(id string) string

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) Get

func (m *Manager) Get(id string) Provider

Get returns the adapter for a provider ID, or nil.

func (*Manager) IDs

func (m *Manager) IDs() []string

IDs returns all enabled provider IDs.

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) ListForUI

func (m *Manager) ListForUI() []map[string]any

ListForUI returns the provider list in the shape the frontend expects.

func (*Manager) OllamaAPIKey

func (m *Manager) OllamaAPIKey() string

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

func (m *Manager) ResolveProviderID(providerOrType string) (string, bool)

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

func AnthropicModelInfo(id string) ModelInfo

AnthropicModelInfo returns known capability metadata for a Claude model ID.

func OpenAIModelInfo

func OpenAIModelInfo(id string) ModelInfo

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 ToolCall

type ToolCall struct {
	ID    string
	Name  string
	Input map[string]any
}

ToolCall is one tool invocation by the assistant in a turn.

type ToolDef

type ToolDef struct {
	Name        string
	Description string
	InputSchema map[string]any
}

ToolDef is the schema definition for one tool passed to the LLM.

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.

Jump to

Keyboard shortcuts

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