use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Config {
#[serde(rename = "$schema")]
pub schema: Option<String>,
pub meta: Option<MetaConfig>,
pub gateway: Option<GatewayConfig>,
pub update: Option<UpdateConfig>,
pub env: Option<EnvConfig>,
pub agents: Option<AgentsConfig>,
pub models: Option<ModelsConfig>,
pub auth: Option<AuthConfig>,
pub channels: Option<ChannelsConfig>,
pub session: Option<SessionConfig>,
pub bindings: Option<Vec<BindingConfig>>,
pub cron: Option<CronConfig>,
pub tools: Option<ToolsConfig>,
pub sandbox: Option<SandboxConfig>,
pub logging: Option<LoggingConfig>,
pub skills: Option<SkillsConfig>,
pub plugins: Option<PluginsConfig>,
pub hooks: Option<HooksConfig>,
pub secrets: Option<SecretsConfig>,
pub memory_search: Option<MemorySearchConfig>,
pub memory: Option<MemoryTopConfig>,
pub mcp: Option<McpConfig>,
pub wizard: Option<Value>,
pub messages: Option<MessagesConfig>,
pub commands: Option<CommandsConfig>,
pub diagnostics: Option<Value>,
pub cli: Option<CliConfig>,
pub browser: Option<BrowserConfig>,
pub ui: Option<UiConfig>,
pub acp: Option<Value>,
pub node_host: Option<Value>,
pub broadcast: Option<Value>,
pub audio: Option<Value>,
pub media: Option<Value>,
pub approvals: Option<ApprovalsConfig>,
pub discovery: Option<Value>,
pub canvas_host: Option<CanvasHostConfig>,
pub talk: Option<TalkConfig>,
pub web: Option<WebConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MetaConfig {
pub version: Option<String>,
pub name: Option<String>,
pub last_touched_version: Option<String>,
pub last_touched_at: Option<String>,
pub timestamp: Option<u64>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct GatewayConfig {
pub port: Option<u16>,
pub mode: Option<GatewayMode>,
pub bind: Option<BindMode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bind_address: Option<String>,
pub auth: Option<GatewayAuth>,
pub control_ui: Option<ControlUiConfig>,
pub reload: Option<ReloadMode>,
pub push: Option<PushConfig>,
pub channel_health_check_minutes: Option<u32>,
pub channel_stale_event_threshold_minutes: Option<u32>,
pub channel_max_restarts_per_hour: Option<u32>,
pub remote: Option<Value>,
pub tailscale: Option<Value>,
pub hot_reload: Option<Value>,
pub language: Option<String>,
pub processing_timeout: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user_agent: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proxy: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proxy_allow: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proxy_deny: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum GatewayMode {
Local,
Cloud,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum BindMode {
Auto,
Lan,
Loopback,
All,
Custom,
Tailnet,
}
impl BindMode {
pub fn from_config_str(s: &str) -> (Self, Option<String>) {
match s.to_lowercase().as_str() {
"auto" => (Self::Auto, None),
"lan" => (Self::Lan, None),
"loopback" => (Self::Loopback, None),
"all" => (Self::All, None),
"custom" => (Self::Custom, None),
"tailnet" => (Self::Tailnet, None),
_ => (Self::Custom, Some(s.to_owned())),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GatewayAuth {
pub mode: Option<String>,
pub token: Option<SecretOrString>,
pub password: Option<SecretOrString>,
pub allow_tailscale: Option<bool>,
pub allow_local: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ControlUiConfig {
pub enabled: Option<bool>,
pub path: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ReloadMode {
Hot,
Hybrid,
Restart,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PushConfig {
pub apns: Option<ApnsConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApnsConfig {
pub relay_url: Option<String>,
pub token: Option<SecretOrString>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateConfig {
pub channel: Option<String>,
pub auto: Option<bool>,
pub check: Option<bool>,
pub notify: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EnvConfig(pub HashMap<String, String>);
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalAgentConfig {
pub id: String,
pub url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth_token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub remote_agent_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct AgentsConfig {
pub defaults: Option<AgentDefaults>,
pub list: Option<Vec<AgentEntry>>,
pub external: Option<Vec<ExternalAgentConfig>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct AgentDefaults {
pub workspace: Option<String>,
pub model: Option<ModelConfig>,
pub flash_model: Option<ModelConfig>,
pub models: Option<HashMap<String, ModelAlias>>,
pub max_concurrent: Option<u32>,
pub context_pruning: Option<ContextPruningConfig>,
pub compaction: Option<CompactionConfig>,
pub heartbeat: Option<HeartbeatConfig>,
pub sandbox: Option<AgentSandboxConfig>,
pub image_max_dimension_px: Option<u32>,
pub group_chat: Option<GroupChatConfig>,
pub skip_bootstrap: Option<bool>,
pub block_streaming_default: Option<bool>,
pub timeout_seconds: Option<u32>,
pub prompt_mode: Option<PromptMode>,
pub memory: Option<MemoryConfig>,
pub subagents: Option<Value>,
pub bootstrap: Option<Value>,
pub image: Option<Value>,
pub pdf: Option<Value>,
pub image_gen: Option<Value>,
pub repo_root: Option<String>,
pub context_tokens: Option<u32>,
pub kv_cache_mode: Option<u8>,
pub btw_tokens: Option<u32>,
pub timezone: Option<String>,
pub timestamp: Option<Value>,
pub thinking: Option<ThinkingConfig>,
pub strip_think_tags: Option<bool>,
pub frequency_penalty: Option<f32>,
pub streaming: Option<StreamingMode>,
pub timeout: Option<Value>,
pub tools: Option<Value>,
pub subagent: Option<Value>,
pub cli: Option<Value>,
pub media: Option<Value>,
pub embedded: Option<Value>,
pub archive: Option<Value>,
pub max_iterations: Option<u32>,
pub intermediate_output: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentEntry {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<ModelConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flash_model: Option<ModelConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lane: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lane_concurrency: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group_chat: Option<GroupChatConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channels: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub commands: Option<Vec<AgentCommand>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allowed_commands: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub opencode: Option<OpenCodeConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub claudecode: Option<ClaudeCodeConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCodeConfig {
pub command: Option<String>,
pub args: Option<Vec<String>>,
pub cwd: Option<String>,
pub model: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClaudeCodeConfig {
pub command: Option<String>,
pub args: Option<Vec<String>>,
pub cwd: Option<String>,
pub model: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentCommand {
pub name: String,
pub description: String,
pub action: String,
pub args: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub primary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fallbacks: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_fallbacks: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub video: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking: Option<ThinkingConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub toolset: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flash: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ThinkingLevel {
Off,
Minimal,
Low,
Medium,
High,
Xhigh,
Adaptive,
}
impl ThinkingLevel {
pub fn budget_tokens(&self) -> u32 {
match self {
ThinkingLevel::Off => 0,
ThinkingLevel::Minimal => 1024,
ThinkingLevel::Low => 4096,
ThinkingLevel::Medium => 10240,
ThinkingLevel::High => 32768,
ThinkingLevel::Xhigh => 65536,
ThinkingLevel::Adaptive => 0, }
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ThinkingConfig {
pub enabled: Option<bool>,
pub level: Option<ThinkingLevel>,
pub budget_tokens: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelAlias {
pub model: Option<String>,
pub alias: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GroupChatConfig {
pub enabled: Option<bool>,
pub mention: Option<bool>,
pub prefix: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum PromptMode {
Full,
Minimal,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ContextPruningConfig {
pub mode: Option<PruningMode>,
pub ttl: Option<String>,
pub keep_last_assistants: Option<u32>,
pub min_prunable_tool_chars: Option<u32>,
pub soft_trim: Option<SoftTrimConfig>,
pub hard_clear: Option<HardClearConfig>,
pub tools: Option<PruningToolPolicy>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum PruningMode {
Off,
#[serde(rename = "cache-ttl")]
CacheTtl,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SoftTrimConfig {
pub enabled: Option<bool>,
pub head_chars: Option<u32>,
pub tail_chars: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HardClearConfig {
pub enabled: Option<bool>,
pub threshold: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PruningToolPolicy {
pub deny: Option<Vec<String>>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompactionConfig {
pub mode: Option<CompactionMode>,
pub reserve_tokens_floor: Option<u32>,
pub identifier_policy: Option<String>,
pub memory_flush: Option<MemoryFlushConfig>,
pub model: Option<String>,
pub keep_recent_pairs: Option<u32>,
pub extract_facts: Option<bool>,
pub max_transcript_tokens: Option<usize>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum CompactionMode {
Default,
Safeguard,
Layered,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MemoryFlushConfig {
pub enabled: Option<bool>,
pub system_prompt: Option<String>,
pub prompt: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HeartbeatConfig {
pub enabled: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentSandboxConfig {
pub enabled: Option<bool>,
pub scope: Option<SandboxScope>,
pub browser_force: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelsConfig {
pub mode: Option<ModelsMode>,
pub providers: HashMap<String, ProviderConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ModelsMode {
Merge,
Replace,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProviderConfig {
pub base_url: Option<String>,
pub api_key: Option<SecretOrString>,
pub api: Option<ApiFormat>,
pub models: Option<Vec<ModelDef>>,
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user_agent: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ApiFormat {
#[serde(rename = "openai-responses")]
OpenAiResponses,
#[serde(rename = "openai-completions", alias = "openai")]
OpenAiCompletions,
Anthropic,
#[serde(rename = "anthropic-messages")]
AnthropicMessages,
Gemini,
Ollama,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelDef {
pub id: String,
pub name: Option<String>,
pub reasoning: Option<bool>,
pub input: Option<Vec<InputType>>,
pub cost: Option<CostConfig>,
pub context_window: Option<u64>,
pub max_tokens: Option<u64>,
pub enabled: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum InputType {
Text,
Image,
Audio,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CostConfig {
pub input: Option<f64>,
pub output: Option<f64>,
pub unit: Option<u64>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthConfig {
pub profiles: Option<HashMap<String, AuthProfile>>,
pub order: Option<HashMap<String, Vec<String>>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "mode", rename_all = "camelCase")]
pub enum AuthProfile {
#[serde(rename = "oauth")]
OAuth { email: Option<String> },
#[serde(rename = "api_key")]
ApiKey {
#[serde(rename = "apiKey")]
api_key: Option<SecretOrString>,
provider: Option<String>,
},
#[serde(rename = "token")]
Token {
token: Option<SecretOrString>,
provider: Option<String>,
},
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ChannelsConfig {
pub telegram: Option<TelegramConfig>,
pub discord: Option<DiscordConfig>,
pub slack: Option<SlackConfig>,
pub whatsapp: Option<WhatsAppConfig>,
pub signal: Option<SignalConfig>,
pub imessage: Option<IMessageConfig>,
pub mattermost: Option<MattermostConfig>,
pub msteams: Option<MSTeamsConfig>,
pub googlechat: Option<GoogleChatConfig>,
pub feishu: Option<FeishuConfig>,
pub dingtalk: Option<DingTalkConfig>,
pub wecom: Option<WeComConfig>,
pub wechat: Option<WeChatPersonalConfig>,
pub qq: Option<QQBotConfig>,
pub line: Option<LineConfig>,
pub zalo: Option<ZaloConfig>,
pub matrix: Option<MatrixConfig>,
pub custom: Option<Vec<CustomChannelConfig>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CustomChannelConfig {
pub name: String,
#[serde(rename = "type")]
pub channel_type: String,
#[serde(flatten)]
pub base: ChannelBase,
pub ws_url: Option<String>,
pub ws_headers: Option<HashMap<String, String>>,
pub auth_frame: Option<String>,
pub auth_success_path: Option<String>,
pub auth_success_value: Option<String>,
pub heartbeat_interval: Option<u64>,
pub heartbeat_frame: Option<String>,
pub filter_path: Option<String>,
pub filter_value: Option<String>,
pub text_path: Option<String>,
pub sender_path: Option<String>,
pub group_path: Option<String>,
pub reply_url: Option<String>,
pub reply_method: Option<String>,
pub reply_template: Option<String>,
pub reply_headers: Option<HashMap<String, String>>,
pub reply_frame: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LineConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub channel_access_token: Option<SecretOrString>,
pub channel_secret: Option<SecretOrString>,
pub api_base: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZaloConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub access_token: Option<SecretOrString>,
pub oa_secret: Option<SecretOrString>,
pub api_base: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MatrixConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub homeserver: Option<String>,
pub access_token: Option<SecretOrString>,
pub user_id: Option<String>,
pub device_id: Option<String>,
pub recovery_key: Option<SecretOrString>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct QQBotConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub app_id: Option<String>,
pub app_secret: Option<SecretOrString>,
pub sandbox: Option<bool>,
pub intents: Option<u32>,
pub api_base: Option<String>,
pub token_url: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WeChatPersonalConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub bot_token: Option<SecretOrString>,
pub base_url: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ChannelBase {
pub enabled: Option<bool>,
pub dm_policy: Option<DmPolicy>,
pub allow_from: Option<Vec<String>>,
pub group_policy: Option<GroupPolicy>,
pub group_allow_from: Option<Vec<String>>,
pub health_monitor: Option<HealthMonitorConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum DmPolicy {
Pairing,
Allowlist,
Open,
Disabled,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum GroupPolicy {
Allowlist,
Open,
Disabled,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HealthMonitorConfig {
pub enabled: Option<bool>,
pub check_interval_min: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TelegramConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub bot_token: Option<SecretOrString>,
pub token_file: Option<String>,
pub history_limit: Option<u32>,
pub reply_to_mode: Option<ReplyToMode>,
pub link_preview: Option<bool>,
pub streaming: Option<StreamingMode>,
pub text_chunk_limit: Option<usize>,
pub media_max_mb: Option<u32>,
pub actions: Option<Value>,
pub reaction_notifications: Option<ReactionNotif>,
pub custom_commands: Option<Vec<BotCommand>>,
pub groups: Option<HashMap<String, Value>>,
pub proxy: Option<String>,
pub webhook_url: Option<String>,
pub webhook_secret: Option<SecretOrString>,
pub webhook_path: Option<String>,
pub network: Option<Value>,
pub retry: Option<RetryConfig>,
pub config_writes: Option<bool>,
pub default_account: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
pub api_base: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiscordConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub token: Option<SecretOrString>,
pub media_max_mb: Option<u32>,
pub allow_bots: Option<bool>,
pub streaming: Option<StreamingMode>,
pub reply_to_mode: Option<ReplyToMode>,
pub max_lines_per_message: Option<u32>,
pub actions: Option<Value>,
pub reaction_notifications: Option<ReactionNotif>,
pub dm: Option<Value>,
pub guilds: Option<HashMap<String, Value>>,
pub accounts: Option<HashMap<String, Value>>,
pub retry: Option<RetryConfig>,
pub gateway_url: Option<String>,
pub api_base: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SlackConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub bot_token: Option<SecretOrString>,
pub app_token: Option<SecretOrString>,
pub api_base: Option<String>,
pub streaming: Option<StreamingMode>,
pub native_streaming: Option<bool>,
pub text_chunk_limit: Option<usize>,
pub media_max_mb: Option<u32>,
pub workspaces: Option<HashMap<String, Value>>,
pub retry: Option<RetryConfig>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WhatsAppConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub text_chunk_limit: Option<usize>,
pub chunk_mode: Option<ChunkMode>,
pub media_max_mb: Option<u32>,
pub send_read_receipts: Option<bool>,
pub groups: Option<HashMap<String, Value>>,
pub default_account: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
pub retry: Option<RetryConfig>,
pub api_base: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignalConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub phone: Option<String>,
pub text_chunk_limit: Option<usize>,
pub retry: Option<RetryConfig>,
pub cli_path: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IMessageConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub server_url: Option<String>,
pub api_key: Option<SecretOrString>,
pub retry: Option<RetryConfig>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MattermostConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub server_url: Option<String>,
pub bot_token: Option<SecretOrString>,
pub retry: Option<RetryConfig>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MSTeamsConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub tenant_id: Option<String>,
pub client_id: Option<String>,
pub client_secret: Option<SecretOrString>,
pub retry: Option<RetryConfig>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GoogleChatConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub service_account_key_file: Option<String>,
pub retry: Option<RetryConfig>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FeishuConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub app_id: Option<String>,
pub app_secret: Option<SecretOrString>,
pub verification_token: Option<SecretOrString>,
pub encrypt_key: Option<SecretOrString>,
pub streaming: Option<StreamingMode>,
pub brand: Option<String>,
pub api_base: Option<String>,
pub ws_url: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DingTalkConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub app_key: Option<String>,
pub app_secret: Option<SecretOrString>,
pub robot_code: Option<String>,
pub streaming: Option<StreamingMode>,
pub api_base: Option<String>,
pub oapi_base: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WeComConfig {
#[serde(flatten)]
pub base: ChannelBase,
pub bot_id: Option<String>,
pub secret: Option<SecretOrString>,
pub token: Option<SecretOrString>,
pub encoding_aes_key: Option<SecretOrString>,
pub streaming: Option<StreamingMode>,
#[serde(alias = "wsUrl")]
pub ws_url: Option<String>,
pub accounts: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ReplyToMode {
Off,
First,
All,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum StreamingMode {
Off,
Partial,
Block,
Progress,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ReactionNotif {
Off,
Own,
All,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ChunkMode {
Length,
Newline,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BotCommand {
pub command: String,
pub description: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RetryConfig {
pub attempts: Option<u32>,
pub min_delay_ms: Option<u64>,
pub max_delay_ms: Option<u64>,
pub jitter: Option<f64>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionConfig {
pub dm_scope: Option<DmScope>,
pub thread_bindings: Option<Value>,
pub reset: Option<SessionResetConfig>,
pub identity_links: Option<HashMap<String, Vec<String>>>,
pub maintenance: Option<SessionMaintenanceConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum DmScope {
Main,
#[serde(rename = "per-peer")]
PerPeer,
#[serde(rename = "per-channel-peer")]
PerChannelPeer,
#[serde(rename = "per-account-channel-peer")]
PerAccountChannelPeer,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionResetConfig {
pub command: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionMaintenanceConfig {
pub prune_after: Option<String>,
pub max_entries: Option<u32>,
pub max_disk_bytes: Option<u64>,
pub rotate_bytes: Option<u64>,
pub reset_archive_retention: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BindingConfig {
#[serde(rename = "type")]
pub kind: Option<String>,
pub agent_id: String,
#[serde(rename = "match")]
pub match_: BindingMatch,
pub priority: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BindingMatch {
pub channel: Option<String>,
pub peer_id: Option<String>,
pub group_id: Option<String>,
pub account_id: Option<String>,
pub path: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CronConfig {
pub enabled: Option<bool>,
pub max_concurrent_runs: Option<u32>,
pub session_retention: Option<Value>,
pub run_log: Option<RunLogConfig>,
pub jobs: Option<Vec<CronJobConfig>>,
pub default_delivery: Option<CronDelivery>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RunLogConfig {
pub enabled: Option<bool>,
pub max_runs: Option<u32>,
pub retention: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct CronDelivery {
#[serde(default)]
pub mode: Option<String>,
pub channel: Option<String>,
#[serde(rename = "to")]
pub to: Option<String>,
pub thread_id: Option<String>,
pub account_id: Option<String>,
#[serde(default)]
pub best_effort: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CronJobConfig {
pub id: String,
#[serde(default)]
pub name: Option<String>,
pub schedule: String,
#[serde(default)]
pub tz: Option<String>,
pub agent_id: Option<String>,
pub message: String,
pub session: Option<Value>,
pub enabled: Option<bool>,
pub delivery: Option<CronDelivery>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolsConfig {
pub loop_detection: Option<LoopDetectionConfig>,
pub deny: Option<Vec<String>>,
pub allow: Option<Vec<String>>,
pub exec: Option<ExecToolConfig>,
pub web_search: Option<WebSearchConfig>,
pub web_fetch: Option<WebFetchConfig>,
pub web_browser: Option<WebBrowserConfig>,
pub computer_use: Option<ComputerUseConfig>,
pub upload: Option<UploadConfig>,
pub session_result_limits: Option<SessionResultLimits>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionResultLimits {
pub web_search: Option<usize>,
pub web_fetch: Option<usize>,
pub web_browser: Option<usize>,
pub exec: Option<usize>,
pub default: Option<usize>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UploadConfig {
pub max_file_size: Option<usize>,
pub max_text_chars: Option<usize>,
pub supports_vision: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExecToolConfig {
pub safety: Option<bool>,
pub timeout_seconds: Option<u64>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebSearchConfig {
pub provider: Option<String>,
pub brave_api_key: Option<SecretOrString>,
pub google_api_key: Option<SecretOrString>,
pub google_cx: Option<String>,
pub bing_api_key: Option<SecretOrString>,
pub serper_api_key: Option<SecretOrString>,
pub max_results: Option<usize>,
pub enabled: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebFetchConfig {
pub enabled: Option<bool>,
pub max_length: Option<usize>,
pub user_agent: Option<String>,
pub summary_model: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebBrowserConfig {
pub enabled: Option<bool>,
pub chrome_path: Option<String>,
pub headed: Option<bool>,
pub profile: Option<String>,
pub remote_debug_ports: Option<Vec<u16>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ComputerUseConfig {
pub enabled: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoopDetectionConfig {
pub enabled: Option<bool>,
pub window: Option<usize>,
pub threshold: Option<usize>,
pub overrides: Option<std::collections::HashMap<String, usize>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SandboxConfig {
pub mode: Option<SandboxMode>,
pub scope: Option<SandboxScope>,
pub docker: Option<DockerConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SandboxMode {
Off,
#[serde(rename = "non-main")]
NonMain,
All,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SandboxScope {
Session,
Agent,
Shared,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DockerConfig {
pub image: Option<String>,
pub network: Option<String>,
pub mounts: Option<Vec<String>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoggingConfig {
pub level: Option<String>,
pub format: Option<LogFormat>,
pub file: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum LogFormat {
Pretty,
Json,
Compact,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillsConfig {
pub install: Option<SkillInstallConfig>,
pub entries: Option<HashMap<String, SkillEntryConfig>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillInstallConfig {
pub directory: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillEntryConfig {
pub enabled: Option<bool>,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PluginsConfig {
pub entries: Option<HashMap<String, PluginEntryConfig>>,
pub slots: Option<PluginSlots>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PluginEntryConfig {
pub enabled: Option<bool>,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PluginSlots {
pub memory: Option<String>,
pub context_engine: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HooksConfig {
pub enabled: bool,
pub token: Option<SecretOrString>,
pub path: Option<String>,
pub default_session_key: Option<String>,
pub allow_request_session_key: Option<bool>,
pub allowed_session_key_prefixes: Option<Vec<String>>,
pub mappings: Option<Vec<HookMapping>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HookMapping {
#[serde(rename = "match")]
pub match_: HookMatch,
pub action: HookAction,
pub agent_id: Option<String>,
pub session_key: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HookMatch {
pub path: Option<String>,
pub method: Option<String>,
pub headers: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum HookAction {
Agent,
Script,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SecretsConfig {
pub providers: HashMap<String, SecretProviderConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SecretProviderConfig {
#[serde(rename = "type")]
pub kind: SecretProviderKind,
pub command: Option<String>,
pub args: Option<Vec<String>>,
pub file: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SecretProviderKind {
Env,
File,
Exec,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MemoryConfig {
pub enabled: Option<bool>,
pub backend: Option<MemoryBackend>,
pub auto_capture: Option<bool>,
pub auto_recall: Option<bool>,
pub retrieval: Option<Value>,
pub enable_management_tools: Option<bool>,
pub scope: Option<ScopeConfig>,
pub recall_top_k: Option<usize>,
pub recall_final_k: Option<usize>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum MemoryBackend {
LanceDb,
None,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScopeConfig {
pub default: Option<String>,
pub definitions: Option<HashMap<String, Value>>,
pub agent_access: Option<HashMap<String, Vec<String>>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum SecretOrString {
Plain(String),
Ref(SecretRef),
}
impl SecretOrString {
pub fn as_plain(&self) -> Option<&str> {
match self {
SecretOrString::Plain(s) => Some(s.as_str()),
SecretOrString::Ref(_) => None,
}
}
pub fn resolve_early(&self) -> Option<String> {
match self {
SecretOrString::Plain(s) => {
let expanded = crate::config::loader::expand_env_vars(s);
Some(expanded)
}
SecretOrString::Ref(r) if r.source == SecretSource::Env => std::env::var(&r.id).ok(),
SecretOrString::Ref(_) => None,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SecretRef {
pub source: SecretSource,
pub provider: Option<String>,
pub id: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SecretSource {
Env,
File,
Exec,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MemorySearchConfig {
pub provider: Option<String>,
pub model: Option<String>,
pub sources: Option<Vec<String>>,
pub base_url: Option<String>,
pub api_key: Option<SecretOrString>,
pub local: Option<LocalEmbeddingConfig>,
pub experimental: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalEmbeddingConfig {
pub model_path: Option<String>,
pub model_download_url: Option<String>,
pub model_repo: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MemoryTopConfig {
pub enabled: Option<bool>,
pub provider: Option<String>,
pub search: Option<MemoryTopSearchConfig>,
pub recall_top_k: Option<usize>,
pub recall_final_k: Option<usize>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MemoryTopSearchConfig {
pub model: Option<String>,
pub max_results: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct McpConfig {
pub enabled: Option<bool>,
pub servers: Option<Vec<McpServerConfig>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct McpServerConfig {
pub name: String,
pub command: String,
pub args: Option<Vec<String>>,
pub env: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MessagesConfig {
pub prefix: Option<String>,
pub ack_reaction: Option<String>,
pub ack_reaction_scope: Option<String>,
pub inbound_debounce: Option<DebounceConfig>,
pub compaction: Option<MsgCompactionConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DebounceConfig {
pub enabled: Option<bool>,
pub window: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MsgCompactionConfig {
pub enabled: Option<bool>,
pub token_threshold: Option<u32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CommandsConfig {
pub enabled: Option<bool>,
pub native: Option<String>,
pub native_skills: Option<String>,
pub restart: Option<bool>,
pub owner_display: Option<String>,
pub prefix: Option<String>,
pub list: Option<Vec<Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CliConfig {
pub banner: Option<bool>,
pub backend: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserConfig {
pub enable_cdp: Option<bool>,
pub headless: Option<bool>,
pub user_agent: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UiConfig {
pub theme: Option<String>,
pub assistant_name: Option<String>,
pub assistant_emoji: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApprovalsConfig {
pub exec: Option<ExecApprovalConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExecApprovalConfig {
pub enabled: Option<bool>,
pub mode: Option<String>,
pub agent_filter: Option<Vec<String>>,
pub session_filter: Option<Vec<String>>,
pub targets: Option<Vec<Value>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CanvasHostConfig {
pub enabled: Option<bool>,
pub port: Option<u16>,
pub bind: Option<String>,
pub root: Option<String>,
pub live_reload: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TalkConfig {
pub enabled: Option<bool>,
pub provider: Option<String>,
pub voice: Option<String>,
pub speed: Option<f64>,
pub instructions: Option<String>,
pub api_key: Option<SecretOrString>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebConfig {
pub port: Option<u16>,
pub bind: Option<String>,
pub tls_enabled: Option<bool>,
}