config

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package config provides configuration loading and defaults for the laplaced bot.

Index

Constants

View Source
const (
	TelegramRichMessagesOff    = "off"
	TelegramRichMessagesShadow = "shadow"
	TelegramRichMessagesSend   = "send"
)
View Source
const (
	// Retrieval thresholds
	DefaultMinSafetyThreshold     = 0.1  // Relaxed for recall - minimum cosine similarity for vector search
	DefaultConsolidationThreshold = 0.75 // Strict for dedup - minimum similarity for topic merge

	// Session formatting limits (for reranker context)
	DefaultMaxSessionMessages = 10  // Recent messages to show reranker
	DefaultMaxCharsPerMessage = 500 // Truncate long messages for reranker

	// Topic retrieval limits
	DefaultRetrievedTopicsCount = 10 // Max topics to retrieve without reranker

	// Consolidation
	DefaultMaxMergedSizeChars = 50000 // 50K chars max for merged topics
	DefaultMergeGapThreshold  = 100   // Max message gap for merge candidate

	// Background loop intervals
	DefaultFactExtractionInterval = 1 * time.Minute  // Check for topics needing fact extraction
	DefaultConsolidationInterval  = 10 * time.Minute // Check for topics needing consolidation
	DefaultChunkProcessingTimeout = 10 * time.Minute // Timeout for processing a single chunk
	DefaultBackfillInterval       = 1 * time.Minute  // Background processing check interval

	// Chunking
	DefaultMaxChunkSize = 400 // Max messages per chunk before forced split

	// Memory
	DefaultFactDefaultImportance = 50 // Default importance for facts without explicit importance

	// Search
	DefaultPeopleSimilarityThreshold = 0.3 // Minimum similarity for people vector search
	DefaultPeopleMaxResults          = 5   // Max results for people vector search
)
View Source
const DefaultChunkInterval = 1 * time.Hour

DefaultChunkInterval is the default inactivity period before a session becomes a topic.

View Source
const DefaultRecentTopicsInContext = 3

DefaultRecentTopicsInContext is the default number of recent topics to show in context.

View Source
const DefaultSplitThreshold = 25000

DefaultSplitThreshold is the default character threshold for splitting large topics.

Variables

This section is empty.

Functions

func DefaultConfigBytes

func DefaultConfigBytes() []byte

DefaultConfigBytes returns the raw embedded default configuration. Useful for generating example config files.

Types

type AgentConfig added in v0.4.7

type AgentConfig struct {
	Name  string `yaml:"name"`
	Model string `yaml:"model"`
}

AgentConfig defines configuration for a single agent.

func (*AgentConfig) GetModel added in v0.4.7

func (a *AgentConfig) GetModel(defaultModel string) string

GetModel returns the agent's model, falling back to default if not set.

type AgentsConfig added in v0.4.7

type AgentsConfig struct {
	Default        AgentConfig          `yaml:"default"`                            // Default model for all agents
	Chat           ChatAgentConfig      `yaml:"chat"`                               // Main bot - talks to users
	ChatModel      string               `yaml:"-" env:"LAPLACED_AGENTS_CHAT_MODEL"` // Override for chat agent model
	Archivist      ArchivistAgentConfig `yaml:"archivist"`                          // Extracts facts and people from conversations
	Enricher       AgentConfig          `yaml:"enricher"`                           // Expands search queries
	Reactor        ReactorAgentConfig   `yaml:"reactor"`                            // Decides emoji reactions to user messages
	Reranker       RerankerAgentConfig  `yaml:"reranker"`                           // Filters and ranks RAG candidates
	Splitter       AgentConfig          `yaml:"splitter"`                           // Splits large topics
	Merger         AgentConfig          `yaml:"merger"`                             // Merges similar topics
	Extractor      ExtractorAgentConfig `yaml:"extractor"`                          // Extracts content from artifacts
	ImageGenerator ImageGeneratorConfig `yaml:"image_generator"`                    // Generates/edits images (v0.8.0)
}

AgentsConfig defines all agents in the system.

func (*AgentsConfig) GetChatMaxToolIterations added in v0.10.2

func (a *AgentsConfig) GetChatMaxToolIterations() int

GetChatMaxToolIterations returns how many tool-loop turns the chat agent may spend per reply before it is forced into a final synthesis turn without tools. Falls back to 5 when unset — traces show the model rarely gains new information after the 4th search; further turns are usually reformulations of the same query.

func (*AgentsConfig) GetChatModel added in v0.5.3

func (a *AgentsConfig) GetChatModel() string

GetChatModel returns the chat agent's model, applying env override if set.

func (*AgentsConfig) GetChatThinkingLevel added in v0.8.0

func (a *AgentsConfig) GetChatThinkingLevel() string

GetChatThinkingLevel returns the chat agent's reasoning effort level. Falls back to "low" when unset — the minimum supported on Gemini 3.1 Pro. Passing an explicit level prevents the model from leaking internal reasoning into content (see docs/bugs/2026-04-22-laplace-thought-leak/). "auto" (and legacy "off") omit the reasoning field entirely — Gemini then uses dynamic thinking and picks its own budget per request.

type ArchivistAgentConfig added in v0.5.1

type ArchivistAgentConfig struct {
	AgentConfig   `yaml:",inline"`
	ThinkingLevel string `yaml:"thinking_level" env:"LAPLACED_ARCHIVIST_THINKING_LEVEL"`
	Timeout       string `yaml:"timeout" env:"LAPLACED_ARCHIVIST_TIMEOUT"`
	MaxToolCalls  int    `yaml:"max_tool_calls" env:"LAPLACED_ARCHIVIST_MAX_TOOL_CALLS"`
}

ArchivistAgentConfig extends AgentConfig with archivist-specific settings.

func (*ArchivistAgentConfig) GetModel added in v0.5.1

func (a *ArchivistAgentConfig) GetModel(defaultModel string) string

GetModel returns the archivist's model, falling back to default if not set.

type ArtifactsConfig added in v0.6.0

type ArtifactsConfig struct {
	Enabled      bool     `yaml:"enabled" env:"LAPLACED_ARTIFACTS_ENABLED"`
	StoragePath  string   `yaml:"storage_path" env:"LAPLACED_ARTIFACTS_STORAGE_PATH"`
	AllowedTypes []string `yaml:"allowed_types"`

	// Voice settings (about storage filtering, not extraction)
	MinVoiceDurationSeconds int `yaml:"min_voice_duration_seconds" env:"LAPLACED_ARTIFACTS_MIN_VOICE_DURATION_SECONDS"` // gates RAG-indexing of voice: 0 = index all, -1 = disable voice artifacts, N = index voices >= N sec (shorter are still retained raw for replay)

	// S3, when present, switches the artifact blob store from the local disk
	// (StoragePath) to an S3-compatible bucket. Absence keeps the local backend,
	// so the home deployment is byte-identical. Capability block, not a mode flag.
	S3 *S3Config `yaml:"s3"`
}

ArtifactsConfig defines configuration for the artifacts system (v0.6.0). Processing settings moved to agents.extractor. RAG settings moved to rag and agents.reranker.artifacts.

type BotConfig

type BotConfig struct {
	Language          string          `yaml:"language" env:"LAPLACED_BOT_LANGUAGE"`
	AllowedUserIDs    []int64         `yaml:"allowed_user_ids" env:"LAPLACED_ALLOWED_USER_IDS"`
	SystemPromptExtra string          `yaml:"system_prompt_extra"`
	TurnWaitDuration  string          `yaml:"turn_wait_duration"`
	Streaming         StreamingConfig `yaml:"streaming"`
}

type ChatAgentConfig added in v0.8.0

type ChatAgentConfig struct {
	AgentConfig       `yaml:",inline"`
	ThinkingLevel     string `yaml:"thinking_level" env:"LAPLACED_AGENTS_CHAT_THINKING_LEVEL"`
	MaxToolIterations int    `yaml:"max_tool_iterations" env:"LAPLACED_AGENTS_CHAT_MAX_TOOL_ITERATIONS"`
}

ChatAgentConfig extends AgentConfig with chat-specific settings. Setting ThinkingLevel explicitly prevents Gemini 3.1 Pro from leaking internal reasoning into the user-visible content field (see docs/bugs/2026-04-22-laplace-thought-leak/).

type Config

type Config struct {
	Log struct {
		Level string `yaml:"level" env:"LAPLACED_LOG_LEVEL"`
	} `yaml:"log"`
	Server struct {
		ListenPort string `yaml:"listen_port" env:"LAPLACED_SERVER_PORT"`
		DebugMode  bool   `yaml:"debug_mode" env:"LAPLACED_SERVER_DEBUG"`
		Auth       struct {
			Enabled  bool   `yaml:"enabled" env:"LAPLACED_AUTH_ENABLED"`
			Username string `yaml:"username" env:"LAPLACED_AUTH_USERNAME"`
			Password string `yaml:"password" env:"LAPLACED_AUTH_PASSWORD"`
		} `yaml:"auth"`
	} `yaml:"server"`
	// Transport selects the chat backend: "telegram" (default) | "mattermost".
	Transport string `yaml:"transport" env:"LAPLACED_TRANSPORT"`
	Telegram  struct {
		Token         string                     `yaml:"token" env:"LAPLACED_TELEGRAM_TOKEN"`
		WebhookURL    string                     `yaml:"webhook_url" env:"LAPLACED_TELEGRAM_WEBHOOK_URL"`
		WebhookPath   string                     // Auto-generated from token hash (not configurable)
		WebhookSecret string                     // Auto-generated from token hash (not configurable)
		ProxyURL      string                     `yaml:"proxy_url" env:"LAPLACED_TELEGRAM_PROXY_URL"`
		RichMessages  TelegramRichMessagesConfig `yaml:"rich_messages"`
	} `yaml:"telegram"`
	Mattermost MattermostConfig `yaml:"mattermost"`
	LLM        LLMConfig        `yaml:"llm"`
	Agents     AgentsConfig     `yaml:"agents"`
	Embedding  EmbeddingConfig  `yaml:"embedding"`
	RAG        RAGConfig        `yaml:"rag"`
	Tools      []ToolConfig     `yaml:"tools"`
	Fetcher    FetcherConfig    `yaml:"fetcher"`
	Bot        BotConfig        `yaml:"bot"`
	Database   struct {
		// Driver selects the storage backend: "sqlite" (default) or "postgres".
		// Empty defaults to sqlite for backward compatibility.
		Driver string `yaml:"driver" env:"LAPLACED_DATABASE_DRIVER"`
		// Path is the SQLite database file path (driver=sqlite).
		Path string `yaml:"path" env:"LAPLACED_DATABASE_PATH"`
		// Postgres holds the connection params for driver=postgres. The password
		// MUST come from the LAPLACED_DATABASE_PASSWORD env var, never a committed literal.
		Postgres struct {
			Host     string `yaml:"host" env:"LAPLACED_DATABASE_HOST"`
			Port     int    `yaml:"port" env:"LAPLACED_DATABASE_PORT"`
			Database string `yaml:"database" env:"LAPLACED_DATABASE_NAME"`
			User     string `yaml:"user" env:"LAPLACED_DATABASE_USER"`
			Password string `yaml:"password" env:"LAPLACED_DATABASE_PASSWORD"`
			SSLMode  string `yaml:"sslmode" env:"LAPLACED_DATABASE_SSLMODE"`
		} `yaml:"postgres"`
		// Retention bounds the periodic trim of debug tables. agent_logs is
		// the only store of full agent prompts/responses (trace retention is
		// much shorter), so an over-eager trim blinds any postmortem.
		Retention RetentionConfig `yaml:"retention"`
	} `yaml:"database"`
	Artifacts ArtifactsConfig `yaml:"artifacts"`
	Memory    MemoryConfig    `yaml:"memory"`
	Search    SearchConfig    `yaml:"search"`
	Telemetry TelemetryConfig `yaml:"telemetry"`
	// Vault, when present, enables pulling secrets from HashiCorp Vault. Absent
	// (nil) = secrets come only from literals / LAPLACED_* env vars (default).
	Vault *VaultConfig `yaml:"vault"`
}

func Load

func Load(path string) (*Config, error)

Load loads configuration from the specified file path. It first loads the embedded default configuration, then merges the user config on top. Finally, it overrides values with environment variables.

func LoadDefault

func LoadDefault() (*Config, error)

LoadDefault loads the embedded default configuration.

func (*Config) DisableTool added in v0.10.3

func (c *Config) DisableTool(name string)

DisableTool removes a tool from the exposed tools list. Used at startup when a tool's backing dependency failed to initialize (e.g. read_url without a fetcher): dropping it here keeps the tool schema and the system prompt consistent, instead of steering the model toward a tool that always fails.

func (*Config) ResolveSecrets added in v0.10.0

func (c *Config) ResolveSecrets(ctx context.Context, provider SecretProvider) error

ResolveSecrets replaces any "vault:" reference held by a known secret field with the value fetched from the provider. Fields holding plain literals are left untouched. Call it after Load and before Validate.

A reference present while provider is nil (no [vault] block configured) is an error rather than a silent miss — see the "config-driven data shape" incident class in CLAUDE.md.

func (*Config) ToolConfigured added in v0.10.3

func (c *Config) ToolConfigured(name string) bool

ToolConfigured reports whether a tool with the given name is exposed in the tools list. Tool schemas and prompt protocol sections are both derived from this list, so it is the single source of truth for tool exposure.

func (*Config) Validate added in v0.2.1

func (c *Config) Validate() error

Validate checks configuration for required fields and valid ranges. It accumulates all validation failures across sections and returns them joined; nil means the config is valid.

It also applies the v0.6.0 reranker config migration (see legacy.go) before validating that section, so callers get a normalized config.

type EmbeddingConfig added in v0.4.7

type EmbeddingConfig struct {
	Model      string `yaml:"model" env:"LAPLACED_EMBEDDING_MODEL"`
	Dimensions int    `yaml:"dimensions" env:"LAPLACED_EMBEDDING_DIMENSIONS"`
}

EmbeddingConfig defines embedding model settings.

type ExtractorAgentConfig added in v0.6.0

type ExtractorAgentConfig struct {
	AgentConfig `yaml:",inline"` // Name, Model

	// Processing settings (moved from ArtifactsConfig in v0.6.0)
	MaxFileSizeMB      int    `yaml:"max_file_size_mb" env:"LAPLACED_EXTRACTOR_MAX_FILE_SIZE_MB"`
	Timeout            string `yaml:"timeout" env:"LAPLACED_EXTRACTOR_TIMEOUT"`
	MaxRetries         int    `yaml:"max_retries" env:"LAPLACED_EXTRACTOR_MAX_RETRIES"`
	PollingInterval    string `yaml:"polling_interval" env:"LAPLACED_EXTRACTOR_POLLING_INTERVAL"`
	MaxConcurrent      int    `yaml:"max_concurrent" env:"LAPLACED_EXTRACTOR_MAX_CONCURRENT"`
	RecoveryThreshold  string `yaml:"recovery_threshold" env:"LAPLACED_EXTRACTOR_RECOVERY_THRESHOLD"`
	RecentMessageCount int    `yaml:"recent_message_count" env:"LAPLACED_EXTRACTOR_RECENT_MESSAGE_COUNT"` // Number of recent session messages to include in artifact context (0 = disable)
}

ExtractorAgentConfig defines configuration for the Extractor agent (v0.6.0). Processing settings were moved from ArtifactsConfig to keep agent-related config together.

func (*ExtractorAgentConfig) GetMaxRetries added in v0.11.0

func (e *ExtractorAgentConfig) GetMaxRetries() int

GetMaxRetries returns the shared retry limit used both when scheduling artifacts and when deciding whether extractor output is on its final attempt.

func (*ExtractorAgentConfig) GetModel added in v0.6.0

func (e *ExtractorAgentConfig) GetModel(defaultModel string) string

GetModel returns the extractor's model, falling back to default if not set.

func (*ExtractorAgentConfig) GetPollingInterval added in v0.6.0

func (e *ExtractorAgentConfig) GetPollingInterval() time.Duration

GetPollingInterval returns the interval for polling pending artifacts. Defaults to 30 seconds if not configured.

func (*ExtractorAgentConfig) GetRecoveryThreshold added in v0.6.0

func (e *ExtractorAgentConfig) GetRecoveryThreshold() time.Duration

GetRecoveryThreshold returns the threshold for recovering zombie artifact states. Defaults to 10 minutes if not configured.

func (*ExtractorAgentConfig) GetTimeout added in v0.6.0

func (e *ExtractorAgentConfig) GetTimeout() time.Duration

GetTimeout returns the timeout for artifact processing. Defaults to 2 minutes if not configured.

type FetcherConfig added in v0.10.3

type FetcherConfig struct {
	// Backend: "firecrawl" (api.firecrawl.dev REST scrape, JS rendering,
	// 1 credit/page) or "raw" (plain HTTP + text extraction, no external
	// service). "mcp" is reserved for a future backend.
	Backend string `yaml:"backend" env:"LAPLACED_FETCHER_BACKEND"`
	Timeout string `yaml:"timeout" env:"LAPLACED_FETCHER_TIMEOUT"`
	// MaxContentChars caps the tool result size in runes; longer pages are
	// truncated with a marker. Guards the main model's context from page dumps.
	MaxContentChars int `yaml:"max_content_chars" env:"LAPLACED_FETCHER_MAX_CONTENT_CHARS"`
	// AllowPrivateNetworks lets the raw backend fetch RFC1918/CGNAT/ULA
	// addresses — for intranet deployments where the bot reads internal
	// wiki/docs pages. Loopback and link-local (incl. the cloud metadata
	// endpoint) stay blocked regardless: the bot's own host carries its admin
	// surface and no page the model should read. The firecrawl backend is
	// unaffected (an external service fetching from its own network).
	AllowPrivateNetworks bool            `yaml:"allow_private_networks" env:"LAPLACED_FETCHER_ALLOW_PRIVATE_NETWORKS"`
	Firecrawl            FirecrawlConfig `yaml:"firecrawl"`
}

FetcherConfig configures the web-page fetcher backing the read_url tool. Backend selects the implementation; there is deliberately no automatic fallback between backends — a silent downgrade would mask credit exhaustion and produce mysteriously degraded extractions.

func (*FetcherConfig) GetMaxContentChars added in v0.10.3

func (c *FetcherConfig) GetMaxContentChars() int

GetMaxContentChars returns the result size cap in runes, defaulting to 15000.

func (*FetcherConfig) GetTimeout added in v0.10.3

func (c *FetcherConfig) GetTimeout() time.Duration

GetTimeout returns the per-fetch timeout. Defaults to 60s — Firecrawl JS rendering routinely takes 15-30s. Non-positive values also fall back: http.Client treats Timeout <= 0 as "no timeout at all", so passing a negative config value through would let read_url hang indefinitely.

type FirecrawlConfig added in v0.10.3

type FirecrawlConfig struct {
	BaseURL string `yaml:"base_url" env:"LAPLACED_FIRECRAWL_BASE_URL"`
	APIKey  string `yaml:"api_key" env:"LAPLACED_FIRECRAWL_API_KEY"`
}

FirecrawlConfig holds the Firecrawl REST API settings. APIKey may be a "vault:" reference (registered in secretFields).

func (*FirecrawlConfig) GetBaseURL added in v0.10.3

func (c *FirecrawlConfig) GetBaseURL() string

GetBaseURL returns the Firecrawl API base URL, defaulting to the public API.

type ImageGeneratorConfig added in v0.8.0

type ImageGeneratorConfig struct {
	AgentConfig `yaml:",inline"` // Name, Model (e.g. google/gemini-3.1-flash-image-preview)

	Timeout            string `yaml:"timeout" env:"LAPLACED_IMAGE_GENERATOR_TIMEOUT"`
	DefaultAspectRatio string `yaml:"default_aspect_ratio" env:"LAPLACED_IMAGE_GENERATOR_DEFAULT_ASPECT_RATIO"`
	DefaultImageSize   string `yaml:"default_image_size" env:"LAPLACED_IMAGE_GENERATOR_DEFAULT_IMAGE_SIZE"`
	// SupportedImageSizes / SupportedAspectRatios drive the generate_image tool
	// JSON schema. They MUST match what the configured Model accepts upstream —
	// see the commented alternative in default.yaml for verified per-model sets.
	SupportedImageSizes   []string `yaml:"supported_image_sizes" env:"LAPLACED_IMAGE_GENERATOR_SUPPORTED_IMAGE_SIZES" env-separator:","`
	SupportedAspectRatios []string `yaml:"supported_aspect_ratios" env:"LAPLACED_IMAGE_GENERATOR_SUPPORTED_ASPECT_RATIOS" env-separator:","`
	MaxInputImages        int      `yaml:"max_input_images" env:"LAPLACED_IMAGE_GENERATOR_MAX_INPUT_IMAGES"`
	MaxOutputImages       int      `yaml:"max_output_images" env:"LAPLACED_IMAGE_GENERATOR_MAX_OUTPUT_IMAGES"`
	MaxInputImageBytes    int      `yaml:"max_input_image_bytes" env:"LAPLACED_IMAGE_GENERATOR_MAX_INPUT_IMAGE_BYTES"`
	// DocumentThresholdBytes: generated images larger than this are sent via
	// sendDocument instead of sendPhoto, preserving full resolution (Telegram
	// recompresses photos to ~1280 px on long side). Default 2 MB covers
	// 2K/4K outputs; set to 0 to always use sendPhoto.
	DocumentThresholdBytes int `yaml:"document_threshold_bytes" env:"LAPLACED_IMAGE_GENERATOR_DOCUMENT_THRESHOLD_BYTES"`
	// MaxConcurrent bounds how many generate_image tool calls from a single
	// assistant turn run in parallel (e.g. "draw three pictures"). Other tools
	// stay sequential. Defaults to 4.
	MaxConcurrent int `yaml:"max_concurrent" env:"LAPLACED_IMAGE_GENERATOR_MAX_CONCURRENT"`
}

ImageGeneratorConfig defines configuration for the image-generation agent that drives OpenRouter image-output models (v0.8.0).

func (*ImageGeneratorConfig) GetMaxConcurrent added in v0.10.0

func (c *ImageGeneratorConfig) GetMaxConcurrent() int

GetMaxConcurrent returns the parallel-generation cap, defaulting to 4.

func (*ImageGeneratorConfig) GetTimeout added in v0.8.0

func (c *ImageGeneratorConfig) GetTimeout() time.Duration

GetTimeout returns the per-call timeout. Defaults to 90s.

type LLMConfig added in v0.10.1

type LLMConfig struct {
	APIKey string `yaml:"api_key" env:"LAPLACED_LLM_API_KEY"`
	// BaseURL is the OpenAI-compatible endpoint the LLM client talks to.
	// Defaults to the public OpenRouter API; override to point at a self-hosted
	// OpenAI-compatible backend (litellm, vLLM, …).
	BaseURL string `yaml:"base_url" env:"LAPLACED_LLM_BASE_URL"`
	// ImageInputFormat selects how images/videos are encoded as LLM content
	// parts: "file" (default, OpenRouter/Gemini) or "openai" (image_url/video_url,
	// required by OpenAI-compatible backends like litellm/vLLM which reject "file").
	ImageInputFormat string                `yaml:"image_input_format" env:"LAPLACED_LLM_IMAGE_INPUT_FORMAT"`
	ProxyURL         string                `yaml:"proxy_url" env:"LAPLACED_LLM_PROXY_URL"`
	PDFParserEngine  string                `yaml:"pdf_parser_engine"`
	RequestCost      float64               `yaml:"request_cost"`
	PriceTiers       []PriceTier           `yaml:"price_tiers"`
	Provider         ProviderRoutingConfig `yaml:"provider"`
}

type MattermostConfig added in v0.10.0

type MattermostConfig struct {
	ServerURL      string   `yaml:"server_url" env:"LAPLACED_MATTERMOST_SERVER_URL"`
	BotToken       string   `yaml:"bot_token" env:"LAPLACED_MATTERMOST_BOT_TOKEN"`
	ProxyURL       string   `yaml:"proxy_url" env:"LAPLACED_MATTERMOST_PROXY_URL"`
	AllowedUserIDs []string `yaml:"allowed_user_ids" env:"LAPLACED_MATTERMOST_ALLOWED_USER_IDS" env-separator:","`
	// PrincipalResolver, when present, turns on principal identity resolution for
	// this transport's DMs. Its mere presence enables resolution
	// (organic config, not a mode flag); absence = passthrough (the default behavior).
	PrincipalResolver *PrincipalResolverConfig `yaml:"principal_resolver"`
}

MattermostConfig configures the Mattermost/Time transport (used when transport == "mattermost"). The proxy is per-client (HTTP proxy); never set a process-wide HTTP_PROXY, which would also route the LLM client.

type MemoryConfig added in v0.6.1

type MemoryConfig struct {
	FactDefaultImportance int `yaml:"fact_default_importance" env:"LAPLACED_MEMORY_FACT_DEFAULT_IMPORTANCE"`
}

MemoryConfig defines configuration for memory operations.

func (*MemoryConfig) GetFactDefaultImportance added in v0.6.1

func (c *MemoryConfig) GetFactDefaultImportance() int

GetFactDefaultImportance returns the default importance for facts without explicit importance. Falls back to DefaultFactDefaultImportance (50) if not configured.

type PriceTier

type PriceTier struct {
	UpToTokens     int     `yaml:"up_to_tokens"`
	PromptCost     float64 `yaml:"prompt_cost"`
	CompletionCost float64 `yaml:"completion_cost"`
}

type PrincipalResolverConfig added in v0.10.0

type PrincipalResolverConfig struct {
	// TrustedAuthServices restricts which Mattermost auth_service values are
	// trusted for principal linkage. Empty = trust any non-empty auth_service
	// (the default gate). Local accounts (auth_service == "") are never linked.
	TrustedAuthServices []string `yaml:"trusted_auth_services" env:"LAPLACED_MATTERMOST_TRUSTED_AUTH_SERVICES" env-separator:","`
	// AccessDeniedMessage is the verbatim text sent to a sender denied access
	// because they are not an SSO-authenticated user. Empty falls back to the
	// neutral localized default (i18n bot.access_denied). Deployment-specific
	// wording (e.g. "sign in via your corporate SSO first") belongs here, in a
	// gitignored overlay — never in tracked locale files.
	AccessDeniedMessage string `yaml:"access_denied_message" env:"LAPLACED_MATTERMOST_ACCESS_DENIED_MESSAGE"`
	// TrustedBots lists bot-account usernames (case-insensitive, leading "@"
	// tolerated) allowed to interact with the bot despite being local accounts
	// that fail the SSO trust gate. Empty = no bots trusted (fail-closed). Use for
	// trusted automation such as an alerting bot requesting an incident summary. A
	// bot not on this list is ignored silently — not sent an access-denied notice.
	TrustedBots []string `yaml:"trusted_bots" env:"LAPLACED_MATTERMOST_TRUSTED_BOTS" env-separator:","`
	// MaxBotChainDepth caps consecutive bot replies within a single thread before
	// the bot stops replying, breaking LLM-bot ping-pong loops. <= 0 uses a
	// built-in default. An authorized human posting in the thread resets the count.
	MaxBotChainDepth int `yaml:"max_bot_chain_depth" env:"LAPLACED_MATTERMOST_MAX_BOT_CHAIN_DEPTH"`
}

PrincipalResolverConfig enables and tunes federated-passive principal resolution for a transport. The trust gate is the transport's own auth_service (Mattermost GetUser): a local account (auth_service == "") is NEVER linked, and identities are NEVER linked by self-claimed email. Resolution is federated-passive only for now; an objectGUID/Keycloak lookup arrives later.

type ProviderRoutingConfig added in v0.9.0

type ProviderRoutingConfig struct {
	Order []string `yaml:"order" env:"LAPLACED_LLM_PROVIDER_ORDER" env-separator:","`
	// AllowFallbacks is YAML-only: cleanenv doesn't support *bool via env tags.
	// The common case (prefer a provider with default fallback) needs only Order,
	// so this is not a practical limitation.
	AllowFallbacks *bool `yaml:"allow_fallbacks"`
}

ProviderRoutingConfig configures OpenRouter provider preference. See https://openrouter.ai/docs/features/provider-routing for semantics. When Order is empty, no routing header is sent — OpenRouter picks freely. AllowFallbacks is a pointer: unset means "use OpenRouter default (true)"; explicit false means "never fall back outside the order list".

func (ProviderRoutingConfig) ToRouting added in v0.9.0

func (c ProviderRoutingConfig) ToRouting() *llm.ProviderRouting

ToRouting converts the YAML config to an llm.ProviderRouting pointer. Returns nil when no preference is configured, so the client stays on OpenRouter's default behavior.

type RAGConfig

type RAGConfig struct {
	Enabled                          bool    `yaml:"enabled" env:"LAPLACED_RAG_ENABLED"`
	MaxContextMessages               int     `yaml:"max_context_messages"`
	AnswerMemoryTokenBudget          int     `yaml:"answer_memory_token_budget" env:"LAPLACED_RAG_ANSWER_MEMORY_TOKEN_BUDGET"`
	MaxProfileFacts                  int     `yaml:"max_profile_facts"`
	RetrievedMessagesCount           int     `yaml:"retrieved_messages_count"`
	RetrievedTopicsCount             int     `yaml:"retrieved_topics_count"`
	SimilarityThreshold              float64 `yaml:"similarity_threshold"`
	ConsolidationSimilarityThreshold float64 `yaml:"consolidation_similarity_threshold"`
	MinSafetyThreshold               float64 `yaml:"min_safety_threshold"`
	MaxChunkSize                     int     `yaml:"max_chunk_size"`
	BackfillBatchSize                int     `yaml:"backfill_batch_size"`
	BackfillInterval                 string  `yaml:"backfill_interval"`
	ChunkInterval                    string  `yaml:"chunk_interval"`
	MaxMergedSizeChars               int     `yaml:"max_merged_size_chars"`
	SplitThresholdChars              int     `yaml:"split_threshold_chars"`
	RecentTopicsInContext            int     `yaml:"recent_topics_in_context"`
}

func (*RAGConfig) GetChunkDuration added in v0.4.6

func (c *RAGConfig) GetChunkDuration() time.Duration

GetChunkDuration returns the parsed chunk interval duration. Falls back to DefaultChunkInterval if not configured or invalid.

func (*RAGConfig) GetConsolidationThreshold added in v0.6.1

func (c *RAGConfig) GetConsolidationThreshold() float64

GetConsolidationThreshold returns the minimum similarity for topic consolidation. Falls back to DefaultConsolidationThreshold (0.75) if not configured. This strict threshold ensures we only merge very similar topics.

func (*RAGConfig) GetMaxChunkSize added in v0.6.1

func (c *RAGConfig) GetMaxChunkSize() int

GetMaxChunkSize returns the max messages per chunk before forced split. Falls back to DefaultMaxChunkSize (400) if not configured.

func (*RAGConfig) GetMaxMergedSizeChars added in v0.6.1

func (c *RAGConfig) GetMaxMergedSizeChars() int

GetMaxMergedSizeChars returns the max character count for merged topics. Falls back to DefaultMaxMergedSizeChars (50000) if not configured.

func (*RAGConfig) GetMinSafetyThreshold added in v0.6.1

func (c *RAGConfig) GetMinSafetyThreshold() float64

GetMinSafetyThreshold returns the minimum cosine similarity for vector search. Falls back to DefaultMinSafetyThreshold (0.1) if not configured. This relaxed threshold prioritizes recall over precision.

func (*RAGConfig) GetRecentTopicsInContext added in v0.4.6

func (c *RAGConfig) GetRecentTopicsInContext() int

GetRecentTopicsInContext returns the number of recent topics to include in context. Falls back to DefaultRecentTopicsInContext if not configured. Returns 0 to disable.

func (*RAGConfig) GetRetrievedTopicsCount added in v0.6.1

func (c *RAGConfig) GetRetrievedTopicsCount() int

GetRetrievedTopicsCount returns the max topics to retrieve without reranker. Falls back to DefaultRetrievedTopicsCount (10) if not configured.

func (*RAGConfig) GetSplitThreshold added in v0.4.6

func (c *RAGConfig) GetSplitThreshold() int

GetSplitThreshold returns the threshold for splitting large topics. Falls back to DefaultSplitThreshold if not configured.

type ReactorAgentConfig added in v0.10.1

type ReactorAgentConfig struct {
	Name    string `yaml:"name"`
	Model   string `yaml:"model" env:"LAPLACED_AGENTS_REACTOR_MODEL"`
	Enabled bool   `yaml:"enabled" env:"LAPLACED_AGENTS_REACTOR_ENABLED"`
}

ReactorAgentConfig configures the emoji-reaction agent. Standalone struct (not inline AgentConfig) so the env tags stay agent-unique.

func (*ReactorAgentConfig) GetModel added in v0.10.1

func (r *ReactorAgentConfig) GetModel(defaultModel string) string

GetModel returns the reactor's model, falling back to default if not set.

type RerankerAgentConfig added in v0.4.7

type RerankerAgentConfig struct {
	AgentConfig        `yaml:",inline"`
	Enabled            bool   `yaml:"enabled" env:"LAPLACED_RERANKER_ENABLED"`
	Timeout            string `yaml:"timeout" env:"LAPLACED_RERANKER_TIMEOUT"`
	TurnTimeout        string `yaml:"turn_timeout" env:"LAPLACED_RERANKER_TURN_TIMEOUT"`
	MaxToolCalls       int    `yaml:"max_tool_calls" env:"LAPLACED_RERANKER_MAX_TOOL_CALLS"`
	ThinkingLevel      string `yaml:"thinking_level" env:"LAPLACED_RERANKER_THINKING_LEVEL"`
	TargetContextChars int    `yaml:"target_context_chars" env:"LAPLACED_RERANKER_TARGET_CONTEXT_CHARS"`
	InputTokenBudget   int    `yaml:"input_token_budget" env:"LAPLACED_RERANKER_INPUT_TOKEN_BUDGET"`

	// Per-type limits (v0.6.0)
	Topics    RerankerTypeConfig      `yaml:"topics"`
	People    RerankerTypeConfig      `yaml:"people"`
	Artifacts RerankerArtifactsConfig `yaml:"artifacts"`

	// Legacy fields (deprecated, kept for migration)
	Candidates int `yaml:"candidates" env:"LAPLACED_RERANKER_CANDIDATES"`
	MaxTopics  int `yaml:"max_topics" env:"LAPLACED_RERANKER_MAX_TOPICS"`
	MaxPeople  int `yaml:"max_people" env:"LAPLACED_RERANKER_MAX_PEOPLE"`
}

RerankerAgentConfig extends AgentConfig with reranker-specific settings.

func (*RerankerAgentConfig) GetModel added in v0.4.7

func (r *RerankerAgentConfig) GetModel(defaultModel string) string

GetModel returns the reranker's model, falling back to default if not set.

type RerankerArtifactsConfig added in v0.9.0

type RerankerArtifactsConfig struct {
	RerankerTypeConfig `yaml:",inline"`
	Session            RerankerArtifactsSessionConfig `yaml:"session"`
}

RerankerArtifactsConfig extends per-type limits with session-aware injection (artifacts attached to messages still in the active session, topic_id IS NULL).

type RerankerArtifactsSessionConfig added in v0.9.0

type RerankerArtifactsSessionConfig struct {
	Max    int    `yaml:"max" env:"LAPLACED_RERANKER_ARTIFACTS_SESSION_MAX"`
	MaxAge string `yaml:"max_age" env:"LAPLACED_RERANKER_ARTIFACTS_SESSION_MAX_AGE"`
}

RerankerArtifactsSessionConfig governs how many session-active artifacts are injected into the reranker candidate pool and how old they may be.

func (RerankerArtifactsSessionConfig) GetMaxAge added in v0.9.0

GetMaxAge returns the session age cap. Defaults to 24h when session is enabled but max_age is missing or invalid; returns 0 when session is disabled.

func (RerankerArtifactsSessionConfig) IsEnabled added in v0.9.0

func (c RerankerArtifactsSessionConfig) IsEnabled() bool

IsEnabled reports whether session-aware artifact injection is configured. Disabled (zero) by default — the operator must opt in via configs/default.yaml or env var. Tests using testutil.TestConfig() therefore won't trigger the new storage path unless they explicitly configure it.

type RerankerTypeConfig added in v0.6.0

type RerankerTypeConfig struct {
	CandidatesLimit int `yaml:"candidates_limit" env:"LAPLACED_RERANKER_TOPICS_CANDIDATES_LIMIT"`
	Max             int `yaml:"max" env:"LAPLACED_RERANKER_TOPICS_MAX"`
	MaxContextBytes int `yaml:"max_context_bytes,omitempty" env:"LAPLACED_RERANKER_TOPICS_MAX_CONTEXT_BYTES"` // For artifacts only (v0.6.0)
}

RerankerTypeConfig defines per-type reranker limits (v0.6.0).

type RetentionConfig added in v0.10.3

type RetentionConfig struct {
	// AgentLogsKeep is how many of the newest agent_logs rows survive per
	// (user, agent type) once past the min-age window. 0 uses the default.
	AgentLogsKeep int `yaml:"agent_logs_keep" env:"LAPLACED_DATABASE_AGENT_LOGS_KEEP"`
	// AgentLogsMinAge protects recent rows from the keep-N trim: rows younger
	// than this are never deleted regardless of count. Duration string
	// ("336h" = 14 days). Empty uses the default; "0" disables the protection.
	AgentLogsMinAge string `yaml:"agent_logs_min_age" env:"LAPLACED_DATABASE_AGENT_LOGS_MIN_AGE"`
}

RetentionConfig bounds the periodic cleanup of debug tables (database.retention).

func (*RetentionConfig) GetAgentLogsKeep added in v0.10.3

func (r *RetentionConfig) GetAgentLogsKeep() int

GetAgentLogsKeep returns the keep-N for agent_logs cleanup (default 50).

func (*RetentionConfig) GetAgentLogsMinAge added in v0.10.3

func (r *RetentionConfig) GetAgentLogsMinAge() time.Duration

GetAgentLogsMinAge returns the min-age guard for agent_logs cleanup (default 14 days). An explicit "0" disables the guard.

type S3Config added in v0.10.0

type S3Config struct {
	Endpoint  string `yaml:"endpoint" env:"LAPLACED_ARTIFACTS_S3_ENDPOINT"`
	Region    string `yaml:"region" env:"LAPLACED_ARTIFACTS_S3_REGION"`
	Bucket    string `yaml:"bucket" env:"LAPLACED_ARTIFACTS_S3_BUCKET"`
	AccessKey string `yaml:"access_key" env:"LAPLACED_ARTIFACTS_S3_ACCESS_KEY"`
	SecretKey string `yaml:"secret_key" env:"LAPLACED_ARTIFACTS_S3_SECRET_KEY"`
}

S3Config configures an S3-compatible artifact backend (Yandex Object Storage). access_key/secret_key may be "vault:" references — they're registered in Config.secretFields() so they resolve at startup.

type SearchConfig added in v0.6.1

type SearchConfig struct {
	PeopleSimilarityThreshold float64 `yaml:"people_similarity_threshold" env:"LAPLACED_SEARCH_PEOPLE_SIMILARITY_THRESHOLD"`
	PeopleMaxResults          int     `yaml:"people_max_results" env:"LAPLACED_SEARCH_PEOPLE_MAX_RESULTS"`
}

SearchConfig defines configuration for search operations.

func (*SearchConfig) GetPeopleMaxResults added in v0.6.1

func (c *SearchConfig) GetPeopleMaxResults() int

GetPeopleMaxResults returns the max results for people vector search. Falls back to DefaultPeopleMaxResults (5) if not configured.

func (*SearchConfig) GetPeopleSimilarityThreshold added in v0.6.1

func (c *SearchConfig) GetPeopleSimilarityThreshold() float64

GetPeopleSimilarityThreshold returns the minimum similarity for people vector search. Falls back to DefaultPeopleSimilarityThreshold (0.3) if not configured.

type SecretProvider added in v0.10.0

type SecretProvider interface {
	Get(ctx context.Context, ref VaultRef) (string, error)
}

SecretProvider fetches a single secret value for a parsed reference.

type StreamingConfig added in v0.9.0

type StreamingConfig struct {
	Enabled        bool `yaml:"enabled" env:"LAPLACED_BOT_STREAMING_ENABLED"`
	EditThrottleMs int  `yaml:"edit_throttle_ms" env:"LAPLACED_BOT_STREAMING_EDIT_THROTTLE_MS"`
	EditMinChars   int  `yaml:"edit_min_chars" env:"LAPLACED_BOT_STREAMING_EDIT_MIN_CHARS"`
	MaxBufferChars int  `yaml:"max_buffer_chars" env:"LAPLACED_BOT_STREAMING_MAX_BUFFER_CHARS"`
}

StreamingConfig controls progressive legacy Telegram replies and the shared snapshot cadence. Enabled applies only to persistent editMessageText output; rich drafts have their own TelegramRichMessagesConfig rollout flag.

EditThrottleMs / EditMinChars throttle snapshots. MaxBufferChars is retained as the legacy editMessageText preview cap; native Rich Message drafts use a separate internal budget because Telegram gives them different limits. The completed agent response remains the source of truth for final delivery.

func (StreamingConfig) GetEditMinChars added in v0.9.0

func (s StreamingConfig) GetEditMinChars() int

GetEditMinChars returns the minimum new-character threshold for an early edit (before the throttle elapses). Defaults to 80 when unset.

func (StreamingConfig) GetEditThrottle added in v0.9.0

func (s StreamingConfig) GetEditThrottle() time.Duration

GetEditThrottle returns the configured throttle as a Duration. Defaults to 1s when unset or non-positive.

func (StreamingConfig) GetMaxBufferChars added in v0.9.0

func (s StreamingConfig) GetMaxBufferChars() int

GetMaxBufferChars returns the legacy editMessageText in-bubble streaming cap. Defaults to 3400 bytes — a safety margin under Telegram's 4096 UTF-16 limit accounting for HTML expansion. The key name is retained for backward compatibility; native Rich Message drafts do not use it.

type TelegramRichMessagesConfig added in v0.11.0

type TelegramRichMessagesConfig struct {
	Mode string `yaml:"mode" env:"LAPLACED_TELEGRAM_RICH_MESSAGES_MODE"`

	// AllowedUserIDs optionally narrows an explicit shadow/send mode to a
	// canary. An empty list means the mode applies to every Telegram-native
	// user; mode=off remains the kill switch either way.
	AllowedUserIDs []int64 `yaml:"allowed_user_ids" env:"LAPLACED_TELEGRAM_RICH_MESSAGES_ALLOWED_USER_IDS" env-separator:","`

	// DraftStreamingEnabled controls ephemeral sendRichMessageDraft previews
	// for eligible private rich turns. It stays deliberately independent from
	// bot.streaming.enabled: the two paths have different terminal ownership
	// and fallback semantics, so neither should silently imply the other.
	// Mode=off remains the kill switch even when this flag is true.
	DraftStreamingEnabled bool `yaml:"draft_streaming_enabled" env:"LAPLACED_TELEGRAM_RICH_MESSAGES_DRAFT_STREAMING_ENABLED"`
}

TelegramRichMessagesConfig controls outbound model-authored Rich Messages. Inbound Rich Messages are intentionally not feature-gated: disabling this config only selects the outbound representation.

func (TelegramRichMessagesConfig) AnyEnabled added in v0.11.0

func (r TelegramRichMessagesConfig) AnyEnabled() bool

AnyEnabled reports whether the transport must expose its Rich Message wire capability. Per-turn eligibility is still enforced by ModeForNativeUser.

func (TelegramRichMessagesConfig) ModeForNativeUser added in v0.11.0

func (r TelegramRichMessagesConfig) ModeForNativeUser(nativeUserID string) string

ModeForNativeUser returns off/shadow/send for a Telegram-native numeric user id. An unrecognized or absent mode still fails closed, and a non-numeric id (any non-Telegram principal) never reaches the Telegram-only rich path. When a canary list is configured the mode is narrowed to it; an empty list applies the configured mode to every Telegram-native user.

type TelemetryConfig added in v0.9.0

type TelemetryConfig struct {
	Enabled bool `yaml:"enabled" env:"LAPLACED_TELEMETRY_ENABLED"`
	// Exporter selects the span exporter backend. Valid values: "otlp"
	// (default, sends to an OTLP/gRPC collector like Alloy) and "stdout"
	// (pretty-prints spans to stderr; for local dev where network-level
	// trace delivery is not being tested).
	Exporter     string `yaml:"exporter" env:"LAPLACED_TELEMETRY_EXPORTER"`
	OTLPEndpoint string `yaml:"otlp_endpoint" env:"LAPLACED_TELEMETRY_OTLP_ENDPOINT"`
	ServiceName  string `yaml:"service_name" env:"LAPLACED_TELEMETRY_SERVICE_NAME"`
	// TraceContent, when true, asks tracing to record full content (LLM
	// request/response bodies, raw RAG queries, tool args/results) as span
	// events. Default off — flip only for debug sessions. Also surfaces as
	// resource attribute laplaced.trace_content on the captured trace.
	TraceContent bool `yaml:"trace_content" env:"LAPLACED_TELEMETRY_TRACE_CONTENT"`
}

TelemetryConfig defines configuration for OpenTelemetry export (traces first, metrics and logs as subsequent iterations). Disabled by default — callers must flip `enabled: true` (or set LAPLACED_TELEMETRY_ENABLED=true) to start an exporter. When disabled, the global OTel provider stays no-op.

type ToolConfig

type ToolConfig struct {
	Name                 string `yaml:"name"`
	Model                string `yaml:"model"`
	Description          string `yaml:"description"`
	ParameterDescription string `yaml:"parameter_description"`
}

type VaultAuthConfig added in v0.10.0

type VaultAuthConfig struct {
	// Method selects the auth backend: "token" | "kubernetes" | "approle".
	Method string `yaml:"method" env:"LAPLACED_VAULT_AUTH_METHOD"`
	// Role is the Vault role for kubernetes auth.
	Role string `yaml:"role" env:"LAPLACED_VAULT_AUTH_ROLE"`
	// MountPath overrides the auth mount (e.g. "kubernetes-2"). Empty = the
	// method's own default mount ("kubernetes" / "approle").
	MountPath string `yaml:"mount_path" env:"LAPLACED_VAULT_AUTH_MOUNT_PATH"`
	// ServiceAccountTokenPath overrides the Kubernetes service-account JWT path
	// (optional; defaults to the in-cluster location).
	ServiceAccountTokenPath string `yaml:"service_account_token_path" env:"LAPLACED_VAULT_K8S_SA_TOKEN_PATH"`
	// RoleID for approle auth. The matching secret_id is read ONLY from the
	// LAPLACED_VAULT_APPROLE_SECRET_ID env var, never from a config literal.
	RoleID string `yaml:"role_id" env:"LAPLACED_VAULT_APPROLE_ROLE_ID"`
}

VaultAuthConfig selects how the bot authenticates to Vault.

type VaultConfig added in v0.10.0

type VaultConfig struct {
	// Address of the Vault server. May be set here (often as ${VAULT_ADDR}) or
	// left empty to fall back to the standard VAULT_ADDR environment variable.
	Address string `yaml:"address" env:"VAULT_ADDR"`
	// Namespace for Vault Enterprise / HCP (optional).
	Namespace string          `yaml:"namespace" env:"VAULT_NAMESPACE"`
	Auth      VaultAuthConfig `yaml:"auth"`
}

VaultConfig enables pulling secrets from HashiCorp Vault. Its mere presence turns the feature on; the block carries only connection + auth. Which secret lives where (mount, path, engine) is encoded per-reference in the field value itself — see VaultRef / parseVaultRef — so different secrets can come from different mounts and engines without a global mount setting.

type VaultRef added in v0.10.0

type VaultRef struct {
	Kind  string // "kv2" | "kv1" | "raw"
	Mount string // secrets-engine mount, e.g. "secret"
	Path  string // path under the mount, e.g. "laplaced/dev"
	Key   string // field within the secret
}

VaultRef is a parsed "vault:" reference. Engine kind decides how a read response is unwrapped:

  • kv2: read <mount>/data/<path>, return the value of <key> under .data
  • kv1: read <mount>/<path>, return <key> from the flat map
  • raw: generic logical read of <mount>/<path>, return <key> from .Data (works for any engine; static reads only — leased/dynamic secrets are not renewed here).

Jump to

Keyboard shortcuts

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