mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-03 05:20:01 +00:00
Register Requesty (https://router.requesty.ai/v1) next to OpenRouter on the existing OpenAI-compatible transport: config and env vars (GOCLAW_REQUESTY_API_KEY, GOCLAW_REQUESTY_BASE_URL), secret masking, DB and in-memory registration, CLI setup, doctor, placeholder provider, OpenAPI enum, Web/Desktop provider lists and docs. The models list merges Requesty managed policies (GET /models/managed) with the key's catalog from GET /models.
682 lines
43 KiB
Go
682 lines
43 KiB
Go
package config
|
|
|
|
// ChatBehaviorConfig controls optional human-like channel delivery behavior.
|
|
// Pointer fields allow per-channel overrides to inherit gateway defaults.
|
|
type ChatBehaviorConfig struct {
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
IntermediateReplies *IntermediateRepliesConfig `json:"intermediate_replies,omitempty"`
|
|
QuickAck *QuickAckConfig `json:"quick_ack,omitempty"`
|
|
FinalSplit *FinalSplitConfig `json:"final_split,omitempty"`
|
|
}
|
|
|
|
// QuickAckConfig controls one short acknowledgement before longer non-streaming runs.
|
|
type QuickAckConfig struct {
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
Mode *string `json:"mode,omitempty"`
|
|
MinDelayMs *int `json:"min_delay_ms,omitempty"`
|
|
Provider string `json:"provider,omitempty"`
|
|
Model string `json:"model,omitempty"`
|
|
TimeoutMs *int `json:"timeout_ms,omitempty"`
|
|
MaxTokens *int `json:"max_tokens,omitempty"`
|
|
MaxChars *int `json:"max_chars,omitempty"`
|
|
Templates []string `json:"templates,omitempty"`
|
|
}
|
|
|
|
// IntermediateRepliesConfig controls delivery-only progress messages during tool phases.
|
|
type IntermediateRepliesConfig struct {
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
Mode *string `json:"mode,omitempty"`
|
|
Provider string `json:"provider,omitempty"`
|
|
Model string `json:"model,omitempty"`
|
|
TimeoutMs *int `json:"timeout_ms,omitempty"`
|
|
MaxTokens *int `json:"max_tokens,omitempty"`
|
|
MaxChars *int `json:"max_chars,omitempty"`
|
|
}
|
|
|
|
// FinalSplitConfig controls semantic splitting of final channel replies.
|
|
type FinalSplitConfig struct {
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
MinChars *int `json:"min_chars,omitempty"`
|
|
MaxMessages *int `json:"max_messages,omitempty"`
|
|
DelayMs *int `json:"delay_ms,omitempty"`
|
|
}
|
|
|
|
// PendingCompactionConfig configures LLM-based compaction of pending group messages.
|
|
// When a group accumulates more than Threshold pending messages, older messages are
|
|
// summarized by an LLM and replaced with a compact summary, keeping KeepRecent raw messages.
|
|
type PendingCompactionConfig struct {
|
|
Threshold int `json:"threshold,omitempty"` // trigger compaction when entries exceed this (default 200)
|
|
KeepRecent int `json:"keep_recent,omitempty"` // keep this many recent raw messages after compaction (default 40)
|
|
MaxTokens int `json:"max_tokens,omitempty"` // max output tokens for LLM summarization (default 4096)
|
|
Provider string `json:"provider,omitempty"` // LLM provider name (e.g. "openai"); empty = use agent's provider
|
|
Model string `json:"model,omitempty"` // model for summarization; empty = use agent's model
|
|
}
|
|
|
|
// TelegramManagerConfig controls the hidden Telegram management tool for this channel.
|
|
// It is intentionally channel-scoped instead of part of the global builtin tool profiles.
|
|
type TelegramManagerConfig struct {
|
|
Enabled bool `json:"enabled,omitempty"`
|
|
AllowedActions []string `json:"allowed_actions,omitempty"`
|
|
}
|
|
|
|
// ChannelsConfig contains per-channel configuration.
|
|
type ChannelsConfig struct {
|
|
Telegram TelegramConfig `json:"telegram"`
|
|
Discord DiscordConfig `json:"discord"`
|
|
Slack SlackConfig `json:"slack"`
|
|
WhatsApp WhatsAppConfig `json:"whatsapp"`
|
|
Zalo ZaloConfig `json:"zalo"`
|
|
ZaloPersonal ZaloPersonalConfig `json:"zalo_personal"`
|
|
Feishu FeishuConfig `json:"feishu"`
|
|
PendingCompaction *PendingCompactionConfig `json:"pending_compaction,omitempty"` // global pending message compaction settings
|
|
}
|
|
|
|
type TelegramConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
Token string `json:"token"`
|
|
Proxy string `json:"proxy,omitempty"`
|
|
APIServer string `json:"api_server,omitempty"` // custom Telegram Bot API server URL (e.g. "http://localhost:8081")
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "pairing" (default), "allowlist", "open", "disabled"
|
|
GroupPolicy string `json:"group_policy,omitempty"` // "open" (default), "allowlist", "disabled"
|
|
RequireMention *bool `json:"require_mention,omitempty"` // require @bot mention in groups (default true)
|
|
MentionMode string `json:"mention_mode,omitempty"` // "strict" (default) = only respond when mentioned; "yield" = respond unless another bot is mentioned
|
|
HistoryLimit int `json:"history_limit,omitempty"` // max pending group messages for context (default 50, 0=disabled)
|
|
DMStream *bool `json:"dm_stream,omitempty"` // enable streaming for DMs (default false) — edits placeholder progressively
|
|
GroupStream *bool `json:"group_stream,omitempty"` // enable streaming for groups (default false) — sends new message, edits progressively
|
|
DraftTransport *bool `json:"draft_transport,omitempty"` // use sendMessageDraft for DM streaming (default true) — stealth preview, no notifications per edit
|
|
ReasoningDelivery string `json:"reasoning_delivery,omitempty"` // off, streaming_only, always_bubbles
|
|
ReasoningStream *bool `json:"reasoning_stream,omitempty"` // show reasoning as separate message when provider emits thinking events (default true)
|
|
ReactionLevel string `json:"reaction_level,omitempty"` // "off" (default), "minimal", "full" — status emoji reactions
|
|
MediaMaxBytes int64 `json:"media_max_bytes,omitempty"` // max media download size in bytes (default 20MB)
|
|
LinkPreview *bool `json:"link_preview,omitempty"` // enable URL previews in messages (default true)
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
ForceIPv4 bool `json:"force_ipv4,omitempty"` // force IPv4 for all Telegram API requests (use when IPv6 routing is broken)
|
|
TelegramManager *TelegramManagerConfig `json:"telegram_manager,omitempty"` // hidden Telegram-only management tool permissions
|
|
|
|
// Optional STT (Speech-to-Text) pipeline for voice/audio inbound messages.
|
|
// When stt_proxy_url is set, audio/voice messages are transcribed before being forwarded to the agent.
|
|
STTProxyURL string `json:"stt_proxy_url,omitempty"` // base URL of the STT proxy service (e.g. "https://stt.example.com")
|
|
STTAPIKey string `json:"stt_api_key,omitempty"` // Bearer token for the STT proxy
|
|
STTTenantID string `json:"stt_tenant_id,omitempty"` // optional tenant/org identifier forwarded to the STT proxy
|
|
STTTimeoutSeconds int `json:"stt_timeout_seconds,omitempty"` // per-request timeout for STT calls (default 30s)
|
|
|
|
// Optional audio-aware routing: when set, voice/audio inbound messages are routed to this
|
|
// agent instead of the default channel agent. Requires the named agent to exist in the config.
|
|
VoiceAgentID string `json:"voice_agent_id,omitempty"` // agent ID to route voice inbound to (e.g. "speaking-agent")
|
|
|
|
// Audio guard: intercept technical errors in voice agent replies and replace with friendly fallbacks.
|
|
// Only active when VoiceAgentID is set. Custom error markers replace built-in defaults when provided.
|
|
AudioGuardFallbackTranscript string `json:"audio_guard_fallback_transcript,omitempty"` // fallback with %s for transcript (e.g. "I heard: \"%s\". Try again!")
|
|
AudioGuardFallbackNoTranscript string `json:"audio_guard_fallback_no_transcript,omitempty"` // fallback when no transcript available
|
|
AudioGuardErrorMarkers []string `json:"audio_guard_error_markers,omitempty"` // custom error detection markers (replaces defaults)
|
|
|
|
// Per-group (and per-topic) overrides. Key is chat ID string (e.g. "-100123456") or "*" for wildcard.
|
|
// TS ref: channels.telegram.groups in src/config/types.telegram.ts.
|
|
Groups map[string]*TelegramGroupConfig `json:"groups,omitempty"`
|
|
}
|
|
|
|
// TelegramGroupConfig defines per-group overrides for a Telegram channel.
|
|
// Matching TS TelegramGroupConfig in src/config/types.telegram.ts.
|
|
type TelegramGroupConfig struct {
|
|
GroupPolicy string `json:"group_policy,omitempty"` // override group policy for this group
|
|
RequireMention *bool `json:"require_mention,omitempty"` // override require_mention for this group
|
|
MentionMode string `json:"mention_mode,omitempty"` // override mention_mode for this group
|
|
AllowFrom FlexibleStringSlice `json:"allow_from,omitempty"` // override allow_from for this group
|
|
Enabled *bool `json:"enabled,omitempty"` // disable bot for this group (default: true)
|
|
Skills []string `json:"skills,omitempty"` // skill whitelist (nil = all, [] = none)
|
|
Tools []string `json:"tools,omitempty"` // tool allow list (nil = all, supports "group:xxx")
|
|
SystemPrompt string `json:"system_prompt,omitempty"` // extra system prompt for this group
|
|
Topics map[string]*TelegramTopicConfig `json:"topics,omitempty"` // per-topic overrides (key: thread ID string)
|
|
Quota *QuotaWindow `json:"quota,omitempty"` // per-group quota override
|
|
}
|
|
|
|
// TelegramTopicConfig defines per-topic overrides within a Telegram group.
|
|
// Matching TS TelegramTopicConfig in src/config/types.telegram.ts.
|
|
type TelegramTopicConfig struct {
|
|
RequireMention *bool `json:"require_mention,omitempty"`
|
|
MentionMode string `json:"mention_mode,omitempty"`
|
|
GroupPolicy string `json:"group_policy,omitempty"`
|
|
Skills []string `json:"skills,omitempty"`
|
|
Tools []string `json:"tools,omitempty"` // tool allow list (nil = inherit, supports "group:xxx")
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from,omitempty"`
|
|
SystemPrompt string `json:"system_prompt,omitempty"`
|
|
}
|
|
|
|
type DiscordConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
Token string `json:"token"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "open" (default), "allowlist", "disabled"
|
|
GroupPolicy string `json:"group_policy,omitempty"` // "open" (default), "allowlist", "disabled"
|
|
RequireMention *bool `json:"require_mention,omitempty"` // require @bot mention in groups (default true)
|
|
HistoryLimit int `json:"history_limit,omitempty"` // max pending group messages for context (default 200, 0=disabled)
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
MediaMaxBytes int64 `json:"media_max_bytes,omitempty"` // max media download size (default 25MB)
|
|
STTProxyURL string `json:"stt_proxy_url,omitempty"`
|
|
STTAPIKey string `json:"stt_api_key,omitempty"`
|
|
STTTenantID string `json:"stt_tenant_id,omitempty"`
|
|
STTTimeoutSeconds int `json:"stt_timeout_seconds,omitempty"`
|
|
VoiceAgentID string `json:"voice_agent_id,omitempty"`
|
|
}
|
|
|
|
type SlackConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
BotToken string `json:"bot_token"` // xoxb-... (Bot User OAuth Token)
|
|
AppToken string `json:"app_token"` // xapp-... (App-Level Token for Socket Mode)
|
|
UserToken string `json:"user_token,omitempty"` // xoxp-... (Optional: custom bot identity)
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "pairing" (default), "allowlist", "open", "disabled"
|
|
GroupPolicy string `json:"group_policy,omitempty"` // "open" (default), "pairing", "allowlist", "disabled"
|
|
RequireMention *bool `json:"require_mention,omitempty"` // require @bot mention in channels (default true)
|
|
HistoryLimit int `json:"history_limit,omitempty"` // max pending group messages for context (default 50, 0=disabled)
|
|
DMStream *bool `json:"dm_stream,omitempty"` // enable streaming for DMs (default false)
|
|
GroupStream *bool `json:"group_stream,omitempty"` // enable streaming for groups (default false)
|
|
NativeStream *bool `json:"native_stream,omitempty"` // use Slack ChatStreamer API if available (default false)
|
|
ReactionLevel string `json:"reaction_level,omitempty"` // "off" (default), "minimal", "full"
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
DebounceDelay *int `json:"debounce_delay,omitempty"` // ms delay before dispatching rapid messages (default 300, 0=disabled)
|
|
ThreadTTL *int `json:"thread_ttl,omitempty"` // hours before thread participation expires (default 24, 0=disabled — always require @mention)
|
|
MediaMaxBytes int64 `json:"media_max_bytes,omitempty"` // max file download size in bytes (default 20MB)
|
|
}
|
|
|
|
type WhatsAppConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
AuthDir string `json:"auth_dir,omitempty"` // optional: SQLite auth dir override (desktop)
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "pairing" (default for DB instances), "open", "allowlist", "disabled"
|
|
GroupPolicy string `json:"group_policy,omitempty"` // "pairing" (default for DB instances), "open" (default for config), "allowlist", "disabled"
|
|
RequireMention *bool `json:"require_mention,omitempty"` // only respond in groups when bot is @mentioned (default false)
|
|
HistoryLimit int `json:"history_limit,omitempty"` // max pending group messages for context (default 200, 0=disabled)
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
}
|
|
|
|
type ZaloConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
Token string `json:"token"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "pairing" (default), "allowlist", "open", "disabled"
|
|
WebhookURL string `json:"webhook_url,omitempty"`
|
|
WebhookSecret string `json:"webhook_secret,omitempty"`
|
|
MediaMaxMB int `json:"media_max_mb,omitempty"` // default 5
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
}
|
|
|
|
type ZaloPersonalConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "pairing" (default), "allowlist", "open", "disabled"
|
|
GroupPolicy string `json:"group_policy,omitempty"` // "open" (default), "allowlist", "disabled"
|
|
RequireMention *bool `json:"require_mention,omitempty"` // require @bot mention in groups (default true)
|
|
HistoryLimit int `json:"history_limit,omitempty"` // max pending group messages for context (default 50, 0=disabled)
|
|
CredentialsPath string `json:"credentials_path,omitempty"` // path to saved cookies JSON
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
}
|
|
|
|
type FeishuConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
AppID string `json:"app_id"`
|
|
AppSecret string `json:"app_secret"`
|
|
EncryptKey string `json:"encrypt_key,omitempty"`
|
|
VerificationToken string `json:"verification_token,omitempty"`
|
|
Domain string `json:"domain,omitempty"` // "lark" (default/global), "feishu" (China), or custom URL
|
|
ConnectionMode string `json:"connection_mode,omitempty"` // "websocket" (default), "webhook"
|
|
WebhookPort int `json:"webhook_port,omitempty"` // default 3000
|
|
WebhookPath string `json:"webhook_path,omitempty"` // default "/feishu/events"
|
|
AllowFrom FlexibleStringSlice `json:"allow_from"`
|
|
DMPolicy string `json:"dm_policy,omitempty"` // "pairing" (default)
|
|
GroupPolicy string `json:"group_policy,omitempty"` // "open" (default)
|
|
GroupAllowFrom FlexibleStringSlice `json:"group_allow_from,omitempty"`
|
|
RequireMention *bool `json:"require_mention,omitempty"` // default true (groups)
|
|
TopicSessionMode string `json:"topic_session_mode,omitempty"` // "disabled" (default)
|
|
TextChunkLimit int `json:"text_chunk_limit,omitempty"` // default 4000
|
|
MediaMaxMB int `json:"media_max_mb,omitempty"` // default 30
|
|
RenderMode string `json:"render_mode,omitempty"` // "auto", "raw", "card"
|
|
Streaming *bool `json:"streaming,omitempty"` // default true
|
|
ReactionLevel string `json:"reaction_level,omitempty"` // "off" (default), "minimal", "full" — typing emoji reactions
|
|
HistoryLimit int `json:"history_limit,omitempty"`
|
|
BlockReply *bool `json:"block_reply,omitempty"` // override gateway block_reply (nil = inherit)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // override gateway chat behavior (nil = inherit)
|
|
STTProxyURL string `json:"stt_proxy_url,omitempty"`
|
|
STTAPIKey string `json:"stt_api_key,omitempty"`
|
|
STTTenantID string `json:"stt_tenant_id,omitempty"`
|
|
STTTimeoutSeconds int `json:"stt_timeout_seconds,omitempty"`
|
|
VoiceAgentID string `json:"voice_agent_id,omitempty"`
|
|
}
|
|
|
|
// ProvidersConfig maps provider name to its config.
|
|
type ProvidersConfig struct {
|
|
Anthropic ProviderConfig `json:"anthropic"`
|
|
OpenAI ProviderConfig `json:"openai"`
|
|
AtlasCloud ProviderConfig `json:"atlascloud"` // Atlas Cloud (OpenAI-compatible endpoint)
|
|
APIRoute ProviderConfig `json:"api_route"` // API Route (OpenAI-compatible endpoint)
|
|
Requesty ProviderConfig `json:"requesty"` // Requesty (OpenAI-compatible router)
|
|
OpenRouter ProviderConfig `json:"openrouter"`
|
|
Groq ProviderConfig `json:"groq"`
|
|
Gemini ProviderConfig `json:"gemini"`
|
|
DeepSeek ProviderConfig `json:"deepseek"`
|
|
Mistral ProviderConfig `json:"mistral"`
|
|
XAI ProviderConfig `json:"xai"`
|
|
MiniMax ProviderConfig `json:"minimax"`
|
|
Cohere ProviderConfig `json:"cohere"`
|
|
Perplexity ProviderConfig `json:"perplexity"`
|
|
DashScope ProviderConfig `json:"dashscope"`
|
|
Bailian ProviderConfig `json:"bailian"`
|
|
Zai ProviderConfig `json:"zai"`
|
|
ZaiCoding ProviderConfig `json:"zai_coding"`
|
|
Ollama OllamaConfig `json:"ollama"` // local Ollama instance (no API key needed)
|
|
OllamaCloud ProviderConfig `json:"ollama_cloud"` // Ollama Cloud (API key required)
|
|
ClaudeCLI ClaudeCLIConfig `json:"claude_cli"`
|
|
ACP ACPConfig `json:"acp"`
|
|
Novita ProviderConfig `json:"novita"` // Novita AI (OpenAI-compatible endpoint)
|
|
BytePlus ProviderConfig `json:"byteplus"` // BytePlus ModelArk (Seed 2.0)
|
|
BytePlusCoding ProviderConfig `json:"byteplus_coding"` // BytePlus ModelArk Coding Plan
|
|
Vertex VertexConfig `json:"vertex"` // Google Cloud Vertex AI (OAuth2 service account + ADC)
|
|
|
|
// RequestTimeoutSec bounds provider verify and models-list HTTP calls.
|
|
// Tenant-scoped, overridable via the "providers.request_timeout_sec" system config.
|
|
RequestTimeoutSec int `json:"request_timeout_sec,omitempty"`
|
|
}
|
|
|
|
// VertexConfig configures Google Cloud Vertex AI.
|
|
// Credentials precedence: APIKey (inline JSON) > CredentialsFile (path) > ADC (both empty).
|
|
// ProjectID and Region are required; Model optional (defaults to google/gemini-2.0-flash-001).
|
|
type VertexConfig struct {
|
|
APIKey string `json:"api_key,omitempty"` // service account JSON inline (secret — never persist in config.json)
|
|
CredentialsFile string `json:"credentials_file,omitempty"` // path to service account JSON file
|
|
ProjectID string `json:"project_id,omitempty"`
|
|
Region string `json:"region,omitempty"`
|
|
Model string `json:"model,omitempty"`
|
|
}
|
|
|
|
// OllamaConfig configures a local (or self-hosted) Ollama instance.
|
|
// No API key is required — Ollama accepts any Bearer token value.
|
|
type OllamaConfig struct {
|
|
Host string `json:"host"` // Ollama server base URL, e.g. http://localhost:11434
|
|
}
|
|
|
|
// ClaudeCLIConfig configures the Claude CLI provider (uses subscription, not API key).
|
|
type ClaudeCLIConfig struct {
|
|
CLIPath string `json:"cli_path" yaml:"cli_path"` // path to claude binary (default: "claude")
|
|
Model string `json:"model" yaml:"model"` // default model alias (default: "sonnet")
|
|
BaseWorkDir string `json:"base_work_dir" yaml:"base_work_dir"` // base dir for agent workspaces
|
|
PermMode string `json:"perm_mode" yaml:"perm_mode"` // permission mode (default: "bypassPermissions")
|
|
}
|
|
|
|
// ACPConfig configures the ACP (Agent Client Protocol) provider.
|
|
// Orchestrates any ACP-compatible coding agent (Claude Code, Codex CLI, Gemini CLI) as a subprocess.
|
|
type ACPConfig struct {
|
|
Binary string `json:"binary"` // agent binary name or path (e.g. "claude", "codex")
|
|
Args []string `json:"args"` // extra spawn args
|
|
Model string `json:"model"` // default model/agent name
|
|
WorkDir string `json:"work_dir"` // base workspace dir
|
|
IdleTTL string `json:"idle_ttl"` // process idle TTL (e.g. "5m")
|
|
PermMode string `json:"perm_mode"` // "approve-all" (default), "approve-reads", "deny-all"
|
|
}
|
|
|
|
type ProviderConfig struct {
|
|
APIKey string `json:"api_key"`
|
|
APIBase string `json:"api_base,omitempty"`
|
|
}
|
|
|
|
// APIBaseForType returns the config-level api_base for a given provider type.
|
|
// Used as a fallback when DB providers have no api_base set.
|
|
func (p *ProvidersConfig) APIBaseForType(providerType string) string {
|
|
switch providerType {
|
|
case "anthropic_native":
|
|
return p.Anthropic.APIBase
|
|
case "openai", "openai_compat":
|
|
return p.OpenAI.APIBase
|
|
case "atlascloud":
|
|
return p.AtlasCloud.APIBase
|
|
case "api_route":
|
|
return p.APIRoute.APIBase
|
|
case "requesty":
|
|
return p.Requesty.APIBase
|
|
case "openrouter":
|
|
return p.OpenRouter.APIBase
|
|
case "groq":
|
|
return p.Groq.APIBase
|
|
case "deepseek":
|
|
return p.DeepSeek.APIBase
|
|
case "gemini_native":
|
|
return p.Gemini.APIBase
|
|
case "mistral":
|
|
return p.Mistral.APIBase
|
|
case "xai":
|
|
return p.XAI.APIBase
|
|
case "minimax_native":
|
|
return p.MiniMax.APIBase
|
|
case "cohere":
|
|
return p.Cohere.APIBase
|
|
case "perplexity":
|
|
return p.Perplexity.APIBase
|
|
case "dashscope":
|
|
return p.DashScope.APIBase
|
|
case "bailian":
|
|
return p.Bailian.APIBase
|
|
case "zai":
|
|
return p.Zai.APIBase
|
|
case "zai_coding":
|
|
return p.ZaiCoding.APIBase
|
|
case "ollama_cloud":
|
|
return p.OllamaCloud.APIBase
|
|
case "novita":
|
|
return p.Novita.APIBase
|
|
case "byteplus":
|
|
return p.BytePlus.APIBase
|
|
case "byteplus_coding":
|
|
return p.BytePlusCoding.APIBase
|
|
case "vertex":
|
|
// Computed from project+region at registration time; no config-level static base.
|
|
return ""
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// HasAnyProvider returns true if at least one provider has an API key or CLI configured.
|
|
func (c *Config) HasAnyProvider() bool {
|
|
p := c.Providers
|
|
return p.Anthropic.APIKey != "" ||
|
|
p.OpenAI.APIKey != "" ||
|
|
p.AtlasCloud.APIKey != "" ||
|
|
p.APIRoute.APIKey != "" ||
|
|
p.Requesty.APIKey != "" ||
|
|
p.OpenRouter.APIKey != "" ||
|
|
p.Groq.APIKey != "" ||
|
|
p.Gemini.APIKey != "" ||
|
|
p.DeepSeek.APIKey != "" ||
|
|
p.Mistral.APIKey != "" ||
|
|
p.XAI.APIKey != "" ||
|
|
p.MiniMax.APIKey != "" ||
|
|
p.Cohere.APIKey != "" ||
|
|
p.Perplexity.APIKey != "" ||
|
|
p.DashScope.APIKey != "" ||
|
|
p.Bailian.APIKey != "" ||
|
|
p.Zai.APIKey != "" ||
|
|
p.ZaiCoding.APIKey != "" ||
|
|
p.Ollama.Host != "" ||
|
|
p.OllamaCloud.APIKey != "" ||
|
|
p.ClaudeCLI.CLIPath != "" ||
|
|
p.ACP.Binary != "" ||
|
|
p.Novita.APIKey != "" ||
|
|
p.BytePlus.APIKey != "" ||
|
|
p.BytePlusCoding.APIKey != "" ||
|
|
(p.Vertex.ProjectID != "" && p.Vertex.Region != "")
|
|
}
|
|
|
|
// QuotaWindow defines request limits per time window. Zero means unlimited.
|
|
type QuotaWindow struct {
|
|
Hour int `json:"hour,omitempty"` // max requests per hour (0 = unlimited)
|
|
Day int `json:"day,omitempty"` // max requests per day (0 = unlimited)
|
|
Week int `json:"week,omitempty"` // max requests per week (0 = unlimited)
|
|
}
|
|
|
|
// IsZero returns true if no limits are set.
|
|
func (w QuotaWindow) IsZero() bool { return w.Hour == 0 && w.Day == 0 && w.Week == 0 }
|
|
|
|
// QuotaConfig configures per-user/group request quotas.
|
|
// Config merge priority: Groups > Channels > Providers > Default.
|
|
type QuotaConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
Default QuotaWindow `json:"default"`
|
|
Providers map[string]QuotaWindow `json:"providers,omitempty"` // key = provider name (e.g. "anthropic")
|
|
Channels map[string]QuotaWindow `json:"channels,omitempty"` // key = channel name (e.g. "telegram")
|
|
Groups map[string]QuotaWindow `json:"groups,omitempty"` // key = userID (e.g. "group:telegram:-100123")
|
|
}
|
|
|
|
// GatewayConfig controls the gateway server.
|
|
type GatewayConfig struct {
|
|
Host string `json:"host"`
|
|
Port int `json:"port"`
|
|
Token string `json:"token,omitempty"` // bearer token for WS/HTTP auth
|
|
MCPServerToken string `json:"mcp_server_token,omitempty"` // bearer token gating the CRUD MCP server mounted at /api/mcp/; callers may pass an optional "X-GoClaw-Tenant-Id" header (UUID or slug) to scope a request to a tenant, defaulting to the master tenant when absent (see internal/mcp/crud_server.go)
|
|
OwnerIDs []string `json:"owner_ids,omitempty"` // sender IDs considered "owner"
|
|
AllowedOrigins []string `json:"allowed_origins,omitempty"` // WebSocket CORS whitelist (empty = allow all)
|
|
MCPAllowedHosts []string `json:"mcp_allowed_hosts,omitempty"` // trusted MCP server hostnames exempt from the private-IP SSRF block during config validation (empty = none)
|
|
MaxMessageChars int `json:"max_message_chars,omitempty"` // max user message characters (default 32000)
|
|
RateLimitRPM int `json:"rate_limit_rpm,omitempty"` // rate limit: requests per minute per user (default 20, 0 = disabled)
|
|
InjectionAction string `json:"injection_action,omitempty"` // prompt injection action: "log", "warn" (default), "block", "off"
|
|
InboundDebounceMs int `json:"inbound_debounce_ms,omitempty"` // silence-window in ms that merges rapid channel/Web Chat messages from the same sender/session; 0 disables for text but media-bearing messages still honor a built-in media floor so multi-attachment bursts (#63) coalesce into a single agent run. Agents may override via per-agent agent_config.inbound_debounce_ms.
|
|
Quota *QuotaConfig `json:"quota,omitempty"` // per-user/group request quotas
|
|
BlockReply *bool `json:"block_reply,omitempty"` // deliver intermediate text during tool iterations (default false)
|
|
ChatBehavior *ChatBehaviorConfig `json:"chat_behavior,omitempty"` // human-like channel delivery behavior (default disabled)
|
|
ToolStatus *bool `json:"tool_status,omitempty"` // show tool name in streaming preview during tool execution (default true)
|
|
TeamWorkClassify *bool `json:"team_work_classify,omitempty"` // classify new requests as direct handling or team workflow (default false)
|
|
TaskRecoveryIntervalSec int `json:"task_recovery_interval_sec,omitempty"` // team task recovery ticker interval in seconds (default 300 = 5min)
|
|
WebhookAsyncTimeoutSec int `json:"webhook_async_timeout_sec,omitempty"` // async webhook worker agent-run deadline in seconds (default 600, cap 3600)
|
|
WebhookSyncTimeoutSec int `json:"webhook_sync_timeout_sec,omitempty"` // sync + test webhook handler agent-run deadline in seconds (default 600, cap 3600). NOTE: sync holds the HTTP connection open for this duration — a value above an upstream proxy/LB read timeout may be cut.
|
|
WebhookStream *bool `json:"webhook_stream,omitempty"` // stream provider responses for server-side webhook agent runs (sync/async/test) so the upstream can populate/serve its prompt cache (default true). Response to the caller is unchanged (still assembled JSON).
|
|
BackgroundProvider string `json:"background_provider,omitempty"` // LLM provider for background workers (vault enrichment, consolidation)
|
|
BackgroundModel string `json:"background_model,omitempty"` // LLM model for background workers
|
|
PublicURL string `json:"public_url,omitempty"` // public base URL for OAuth callbacks (e.g. "https://goclaw.example.com")
|
|
}
|
|
|
|
// ToolsConfig controls tool availability, policy, and web search.
|
|
type ToolsConfig struct {
|
|
Profile string `json:"profile,omitempty"` // global profile: "minimal", "coding", "messaging", "full"
|
|
Allow []string `json:"allow,omitempty"` // global allow list (tool names or "group:xxx")
|
|
Deny []string `json:"deny,omitempty"` // global deny list
|
|
AlsoAllow []string `json:"alsoAllow,omitempty"` // additive: adds without removing existing
|
|
ByProvider map[string]*ToolPolicySpec `json:"byProvider,omitempty"` // per-provider overrides
|
|
ShellDenyGroups map[string]bool `json:"shellDenyGroups,omitempty"` // global shell deny-group toggles (group name -> denied); per-agent overrides win per-key
|
|
CommandKeywordAllowlist []CommandKeywordAllowlistRule `json:"commandKeywordAllowlist,omitempty"` // scoped bypass for credentialed CLI content keywords
|
|
ExecApproval ExecApprovalCfg `json:"execApproval"` // exec command approval settings
|
|
WebFetch WebFetchPolicyConfig `json:"web_fetch"` // domain policy for URL fetching
|
|
Browser BrowserToolConfig `json:"browser"`
|
|
RateLimitPerHour int `json:"rate_limit_per_hour,omitempty"` // max tool executions per hour per session (0 = disabled)
|
|
ScrubCredentials *bool `json:"scrub_credentials,omitempty"` // auto-redact API keys/tokens in tool output (default true)
|
|
McpServers map[string]*MCPServerConfig `json:"mcp_servers,omitempty"` // external MCP server connections
|
|
DocumentParser DocumentParserConfig `json:"document_parser"` // local-first document text extraction
|
|
}
|
|
|
|
// DocumentParserConfig controls local-first document text extraction in the
|
|
// read_document tool. Local-first is OPT-IN (disabled by default): plain
|
|
// pdftotext/pandoc output mis-orders tables/multi-column layouts and ignores
|
|
// the analysis prompt, so the cloud vision path remains the default. When
|
|
// enabled, PDF/DOCX are extracted via local binaries before any cloud LLM
|
|
// call; missing binaries or empty output fall back to the provider chain.
|
|
//
|
|
// Changing these values requires a gateway restart — they are captured at tool
|
|
// construction and not picked up by config hot-reload. (Binary availability,
|
|
// however, is re-checked per call, so runtime installs are seen without a
|
|
// restart.)
|
|
type DocumentParserConfig struct {
|
|
LocalFirst *bool `json:"local_first,omitempty"` // nil => false (default OFF)
|
|
MaxPages int `json:"max_pages,omitempty"` // 0 => 200
|
|
TimeoutSec int `json:"timeout_sec,omitempty"` // 0 => 30s
|
|
MinTextLen int `json:"min_text_len,omitempty"` // 0 => 16
|
|
}
|
|
|
|
// LocalFirstEnabled reports whether local-first extraction is turned on.
|
|
func (c DocumentParserConfig) LocalFirstEnabled() bool {
|
|
return c.LocalFirst != nil && *c.LocalFirst
|
|
}
|
|
|
|
// CommandKeywordAllowlistRule scopes product/security vocabulary that may appear
|
|
// in credentialed CLI content args without disabling command-path deny patterns.
|
|
type CommandKeywordAllowlistRule struct {
|
|
ID string `json:"id,omitempty"`
|
|
Command string `json:"command"`
|
|
Subcommands []string `json:"subcommands,omitempty"`
|
|
Args []string `json:"args,omitempty"`
|
|
ArgPositions []int `json:"argPositions,omitempty"` // 0-based after matched subcommand
|
|
Keywords []string `json:"keywords,omitempty"`
|
|
Reason string `json:"reason,omitempty"`
|
|
Enabled *bool `json:"enabled,omitempty"` // nil means enabled
|
|
}
|
|
|
|
// MCPServerConfig configures a single external MCP server connection.
|
|
type MCPServerConfig struct {
|
|
Transport string `json:"transport"` // "stdio", "sse", "streamable-http"
|
|
Command string `json:"command,omitempty"` // stdio: command to spawn
|
|
Args []string `json:"args,omitempty"` // stdio: command arguments
|
|
Env map[string]string `json:"env,omitempty"` // stdio: extra environment variables
|
|
URL string `json:"url,omitempty"` // sse/http: server URL
|
|
Headers map[string]string `json:"headers,omitempty"` // sse/http: extra HTTP headers
|
|
Enabled *bool `json:"enabled,omitempty"` // default true
|
|
ToolPrefix string `json:"tool_prefix,omitempty"` // prefix for tool names (avoids collisions)
|
|
TimeoutSec int `json:"timeout_sec,omitempty"` // per-tool-call timeout in seconds (default 60)
|
|
}
|
|
|
|
// IsEnabled returns whether this MCP server is enabled (default true).
|
|
func (c *MCPServerConfig) IsEnabled() bool {
|
|
return c.Enabled == nil || *c.Enabled
|
|
}
|
|
|
|
// ExecApprovalCfg configures command execution approval (matching TS exec-approval.ts).
|
|
type ExecApprovalCfg struct {
|
|
Security string `json:"security,omitempty"` // "deny", "allowlist", "full" (default "full")
|
|
Ask string `json:"ask,omitempty"` // "off", "on-miss", "always" (default "off")
|
|
Allowlist []string `json:"allowlist,omitempty"` // glob patterns for allowed commands
|
|
}
|
|
|
|
// WebFetchPolicyConfig controls domain filtering for the web_fetch tool.
|
|
type WebFetchPolicyConfig struct {
|
|
Policy string `json:"policy,omitempty"` // "allow_all" (default), "allowlist"
|
|
AllowedDomains []string `json:"allowed_domains,omitempty"` // e.g. ["github.com", "*.example.com"]
|
|
BlockedDomains []string `json:"blocked_domains,omitempty"` // always checked regardless of policy
|
|
}
|
|
|
|
// BrowserToolConfig controls the browser automation tool.
|
|
type BrowserToolConfig struct {
|
|
Enabled bool `json:"enabled"` // enable the browser tool (default false)
|
|
Backend string `json:"backend,omitempty"` // "chrome" (default) or "lightpanda"; auto-detected from /json/version if empty
|
|
Headless bool `json:"headless,omitempty"` // run Chrome in headless mode (ignored when RemoteURL is set)
|
|
RemoteURL string `json:"remote_url,omitempty"` // CDP endpoint for remote Chrome sidecar, e.g. "ws://chrome:9222"
|
|
ActionTimeoutMs int `json:"action_timeout_ms,omitempty"` // per-action timeout in ms (default 30000)
|
|
IdleTimeoutMs int `json:"idle_timeout_ms,omitempty"` // idle page auto-close in ms (default 600000, 0=disabled)
|
|
MaxPages int `json:"max_pages,omitempty"` // max open pages per tenant (default 5)
|
|
CookieSyncEnabled bool `json:"cookie_sync_enabled"` // apply selected synced cookies to scoped browser sessions
|
|
}
|
|
|
|
// ToolPolicySpec defines a tool policy at any level (global, per-agent, per-provider).
|
|
type ToolPolicySpec struct {
|
|
Profile string `json:"profile,omitempty"`
|
|
Allow []string `json:"allow,omitempty"`
|
|
Deny []string `json:"deny,omitempty"`
|
|
AlsoAllow []string `json:"alsoAllow,omitempty"`
|
|
ByProvider map[string]*ToolPolicySpec `json:"byProvider,omitempty"`
|
|
Wait *WaitToolPolicy `json:"wait,omitempty"`
|
|
ToolCallPrefix string `json:"toolCallPrefix,omitempty"` // prefix to strip from model's tool call names before registry lookup
|
|
// RateLimitPerHour overrides the global tools.rate_limit_per_hour for this
|
|
// agent (applied per session key). 0 = inherit the global limit.
|
|
RateLimitPerHour int `json:"rate_limit_per_hour,omitempty"`
|
|
}
|
|
|
|
// WaitToolPolicy configures per-agent safety bounds for the wait tool.
|
|
type WaitToolPolicy struct {
|
|
MinMs int `json:"min_ms,omitempty"`
|
|
MaxMs int `json:"max_ms,omitempty"`
|
|
}
|
|
|
|
// SessionsConfig controls session behavior.
|
|
// Matching TS src/config/sessions/types.ts + src/config/types.base.ts.
|
|
type SessionsConfig struct {
|
|
Scope string `json:"scope,omitempty"` // "per-sender" (default), "global"
|
|
DmScope string `json:"dm_scope,omitempty"` // "main", "per-peer", "per-channel-peer" (default), "per-account-channel-peer"
|
|
MainKey string `json:"main_key,omitempty"` // main session key suffix (default "main", used when dm_scope="main")
|
|
}
|
|
|
|
// TtsConfig configures text-to-speech.
|
|
// Matching TS src/config/types.tts.ts.
|
|
type TtsConfig struct {
|
|
Provider string `json:"provider,omitempty"` // "openai", "openai_compat", "elevenlabs", "edge", "minimax", "gemini"
|
|
Auto string `json:"auto,omitempty"` // "off" (default), "always", "inbound", "tagged"
|
|
Mode string `json:"mode,omitempty"` // "final" (default), "all"
|
|
MaxLength int `json:"max_length,omitempty"` // max text length before truncation (default 1500)
|
|
TimeoutMs int `json:"timeout_ms,omitempty"` // API timeout in ms (default 30000)
|
|
OpenAI TtsOpenAIConfig `json:"openai"`
|
|
OpenAICompat TtsOpenAICompatConfig `json:"openai_compat"`
|
|
ElevenLabs TtsElevenLabsConfig `json:"elevenlabs"`
|
|
Edge TtsEdgeConfig `json:"edge"`
|
|
MiniMax TtsMiniMaxConfig `json:"minimax"`
|
|
Gemini TtsGeminiConfig `json:"gemini"`
|
|
}
|
|
|
|
// TtsOpenAICompatConfig configures a self-hosted OpenAI-compatible audio
|
|
// endpoint (gpu-manager, Speaches, vLLM, LocalAI, llama.cpp, Ollama) for both
|
|
// TTS and STT. Kept separate from TtsOpenAIConfig because the voice namespace
|
|
// is the engine's own — see internal/audio/openaicompat for why the provider
|
|
// name must not be "openai".
|
|
//
|
|
// Setting api_base is what enables the provider; there is no vendor default.
|
|
type TtsOpenAICompatConfig struct {
|
|
APIBase string `json:"api_base,omitempty"` // required to enable, e.g. "http://gpu-manager:8080/v1"
|
|
APIKey string `json:"api_key,omitempty"` // optional; omitted entirely when empty
|
|
Model string `json:"model,omitempty"` // TTS model; optional when the engine picks it from the voice
|
|
Voice string `json:"voice,omitempty"` // engine-specific voice ID, e.g. "fr_FR-gilles-low"
|
|
Format string `json:"format,omitempty"` // response_format; default "mp3", use "wav" for engines without an encoder
|
|
STTModel string `json:"stt_model,omitempty"` // transcription model; optional
|
|
}
|
|
|
|
// TtsGeminiConfig configures the Google Gemini TTS provider.
|
|
type TtsGeminiConfig struct {
|
|
APIKey string `json:"api_key,omitempty"` // required; encrypted at rest
|
|
APIBase string `json:"api_base,omitempty"` // custom endpoint (optional; SSRF-gated)
|
|
Voice string `json:"voice,omitempty"` // default "Kore"
|
|
Model string `json:"model,omitempty"` // default "gemini-2.5-flash-preview-tts"
|
|
Speakers string `json:"speakers,omitempty"` // JSON-encoded []SpeakerVoice for multi-speaker mode
|
|
}
|
|
|
|
// TtsOpenAIConfig configures the OpenAI TTS provider.
|
|
type TtsOpenAIConfig struct {
|
|
APIKey string `json:"api_key,omitempty"`
|
|
APIBase string `json:"api_base,omitempty"` // custom endpoint URL
|
|
Model string `json:"model,omitempty"` // default "gpt-4o-mini-tts"
|
|
Voice string `json:"voice,omitempty"` // default "alloy"
|
|
}
|
|
|
|
// TtsElevenLabsConfig configures the ElevenLabs TTS provider.
|
|
type TtsElevenLabsConfig struct {
|
|
APIKey string `json:"api_key,omitempty"`
|
|
BaseURL string `json:"base_url,omitempty"`
|
|
VoiceID string `json:"voice_id,omitempty"` // default "pMsXgVXv3BLzUgSXRplE"
|
|
ModelID string `json:"model_id,omitempty"` // default "eleven_multilingual_v2"
|
|
}
|
|
|
|
// TtsEdgeConfig configures the Microsoft Edge TTS provider (free, no API key).
|
|
type TtsEdgeConfig struct {
|
|
Enabled bool `json:"enabled,omitempty"`
|
|
Voice string `json:"voice,omitempty"` // default "en-US-MichelleNeural"
|
|
Rate string `json:"rate,omitempty"` // speech rate, e.g. "+0%"
|
|
}
|
|
|
|
// TtsMiniMaxConfig configures the MiniMax TTS provider.
|
|
type TtsMiniMaxConfig struct {
|
|
APIKey string `json:"api_key,omitempty"`
|
|
GroupID string `json:"group_id,omitempty"` // MiniMax GroupId (required)
|
|
APIBase string `json:"api_base,omitempty"` // default "https://api.minimax.io/v1"
|
|
Model string `json:"model,omitempty"` // default "speech-02-hd"
|
|
VoiceID string `json:"voice_id,omitempty"` // default "Wise_Woman"
|
|
}
|
|
|
|
// MergeChannelGroupQuotas merges per-group quota overrides from channel configs
|
|
// (e.g., channels.telegram.groups[chatID].quota) into gateway.quota.groups.
|
|
// This allows per-group quotas to be set at the channel level and picked up
|
|
// by the QuotaChecker at runtime.
|
|
func MergeChannelGroupQuotas(cfg *Config) {
|
|
if cfg.Gateway.Quota == nil {
|
|
return
|
|
}
|
|
if cfg.Gateway.Quota.Groups == nil {
|
|
cfg.Gateway.Quota.Groups = make(map[string]QuotaWindow)
|
|
}
|
|
|
|
// Telegram per-group quotas
|
|
for chatID, groupCfg := range cfg.Channels.Telegram.Groups {
|
|
if groupCfg != nil && groupCfg.Quota != nil && !groupCfg.Quota.IsZero() {
|
|
key := "group:telegram:" + chatID
|
|
cfg.Gateway.Quota.Groups[key] = *groupCfg.Quota
|
|
}
|
|
}
|
|
}
|