mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
1033 lines
51 KiB
Go
1033 lines
51 KiB
Go
// PicoClaw - Ultra-lightweight personal AI agent
|
|
// License: MIT
|
|
//
|
|
// Copyright (c) 2026 PicoClaw contributors
|
|
|
|
package config
|
|
|
|
import "encoding/json"
|
|
|
|
type agentDefaultsV0 struct {
|
|
Workspace string `json:"workspace" env:"PICOCLAW_AGENTS_DEFAULTS_WORKSPACE"`
|
|
RestrictToWorkspace bool `json:"restrict_to_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_RESTRICT_TO_WORKSPACE"`
|
|
AllowReadOutsideWorkspace bool `json:"allow_read_outside_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_ALLOW_READ_OUTSIDE_WORKSPACE"`
|
|
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
|
|
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL_NAME"`
|
|
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"` // Deprecated: use model_name instead
|
|
ModelFallbacks []string `json:"model_fallbacks,omitempty"`
|
|
ImageModel string `json:"image_model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_IMAGE_MODEL"`
|
|
ImageModelFallbacks []string `json:"image_model_fallbacks,omitempty"`
|
|
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
|
|
Temperature *float64 `json:"temperature,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"`
|
|
MaxToolIterations int `json:"max_tool_iterations" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"`
|
|
SummarizeMessageThreshold int `json:"summarize_message_threshold" env:"PICOCLAW_AGENTS_DEFAULTS_SUMMARIZE_MESSAGE_THRESHOLD"`
|
|
SummarizeTokenPercent int `json:"summarize_token_percent" env:"PICOCLAW_AGENTS_DEFAULTS_SUMMARIZE_TOKEN_PERCENT"`
|
|
MaxMediaSize int `json:"max_media_size,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_MEDIA_SIZE"`
|
|
Routing *RoutingConfig `json:"routing,omitempty"`
|
|
}
|
|
|
|
// GetModelName returns the effective model name for the agent defaults.
|
|
// It prefers the new "model_name" field but falls back to "model" for backward compatibility.
|
|
func (d *agentDefaultsV0) GetModelName() string {
|
|
if d.ModelName != "" {
|
|
return d.ModelName
|
|
}
|
|
return d.Model
|
|
}
|
|
|
|
type agentsConfigV0 struct {
|
|
Defaults agentDefaultsV0 `json:"defaults"`
|
|
List []AgentConfig `json:"list,omitempty"`
|
|
}
|
|
|
|
// configV0 represents the config structure before versioning was introduced.
|
|
// This struct is used for loading legacy config files (version 0).
|
|
// It is unexported since it's only used internally for migration.
|
|
type configV0 struct {
|
|
Agents agentsConfigV0 `json:"agents"`
|
|
Bindings []AgentBinding `json:"bindings,omitempty"`
|
|
Session SessionConfig `json:"session,omitempty"`
|
|
Channels channelsConfigV0 `json:"channels"`
|
|
Providers providersConfigV0 `json:"providers,omitempty"`
|
|
ModelList []modelConfigV0 `json:"model_list"`
|
|
Gateway GatewayConfig `json:"gateway"`
|
|
Tools toolsConfigV0 `json:"tools"`
|
|
Heartbeat HeartbeatConfig `json:"heartbeat"`
|
|
Devices DevicesConfig `json:"devices"`
|
|
}
|
|
|
|
type toolsConfigV0 struct {
|
|
AllowReadPaths []string `json:"allow_read_paths" env:"PICOCLAW_TOOLS_ALLOW_READ_PATHS"`
|
|
AllowWritePaths []string `json:"allow_write_paths" env:"PICOCLAW_TOOLS_ALLOW_WRITE_PATHS"`
|
|
Web webToolsConfigV0 `json:"web"`
|
|
Cron CronToolsConfig `json:"cron"`
|
|
Exec ExecConfig `json:"exec"`
|
|
Skills skillsToolsConfigV0 `json:"skills"`
|
|
MediaCleanup MediaCleanupConfig `json:"media_cleanup"`
|
|
MCP MCPConfig `json:"mcp"`
|
|
AppendFile ToolConfig `json:"append_file" envPrefix:"PICOCLAW_TOOLS_APPEND_FILE_"`
|
|
EditFile ToolConfig `json:"edit_file" envPrefix:"PICOCLAW_TOOLS_EDIT_FILE_"`
|
|
FindSkills ToolConfig `json:"find_skills" envPrefix:"PICOCLAW_TOOLS_FIND_SKILLS_"`
|
|
I2C ToolConfig `json:"i2c" envPrefix:"PICOCLAW_TOOLS_I2C_"`
|
|
InstallSkill ToolConfig `json:"install_skill" envPrefix:"PICOCLAW_TOOLS_INSTALL_SKILL_"`
|
|
ListDir ToolConfig `json:"list_dir" envPrefix:"PICOCLAW_TOOLS_LIST_DIR_"`
|
|
Message ToolConfig `json:"message" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"`
|
|
ReadFile ReadFileToolConfig `json:"read_file" envPrefix:"PICOCLAW_TOOLS_READ_FILE_"`
|
|
SendFile ToolConfig `json:"send_file" envPrefix:"PICOCLAW_TOOLS_SEND_FILE_"`
|
|
Spawn ToolConfig `json:"spawn" envPrefix:"PICOCLAW_TOOLS_SPAWN_"`
|
|
SpawnStatus ToolConfig `json:"spawn_status" envPrefix:"PICOCLAW_TOOLS_SPAWN_STATUS_"`
|
|
SPI ToolConfig `json:"spi" envPrefix:"PICOCLAW_TOOLS_SPI_"`
|
|
Subagent ToolConfig `json:"subagent" envPrefix:"PICOCLAW_TOOLS_SUBAGENT_"`
|
|
WebFetch ToolConfig `json:"web_fetch" envPrefix:"PICOCLAW_TOOLS_WEB_FETCH_"`
|
|
WriteFile ToolConfig `json:"write_file" envPrefix:"PICOCLAW_TOOLS_WRITE_FILE_"`
|
|
}
|
|
|
|
type channelsConfigV0 struct {
|
|
WhatsApp WhatsAppConfig `json:"whatsapp"`
|
|
Telegram telegramConfigV0 `json:"telegram"`
|
|
Feishu feishuConfigV0 `json:"feishu"`
|
|
Discord discordConfigV0 `json:"discord"`
|
|
MaixCam maixcamConfigV0 `json:"maixcam"`
|
|
Weixin weixinConfigV0 `json:"weixin"`
|
|
QQ qqConfigV0 `json:"qq"`
|
|
DingTalk dingtalkConfigV0 `json:"dingtalk"`
|
|
Slack slackConfigV0 `json:"slack"`
|
|
Matrix matrixConfigV0 `json:"matrix"`
|
|
LINE lineConfigV0 `json:"line"`
|
|
OneBot onebotConfigV0 `json:"onebot"`
|
|
WeCom wecomConfigV0 `json:"wecom"`
|
|
WeComApp wecomappConfigV0 `json:"wecom_app"`
|
|
WeComAIBot wecomaibotConfigV0 `json:"wecom_aibot"`
|
|
Pico picoConfigV0 `json:"pico"`
|
|
IRC ircConfigV0 `json:"irc"`
|
|
}
|
|
|
|
func (v *channelsConfigV0) ToChannelsConfig() (ChannelsConfig, ChannelsSecurity) {
|
|
telegram, telegramSecurity := v.Telegram.ToTelegramConfig()
|
|
feishu, feishuSecurity := v.Feishu.ToFeishuConfig()
|
|
discord, discordSecurity := v.Discord.ToDiscordConfig()
|
|
maixcam := v.MaixCam.ToMaixCamConfig()
|
|
qq, qqSecurity := v.QQ.ToQQConfig()
|
|
weixin, weixinSecurity := v.Weixin.ToWeiXinConfig()
|
|
dingtalk, dingtalkSecurity := v.DingTalk.ToDingTalkConfig()
|
|
slack, slackSecurity := v.Slack.ToSlackConfig()
|
|
matrix, matrixSecurity := v.Matrix.ToMatrixConfig()
|
|
line, lineSecurity := v.LINE.ToLINEConfig()
|
|
onebot, onebotSecurity := v.OneBot.ToOneBotConfig()
|
|
wecom, wecomSecurity := v.WeCom.ToWeComConfig()
|
|
wecomapp, wecomappSecurity := v.WeComApp.ToWeComAppConfig()
|
|
wecomaibot, wecomaibotSecurity := v.WeComAIBot.ToWeComAIBotConfig()
|
|
pico, picoSecurity := v.Pico.ToPicoConfig()
|
|
irc, ircSecurity := v.IRC.ToIRCConfig()
|
|
|
|
return ChannelsConfig{
|
|
WhatsApp: v.WhatsApp,
|
|
Telegram: telegram,
|
|
Feishu: feishu,
|
|
Discord: discord,
|
|
MaixCam: maixcam,
|
|
QQ: qq,
|
|
Weixin: weixin,
|
|
DingTalk: dingtalk,
|
|
Slack: slack,
|
|
Matrix: matrix,
|
|
LINE: line,
|
|
OneBot: onebot,
|
|
WeCom: wecom,
|
|
WeComApp: wecomapp,
|
|
WeComAIBot: wecomaibot,
|
|
Pico: pico,
|
|
IRC: irc,
|
|
}, ChannelsSecurity{
|
|
Telegram: &telegramSecurity,
|
|
Feishu: &feishuSecurity,
|
|
Discord: &discordSecurity,
|
|
QQ: &qqSecurity,
|
|
Weixin: &weixinSecurity,
|
|
DingTalk: &dingtalkSecurity,
|
|
Slack: &slackSecurity,
|
|
Matrix: &matrixSecurity,
|
|
LINE: &lineSecurity,
|
|
OneBot: &onebotSecurity,
|
|
WeCom: &wecomSecurity,
|
|
WeComApp: &wecomappSecurity,
|
|
WeComAIBot: &wecomaibotSecurity,
|
|
Pico: &picoSecurity,
|
|
IRC: &ircSecurity,
|
|
}
|
|
}
|
|
|
|
type qqConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_QQ_ENABLED"`
|
|
AppID string `json:"app_id" env:"PICOCLAW_CHANNELS_QQ_APP_ID"`
|
|
AppSecret string `json:"app_secret" env:"PICOCLAW_CHANNELS_QQ_APP_SECRET"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_QQ_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
MaxMessageLength int `json:"max_message_length" env:"PICOCLAW_CHANNELS_QQ_MAX_MESSAGE_LENGTH"`
|
|
MaxBase64FileSizeMiB int64 `json:"max_base64_file_size_mib" env:"PICOCLAW_CHANNELS_QQ_MAX_BASE64_FILE_SIZE_MIB"`
|
|
SendMarkdown bool `json:"send_markdown" env:"PICOCLAW_CHANNELS_QQ_SEND_MARKDOWN"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_QQ_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *qqConfigV0) ToQQConfig() (QQConfig, QQSecurity) {
|
|
return QQConfig{
|
|
Enabled: v.Enabled,
|
|
AppID: v.AppID,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
MaxMessageLength: v.MaxMessageLength,
|
|
MaxBase64FileSizeMiB: v.MaxBase64FileSizeMiB,
|
|
SendMarkdown: v.SendMarkdown,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, QQSecurity{
|
|
AppSecret: v.AppSecret,
|
|
}
|
|
}
|
|
|
|
type telegramConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_TELEGRAM_ENABLED"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_TELEGRAM_TOKEN"`
|
|
BaseURL string `json:"base_url" env:"PICOCLAW_CHANNELS_TELEGRAM_BASE_URL"`
|
|
Proxy string `json:"proxy" env:"PICOCLAW_CHANNELS_TELEGRAM_PROXY"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Typing TypingConfig `json:"typing,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_TELEGRAM_REASONING_CHANNEL_ID"`
|
|
UseMarkdownV2 bool `json:"use_markdown_v2" env:"PICOCLAW_CHANNELS_TELEGRAM_USE_MARKDOWN_V2"`
|
|
}
|
|
|
|
func (v *telegramConfigV0) ToTelegramConfig() (TelegramConfig, TelegramSecurity) {
|
|
return TelegramConfig{
|
|
Enabled: v.Enabled,
|
|
token: v.Token,
|
|
BaseURL: v.BaseURL,
|
|
Proxy: v.Proxy,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Typing: v.Typing,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
UseMarkdownV2: v.UseMarkdownV2,
|
|
}, TelegramSecurity{
|
|
Token: v.Token,
|
|
}
|
|
}
|
|
|
|
type feishuConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_FEISHU_ENABLED"`
|
|
AppID string `json:"app_id" env:"PICOCLAW_CHANNELS_FEISHU_APP_ID"`
|
|
AppSecret string `json:"app_secret" env:"PICOCLAW_CHANNELS_FEISHU_APP_SECRET"`
|
|
EncryptKey string `json:"encrypt_key" env:"PICOCLAW_CHANNELS_FEISHU_ENCRYPT_KEY"`
|
|
VerificationToken string `json:"verification_token" env:"PICOCLAW_CHANNELS_FEISHU_VERIFICATION_TOKEN"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_FEISHU_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_FEISHU_REASONING_CHANNEL_ID"`
|
|
RandomReactionEmoji FlexibleStringSlice `json:"random_reaction_emoji" env:"PICOCLAW_CHANNELS_FEISHU_RANDOM_REACTION_EMOJI"`
|
|
IsLark bool `json:"is_lark" env:"PICOCLAW_CHANNELS_FEISHU_IS_LARK"`
|
|
}
|
|
|
|
func (v *feishuConfigV0) ToFeishuConfig() (FeishuConfig, FeishuSecurity) {
|
|
return FeishuConfig{
|
|
Enabled: v.Enabled,
|
|
AppID: v.AppID,
|
|
appSecret: v.AppSecret,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, FeishuSecurity{
|
|
AppSecret: v.AppSecret,
|
|
EncryptKey: v.EncryptKey,
|
|
VerificationToken: v.VerificationToken,
|
|
}
|
|
}
|
|
|
|
type discordConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_DISCORD_ENABLED"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_DISCORD_TOKEN"`
|
|
Proxy string `json:"proxy" env:"PICOCLAW_CHANNELS_DISCORD_PROXY"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_DISCORD_ALLOW_FROM"`
|
|
MentionOnly bool `json:"mention_only" env:"PICOCLAW_CHANNELS_DISCORD_MENTION_ONLY"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Typing TypingConfig `json:"typing,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_DISCORD_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *discordConfigV0) ToDiscordConfig() (DiscordConfig, DiscordSecurity) {
|
|
return DiscordConfig{
|
|
Enabled: v.Enabled,
|
|
token: v.Token,
|
|
Proxy: v.Proxy,
|
|
AllowFrom: v.AllowFrom,
|
|
MentionOnly: v.MentionOnly,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Typing: v.Typing,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, DiscordSecurity{
|
|
Token: v.Token,
|
|
}
|
|
}
|
|
|
|
type maixcamConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_MAIXCAM_ENABLED"`
|
|
Host string `json:"host" env:"PICOCLAW_CHANNELS_MAIXCAM_HOST"`
|
|
Port int `json:"port" env:"PICOCLAW_CHANNELS_MAIXCAM_PORT"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_MAIXCAM_ALLOW_FROM"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_MAIXCAM_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *maixcamConfigV0) ToMaixCamConfig() MaixCamConfig {
|
|
return MaixCamConfig{
|
|
Enabled: v.Enabled,
|
|
Host: v.Host,
|
|
Port: v.Port,
|
|
AllowFrom: v.AllowFrom,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}
|
|
}
|
|
|
|
type dingtalkConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_DINGTALK_ENABLED"`
|
|
ClientID string `json:"client_id" env:"PICOCLAW_CHANNELS_DINGTALK_CLIENT_ID"`
|
|
ClientSecret string `json:"client_secret" env:"PICOCLAW_CHANNELS_DINGTALK_CLIENT_SECRET"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_DINGTALK_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_DINGTALK_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *dingtalkConfigV0) ToDingTalkConfig() (DingTalkConfig, DingTalkSecurity) {
|
|
return DingTalkConfig{
|
|
Enabled: v.Enabled,
|
|
ClientID: v.ClientID,
|
|
clientSecret: v.ClientSecret,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, DingTalkSecurity{
|
|
ClientSecret: v.ClientSecret,
|
|
}
|
|
}
|
|
|
|
type slackConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_SLACK_ENABLED"`
|
|
BotToken string `json:"bot_token" env:"PICOCLAW_CHANNELS_SLACK_BOT_TOKEN"`
|
|
AppToken string `json:"app_token" env:"PICOCLAW_CHANNELS_SLACK_APP_TOKEN"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_SLACK_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Typing TypingConfig `json:"typing,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_SLACK_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *slackConfigV0) ToSlackConfig() (SlackConfig, SlackSecurity) {
|
|
return SlackConfig{
|
|
Enabled: v.Enabled,
|
|
botToken: v.BotToken,
|
|
appToken: v.AppToken,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Typing: v.Typing,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, SlackSecurity{
|
|
BotToken: v.BotToken,
|
|
AppToken: v.AppToken,
|
|
}
|
|
}
|
|
|
|
type matrixConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_MATRIX_ENABLED"`
|
|
Homeserver string `json:"homeserver" env:"PICOCLAW_CHANNELS_MATRIX_HOMESERVER"`
|
|
UserID string `json:"user_id" env:"PICOCLAW_CHANNELS_MATRIX_USER_ID"`
|
|
AccessToken string `json:"access_token" env:"PICOCLAW_CHANNELS_MATRIX_ACCESS_TOKEN"`
|
|
DeviceID string `json:"device_id,omitempty" env:"PICOCLAW_CHANNELS_MATRIX_DEVICE_ID"`
|
|
JoinOnInvite bool `json:"join_on_invite" env:"PICOCLAW_CHANNELS_MATRIX_JOIN_ON_INVITE"`
|
|
MessageFormat string `json:"message_format,omitempty" env:"PICOCLAW_CHANNELS_MATRIX_MESSAGE_FORMAT"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_MATRIX_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_MATRIX_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *matrixConfigV0) ToMatrixConfig() (MatrixConfig, MatrixSecurity) {
|
|
return MatrixConfig{
|
|
Enabled: v.Enabled,
|
|
Homeserver: v.Homeserver,
|
|
UserID: v.UserID,
|
|
accessToken: v.AccessToken,
|
|
DeviceID: v.DeviceID,
|
|
JoinOnInvite: v.JoinOnInvite,
|
|
MessageFormat: v.MessageFormat,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, MatrixSecurity{
|
|
AccessToken: v.AccessToken,
|
|
}
|
|
}
|
|
|
|
type lineConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_LINE_ENABLED"`
|
|
ChannelSecret string `json:"channel_secret" env:"PICOCLAW_CHANNELS_LINE_CHANNEL_SECRET"`
|
|
ChannelAccessToken string `json:"channel_access_token" env:"PICOCLAW_CHANNELS_LINE_CHANNEL_ACCESS_TOKEN"`
|
|
WebhookHost string `json:"webhook_host" env:"PICOCLAW_CHANNELS_LINE_WEBHOOK_HOST"`
|
|
WebhookPort int `json:"webhook_port" env:"PICOCLAW_CHANNELS_LINE_WEBHOOK_PORT"`
|
|
WebhookPath string `json:"webhook_path" env:"PICOCLAW_CHANNELS_LINE_WEBHOOK_PATH"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_LINE_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Typing TypingConfig `json:"typing,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_LINE_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *lineConfigV0) ToLINEConfig() (LINEConfig, LINESecurity) {
|
|
return LINEConfig{
|
|
Enabled: v.Enabled,
|
|
channelSecret: v.ChannelSecret,
|
|
channelAccessToken: v.ChannelAccessToken,
|
|
WebhookHost: v.WebhookHost,
|
|
WebhookPort: v.WebhookPort,
|
|
WebhookPath: v.WebhookPath,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Typing: v.Typing,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, LINESecurity{
|
|
ChannelSecret: v.ChannelSecret,
|
|
ChannelAccessToken: v.ChannelAccessToken,
|
|
}
|
|
}
|
|
|
|
type onebotConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_ONEBOT_ENABLED"`
|
|
WSUrl string `json:"ws_url" env:"PICOCLAW_CHANNELS_ONEBOT_WS_URL"`
|
|
AccessToken string `json:"access_token" env:"PICOCLAW_CHANNELS_ONEBOT_ACCESS_TOKEN"`
|
|
ReconnectInterval int `json:"reconnect_interval" env:"PICOCLAW_CHANNELS_ONEBOT_RECONNECT_INTERVAL"`
|
|
GroupTriggerPrefix []string `json:"group_trigger_prefix" env:"PICOCLAW_CHANNELS_ONEBOT_GROUP_TRIGGER_PREFIX"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_ONEBOT_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Typing TypingConfig `json:"typing,omitempty"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_ONEBOT_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *onebotConfigV0) ToOneBotConfig() (OneBotConfig, OneBotSecurity) {
|
|
return OneBotConfig{
|
|
Enabled: v.Enabled,
|
|
WSUrl: v.WSUrl,
|
|
accessToken: v.AccessToken,
|
|
ReconnectInterval: v.ReconnectInterval,
|
|
GroupTriggerPrefix: v.GroupTriggerPrefix,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Typing: v.Typing,
|
|
Placeholder: v.Placeholder,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, OneBotSecurity{
|
|
AccessToken: v.AccessToken,
|
|
}
|
|
}
|
|
|
|
type wecomConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_WECOM_ENABLED"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_WECOM_TOKEN"`
|
|
EncodingAESKey string `json:"encoding_aes_key" env:"PICOCLAW_CHANNELS_WECOM_ENCODING_AES_KEY"`
|
|
WebhookURL string `json:"webhook_url" env:"PICOCLAW_CHANNELS_WECOM_WEBHOOK_URL"`
|
|
WebhookHost string `json:"webhook_host" env:"PICOCLAW_CHANNELS_WECOM_WEBHOOK_HOST"`
|
|
WebhookPort int `json:"webhook_port" env:"PICOCLAW_CHANNELS_WECOM_WEBHOOK_PORT"`
|
|
WebhookPath string `json:"webhook_path" env:"PICOCLAW_CHANNELS_WECOM_WEBHOOK_PATH"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_WECOM_ALLOW_FROM"`
|
|
ReplyTimeout int `json:"reply_timeout" env:"PICOCLAW_CHANNELS_WECOM_REPLY_TIMEOUT"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_WECOM_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *wecomConfigV0) ToWeComConfig() (WeComConfig, WeComSecurity) {
|
|
return WeComConfig{
|
|
Enabled: v.Enabled,
|
|
token: v.Token,
|
|
encodingAESKey: v.EncodingAESKey,
|
|
WebhookURL: v.WebhookURL,
|
|
WebhookHost: v.WebhookHost,
|
|
WebhookPort: v.WebhookPort,
|
|
WebhookPath: v.WebhookPath,
|
|
AllowFrom: v.AllowFrom,
|
|
ReplyTimeout: v.ReplyTimeout,
|
|
GroupTrigger: v.GroupTrigger,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, WeComSecurity{
|
|
Token: v.Token,
|
|
EncodingAESKey: v.EncodingAESKey,
|
|
}
|
|
}
|
|
|
|
type weixinConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_WEIXIN_ENABLED"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_WEIXIN_TOKEN"`
|
|
BaseURL string `json:"base_url" env:"PICOCLAW_CHANNELS_WEIXIN_BASE_URL"`
|
|
CDNBaseURL string `json:"cdn_base_url" env:"PICOCLAW_CHANNELS_WEIXIN_CDN_BASE_URL"`
|
|
Proxy string `json:"proxy" env:"PICOCLAW_CHANNELS_WEIXIN_PROXY"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_WEIXIN_ALLOW_FROM"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_WEIXIN_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *weixinConfigV0) ToWeiXinConfig() (WeixinConfig, WeixinSecurity) {
|
|
return WeixinConfig{
|
|
Enabled: v.Enabled,
|
|
token: v.Token,
|
|
BaseURL: v.BaseURL,
|
|
CDNBaseURL: v.CDNBaseURL,
|
|
Proxy: v.Proxy,
|
|
AllowFrom: v.AllowFrom,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, WeixinSecurity{
|
|
Token: v.Token,
|
|
}
|
|
}
|
|
|
|
type wecomappConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_WECOM_APP_ENABLED"`
|
|
CorpID string `json:"corp_id" env:"PICOCLAW_CHANNELS_WECOM_APP_CORP_ID"`
|
|
CorpSecret string `json:"corp_secret" env:"PICOCLAW_CHANNELS_WECOM_APP_CORP_SECRET"`
|
|
AgentID int64 `json:"agent_id" env:"PICOCLAW_CHANNELS_WECOM_APP_AGENT_ID"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_WECOM_APP_TOKEN"`
|
|
EncodingAESKey string `json:"encoding_aes_key" env:"PICOCLAW_CHANNELS_WECOM_APP_ENCODING_AES_KEY"`
|
|
WebhookHost string `json:"webhook_host" env:"PICOCLAW_CHANNELS_WECOM_APP_WEBHOOK_HOST"`
|
|
WebhookPort int `json:"webhook_port" env:"PICOCLAW_CHANNELS_WECOM_APP_WEBHOOK_PORT"`
|
|
WebhookPath string `json:"webhook_path" env:"PICOCLAW_CHANNELS_WECOM_APP_WEBHOOK_PATH"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_WECOM_APP_ALLOW_FROM"`
|
|
ReplyTimeout int `json:"reply_timeout" env:"PICOCLAW_CHANNELS_WECOM_APP_REPLY_TIMEOUT"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_WECOM_APP_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *wecomappConfigV0) ToWeComAppConfig() (WeComAppConfig, WeComAppSecurity) {
|
|
return WeComAppConfig{
|
|
Enabled: v.Enabled,
|
|
CorpID: v.CorpID,
|
|
corpSecret: v.CorpSecret,
|
|
AgentID: v.AgentID,
|
|
token: v.Token,
|
|
encodingAESKey: v.EncodingAESKey,
|
|
WebhookHost: v.WebhookHost,
|
|
WebhookPort: v.WebhookPort,
|
|
WebhookPath: v.WebhookPath,
|
|
AllowFrom: v.AllowFrom,
|
|
ReplyTimeout: v.ReplyTimeout,
|
|
GroupTrigger: v.GroupTrigger,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, WeComAppSecurity{
|
|
CorpSecret: v.CorpSecret,
|
|
Token: v.Token,
|
|
EncodingAESKey: v.EncodingAESKey,
|
|
}
|
|
}
|
|
|
|
type wecomaibotConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_ENABLED"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_TOKEN"`
|
|
Secret string `json:"secret" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_SECRET"`
|
|
EncodingAESKey string `json:"encoding_aes_key" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_ENCODING_AES_KEY"`
|
|
WebhookPath string `json:"webhook_path" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_WEBHOOK_PATH"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_ALLOW_FROM"`
|
|
ReplyTimeout int `json:"reply_timeout" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_REPLY_TIMEOUT"`
|
|
MaxSteps int `json:"max_steps" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_MAX_STEPS"`
|
|
WelcomeMessage string `json:"welcome_message" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_WELCOME_MESSAGE"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_WECOM_AIBOT_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *wecomaibotConfigV0) ToWeComAIBotConfig() (WeComAIBotConfig, WeComAIBotSecurity) {
|
|
return WeComAIBotConfig{
|
|
Enabled: v.Enabled,
|
|
WebhookPath: v.WebhookPath,
|
|
AllowFrom: v.AllowFrom,
|
|
ReplyTimeout: v.ReplyTimeout,
|
|
MaxSteps: v.MaxSteps,
|
|
WelcomeMessage: v.WelcomeMessage,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, WeComAIBotSecurity{
|
|
Token: v.Token,
|
|
Secret: v.Secret,
|
|
EncodingAESKey: v.EncodingAESKey,
|
|
}
|
|
}
|
|
|
|
type picoConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_PICO_ENABLED"`
|
|
Token string `json:"token" env:"PICOCLAW_CHANNELS_PICO_TOKEN"`
|
|
AllowTokenQuery bool `json:"allow_token_query,omitempty"`
|
|
AllowOrigins []string `json:"allow_origins,omitempty"`
|
|
PingInterval int `json:"ping_interval,omitempty"`
|
|
ReadTimeout int `json:"read_timeout,omitempty"`
|
|
WriteTimeout int `json:"write_timeout,omitempty"`
|
|
MaxConnections int `json:"max_connections,omitempty"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_PICO_ALLOW_FROM"`
|
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty"`
|
|
}
|
|
|
|
func (v *picoConfigV0) ToPicoConfig() (PicoConfig, PicoSecurity) {
|
|
return PicoConfig{
|
|
Enabled: v.Enabled,
|
|
token: v.Token,
|
|
AllowTokenQuery: v.AllowTokenQuery,
|
|
AllowOrigins: v.AllowOrigins,
|
|
PingInterval: v.PingInterval,
|
|
ReadTimeout: v.ReadTimeout,
|
|
WriteTimeout: v.WriteTimeout,
|
|
MaxConnections: v.MaxConnections,
|
|
AllowFrom: v.AllowFrom,
|
|
Placeholder: v.Placeholder,
|
|
}, PicoSecurity{
|
|
Token: v.Token,
|
|
}
|
|
}
|
|
|
|
type ircConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_CHANNELS_IRC_ENABLED"`
|
|
Server string `json:"server" env:"PICOCLAW_CHANNELS_IRC_SERVER"`
|
|
TLS bool `json:"tls" env:"PICOCLAW_CHANNELS_IRC_TLS"`
|
|
Nick string `json:"nick" env:"PICOCLAW_CHANNELS_IRC_NICK"`
|
|
User string `json:"user,omitempty" env:"PICOCLAW_CHANNELS_IRC_USER"`
|
|
RealName string `json:"real_name,omitempty" env:"PICOCLAW_CHANNELS_IRC_REAL_NAME"`
|
|
Password string `json:"password" env:"PICOCLAW_CHANNELS_IRC_PASSWORD"`
|
|
NickServPassword string `json:"nickserv_password" env:"PICOCLAW_CHANNELS_IRC_NICKSERV_PASSWORD"`
|
|
SASLUser string `json:"sasl_user" env:"PICOCLAW_CHANNELS_IRC_SASL_USER"`
|
|
SASLPassword string `json:"sasl_password" env:"PICOCLAW_CHANNELS_IRC_SASL_PASSWORD"`
|
|
Channels FlexibleStringSlice `json:"channels" env:"PICOCLAW_CHANNELS_IRC_CHANNELS"`
|
|
RequestCaps FlexibleStringSlice `json:"request_caps,omitempty" env:"PICOCLAW_CHANNELS_IRC_REQUEST_CAPS"`
|
|
AllowFrom FlexibleStringSlice `json:"allow_from" env:"PICOCLAW_CHANNELS_IRC_ALLOW_FROM"`
|
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty"`
|
|
Typing TypingConfig `json:"typing,omitempty"`
|
|
ReasoningChannelID string `json:"reasoning_channel_id" env:"PICOCLAW_CHANNELS_IRC_REASONING_CHANNEL_ID"`
|
|
}
|
|
|
|
func (v *ircConfigV0) ToIRCConfig() (IRCConfig, IRCSecurity) {
|
|
return IRCConfig{
|
|
Enabled: v.Enabled,
|
|
Server: v.Server,
|
|
TLS: v.TLS,
|
|
Nick: v.Nick,
|
|
User: v.User,
|
|
RealName: v.RealName,
|
|
password: v.Password,
|
|
nickServPassword: v.NickServPassword,
|
|
SASLUser: v.SASLUser,
|
|
saslPassword: v.SASLPassword,
|
|
Channels: v.Channels,
|
|
RequestCaps: v.RequestCaps,
|
|
AllowFrom: v.AllowFrom,
|
|
GroupTrigger: v.GroupTrigger,
|
|
Typing: v.Typing,
|
|
ReasoningChannelID: v.ReasoningChannelID,
|
|
}, IRCSecurity{
|
|
Password: v.Password,
|
|
NickServPassword: v.NickServPassword,
|
|
SASLPassword: v.SASLPassword,
|
|
}
|
|
}
|
|
|
|
type providersConfigV0 struct {
|
|
Anthropic providerConfigV0 `json:"anthropic"`
|
|
OpenAI openAIProviderConfigV0 `json:"openai"`
|
|
LiteLLM providerConfigV0 `json:"litellm"`
|
|
OpenRouter providerConfigV0 `json:"openrouter"`
|
|
Groq providerConfigV0 `json:"groq"`
|
|
Zhipu providerConfigV0 `json:"zhipu"`
|
|
VLLM providerConfigV0 `json:"vllm"`
|
|
Gemini providerConfigV0 `json:"gemini"`
|
|
Nvidia providerConfigV0 `json:"nvidia"`
|
|
Ollama providerConfigV0 `json:"ollama"`
|
|
Moonshot providerConfigV0 `json:"moonshot"`
|
|
ShengSuanYun providerConfigV0 `json:"shengsuanyun"`
|
|
DeepSeek providerConfigV0 `json:"deepseek"`
|
|
Cerebras providerConfigV0 `json:"cerebras"`
|
|
Vivgrid providerConfigV0 `json:"vivgrid"`
|
|
VolcEngine providerConfigV0 `json:"volcengine"`
|
|
GitHubCopilot providerConfigV0 `json:"github_copilot"`
|
|
Antigravity providerConfigV0 `json:"antigravity"`
|
|
Qwen providerConfigV0 `json:"qwen"`
|
|
Mistral providerConfigV0 `json:"mistral"`
|
|
Avian providerConfigV0 `json:"avian"`
|
|
Minimax providerConfigV0 `json:"minimax"`
|
|
LongCat providerConfigV0 `json:"longcat"`
|
|
ModelScope providerConfigV0 `json:"modelscope"`
|
|
Novita providerConfigV0 `json:"novita"`
|
|
}
|
|
|
|
// IsEmpty checks if all provider configs are empty (no API keys or API bases set)
|
|
// Note: WebSearch is an optimization option and doesn't count as "non-empty"
|
|
func (p providersConfigV0) IsEmpty() bool {
|
|
return p.Anthropic.APIKey == "" && p.Anthropic.APIBase == "" &&
|
|
p.OpenAI.APIKey == "" && p.OpenAI.APIBase == "" &&
|
|
p.LiteLLM.APIKey == "" && p.LiteLLM.APIBase == "" &&
|
|
p.OpenRouter.APIKey == "" && p.OpenRouter.APIBase == "" &&
|
|
p.Groq.APIKey == "" && p.Groq.APIBase == "" &&
|
|
p.Zhipu.APIKey == "" && p.Zhipu.APIBase == "" &&
|
|
p.VLLM.APIKey == "" && p.VLLM.APIBase == "" &&
|
|
p.Gemini.APIKey == "" && p.Gemini.APIBase == "" &&
|
|
p.Nvidia.APIKey == "" && p.Nvidia.APIBase == "" &&
|
|
p.Ollama.APIKey == "" && p.Ollama.APIBase == "" &&
|
|
p.Moonshot.APIKey == "" && p.Moonshot.APIBase == "" &&
|
|
p.ShengSuanYun.APIKey == "" && p.ShengSuanYun.APIBase == "" &&
|
|
p.DeepSeek.APIKey == "" && p.DeepSeek.APIBase == "" &&
|
|
p.Cerebras.APIKey == "" && p.Cerebras.APIBase == "" &&
|
|
p.Vivgrid.APIKey == "" && p.Vivgrid.APIBase == "" &&
|
|
p.VolcEngine.APIKey == "" && p.VolcEngine.APIBase == "" &&
|
|
p.GitHubCopilot.APIKey == "" && p.GitHubCopilot.APIBase == "" &&
|
|
p.Antigravity.APIKey == "" && p.Antigravity.APIBase == "" &&
|
|
p.Qwen.APIKey == "" && p.Qwen.APIBase == "" &&
|
|
p.Mistral.APIKey == "" && p.Mistral.APIBase == "" &&
|
|
p.Avian.APIKey == "" && p.Avian.APIBase == "" &&
|
|
p.Minimax.APIKey == "" && p.Minimax.APIBase == "" &&
|
|
p.LongCat.APIKey == "" && p.LongCat.APIBase == "" &&
|
|
p.ModelScope.APIKey == "" && p.ModelScope.APIBase == "" &&
|
|
p.Novita.APIKey == "" && p.Novita.APIBase == ""
|
|
}
|
|
|
|
type providerConfigV0 struct {
|
|
APIKey string `json:"api_key" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_KEY"`
|
|
APIBase string `json:"api_base" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_BASE"`
|
|
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_PROXY"`
|
|
RequestTimeout int `json:"request_timeout,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_REQUEST_TIMEOUT"`
|
|
AuthMethod string `json:"auth_method,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_AUTH_METHOD"`
|
|
ConnectMode string `json:"connect_mode,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_CONNECT_MODE"` // only for Github Copilot, `stdio` or `grpc`
|
|
}
|
|
|
|
// MarshalJSON implements custom JSON marshaling for providersConfig
|
|
// to omit the entire section when empty
|
|
func (p providersConfigV0) MarshalJSON() ([]byte, error) {
|
|
if p.IsEmpty() {
|
|
return []byte("null"), nil
|
|
}
|
|
type Alias providersConfigV0
|
|
return json.Marshal((*Alias)(&p))
|
|
}
|
|
|
|
type openAIProviderConfigV0 struct {
|
|
providerConfigV0
|
|
WebSearch bool `json:"web_search" env:"PICOCLAW_PROVIDERS_OPENAI_WEB_SEARCH"`
|
|
}
|
|
|
|
type modelConfigV0 struct {
|
|
// Required fields
|
|
ModelName string `json:"model_name"` // User-facing alias for the model
|
|
Model string `json:"model"` // Protocol/model-identifier (e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4.6")
|
|
|
|
// HTTP-based providers
|
|
APIBase string `json:"api_base,omitempty"` // API endpoint URL
|
|
APIKey string `json:"api_key"` // API authentication key (single key)
|
|
APIKeys []string `json:"api_keys,omitempty"` // API authentication keys (multiple keys for failover)
|
|
Proxy string `json:"proxy,omitempty"` // HTTP proxy URL
|
|
Fallbacks []string `json:"fallbacks,omitempty"` // Fallback model names for failover
|
|
|
|
// Special providers (CLI-based, OAuth, etc.)
|
|
AuthMethod string `json:"auth_method,omitempty"` // Authentication method: oauth, token
|
|
ConnectMode string `json:"connect_mode,omitempty"` // Connection mode: stdio, grpc
|
|
Workspace string `json:"workspace,omitempty"` // Workspace path for CLI-based providers
|
|
|
|
// Optional optimizations
|
|
RPM int `json:"rpm,omitempty"` // Requests per minute limit
|
|
MaxTokensField string `json:"max_tokens_field,omitempty"` // Field name for max tokens (e.g., "max_completion_tokens")
|
|
RequestTimeout int `json:"request_timeout,omitempty"`
|
|
ThinkingLevel string `json:"thinking_level,omitempty"` // Extended thinking: off|low|medium|high|xhigh|adaptive
|
|
}
|
|
|
|
func (c *configV0) migrateChannelConfigs() {
|
|
// Discord: mention_only -> group_trigger.mention_only
|
|
if c.Channels.Discord.MentionOnly && !c.Channels.Discord.GroupTrigger.MentionOnly {
|
|
c.Channels.Discord.GroupTrigger.MentionOnly = true
|
|
}
|
|
|
|
// OneBot: group_trigger_prefix -> group_trigger.prefixes
|
|
if len(c.Channels.OneBot.GroupTriggerPrefix) > 0 &&
|
|
len(c.Channels.OneBot.GroupTrigger.Prefixes) == 0 {
|
|
c.Channels.OneBot.GroupTrigger.Prefixes = c.Channels.OneBot.GroupTriggerPrefix
|
|
}
|
|
}
|
|
|
|
func (c *configV0) Migrate() (*Config, error) {
|
|
// Migrate legacy channel config fields to new unified structures
|
|
cfg := DefaultConfig()
|
|
|
|
// Always copy user's Agents config to preserve settings like Provider, Model, MaxTokens
|
|
cfg.Agents.List = c.Agents.List
|
|
cfg.Agents.Defaults.Workspace = c.Agents.Defaults.Workspace
|
|
cfg.Agents.Defaults.RestrictToWorkspace = c.Agents.Defaults.RestrictToWorkspace
|
|
cfg.Agents.Defaults.AllowReadOutsideWorkspace = c.Agents.Defaults.AllowReadOutsideWorkspace
|
|
cfg.Agents.Defaults.Provider = c.Agents.Defaults.Provider
|
|
cfg.Agents.Defaults.ModelName = c.Agents.Defaults.GetModelName()
|
|
cfg.Agents.Defaults.ModelFallbacks = c.Agents.Defaults.ModelFallbacks
|
|
cfg.Agents.Defaults.ImageModel = c.Agents.Defaults.ImageModel
|
|
cfg.Agents.Defaults.ImageModelFallbacks = c.Agents.Defaults.ImageModelFallbacks
|
|
cfg.Agents.Defaults.MaxTokens = c.Agents.Defaults.MaxTokens
|
|
cfg.Agents.Defaults.Temperature = c.Agents.Defaults.Temperature
|
|
cfg.Agents.Defaults.MaxToolIterations = c.Agents.Defaults.MaxToolIterations
|
|
cfg.Agents.Defaults.SummarizeMessageThreshold = c.Agents.Defaults.SummarizeMessageThreshold
|
|
cfg.Agents.Defaults.SummarizeTokenPercent = c.Agents.Defaults.SummarizeTokenPercent
|
|
cfg.Agents.Defaults.MaxMediaSize = c.Agents.Defaults.MaxMediaSize
|
|
cfg.Agents.Defaults.Routing = c.Agents.Defaults.Routing
|
|
|
|
// Copy other top-level fields
|
|
cfg.Bindings = c.Bindings
|
|
cfg.Session = c.Session
|
|
var secChannels ChannelsSecurity
|
|
cfg.Channels, secChannels = c.Channels.ToChannelsConfig()
|
|
cfg.Gateway = c.Gateway
|
|
var secWeb WebToolsSecurity
|
|
cfg.Tools.Web, secWeb = c.Tools.Web.ToWebToolsConfig()
|
|
cfg.Tools.Cron = c.Tools.Cron
|
|
cfg.Tools.Exec = c.Tools.Exec
|
|
var secSkills SkillsSecurity
|
|
cfg.Tools.Skills, secSkills = c.Tools.Skills.ToSkillsToolsConfig()
|
|
cfg.Tools.MediaCleanup = c.Tools.MediaCleanup
|
|
cfg.Tools.MCP = c.Tools.MCP
|
|
cfg.Tools.AppendFile = c.Tools.AppendFile
|
|
cfg.Tools.EditFile = c.Tools.EditFile
|
|
cfg.Tools.FindSkills = c.Tools.FindSkills
|
|
cfg.Tools.I2C = c.Tools.I2C
|
|
cfg.Tools.InstallSkill = c.Tools.InstallSkill
|
|
cfg.Tools.ListDir = c.Tools.ListDir
|
|
cfg.Tools.Message = c.Tools.Message
|
|
cfg.Tools.ReadFile = c.Tools.ReadFile
|
|
cfg.Tools.SendFile = c.Tools.SendFile
|
|
cfg.Tools.Spawn = c.Tools.Spawn
|
|
cfg.Tools.SpawnStatus = c.Tools.SpawnStatus
|
|
cfg.Tools.SPI = c.Tools.SPI
|
|
cfg.Tools.Subagent = c.Tools.Subagent
|
|
cfg.Tools.WebFetch = c.Tools.WebFetch
|
|
cfg.Tools.AllowReadPaths = c.Tools.AllowReadPaths
|
|
cfg.Tools.AllowWritePaths = c.Tools.AllowWritePaths
|
|
cfg.Heartbeat = c.Heartbeat
|
|
cfg.Devices = c.Devices
|
|
|
|
secModels := make(map[string]ModelSecurityEntry, 0)
|
|
// Only override ModelList if user provided values
|
|
if len(c.ModelList) > 0 {
|
|
// Convert []modelConfigV0 to []ModelConfig
|
|
cfg.ModelList = make([]*ModelConfig, len(c.ModelList))
|
|
for i, m := range c.ModelList {
|
|
// Merge APIKey and APIKeys, deduplicating
|
|
mergedKeys := MergeAPIKeys(m.APIKey, m.APIKeys)
|
|
|
|
cfg.ModelList[i] = &ModelConfig{
|
|
ModelName: m.ModelName,
|
|
Model: m.Model,
|
|
APIBase: m.APIBase,
|
|
Proxy: m.Proxy,
|
|
Fallbacks: m.Fallbacks,
|
|
AuthMethod: m.AuthMethod,
|
|
ConnectMode: m.ConnectMode,
|
|
Workspace: m.Workspace,
|
|
RPM: m.RPM,
|
|
MaxTokensField: m.MaxTokensField,
|
|
RequestTimeout: m.RequestTimeout,
|
|
ThinkingLevel: m.ThinkingLevel,
|
|
apiKeys: mergedKeys,
|
|
}
|
|
}
|
|
names := toNameIndex(cfg.ModelList)
|
|
for i, m := range c.ModelList {
|
|
// Merge APIKey and APIKeys, deduplicating
|
|
mergedKeys := MergeAPIKeys(m.APIKey, m.APIKeys)
|
|
secModels[names[i]] = ModelSecurityEntry{
|
|
APIKeys: mergedKeys,
|
|
}
|
|
}
|
|
}
|
|
|
|
cfg.WithSecurity(&SecurityConfig{
|
|
ModelList: secModels,
|
|
Channels: secChannels,
|
|
Web: secWeb,
|
|
Skills: secSkills,
|
|
})
|
|
cfg.Version = CurrentVersion
|
|
return cfg, nil
|
|
}
|
|
|
|
type webToolsConfigV0 struct {
|
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_WEB_"`
|
|
Brave braveConfigV0 ` json:"brave"`
|
|
Tavily tavilyConfigV0 ` json:"tavily"`
|
|
DuckDuckGo DuckDuckGoConfig ` json:"duckduckgo"`
|
|
Perplexity perplexityConfigV0 ` json:"perplexity"`
|
|
SearXNG SearXNGConfig ` json:"searxng"`
|
|
GLMSearch glmSearchConfigV0 ` json:"glm_search"`
|
|
PreferNative bool ` json:"prefer_native" env:"PICOCLAW_TOOLS_WEB_PREFER_NATIVE"`
|
|
Proxy string ` json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
|
FetchLimitBytes int64 ` json:"fetch_limit_bytes,omitempty" env:"PICOCLAW_TOOLS_WEB_FETCH_LIMIT_BYTES"`
|
|
Format string ` json:"format,omitempty" env:"PICOCLAW_TOOLS_WEB_FORMAT"`
|
|
PrivateHostWhitelist FlexibleStringSlice ` json:"private_host_whitelist,omitempty" env:"PICOCLAW_TOOLS_WEB_PRIVATE_HOST_WHITELIST"`
|
|
}
|
|
|
|
type braveConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"`
|
|
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"`
|
|
APIKeys []string `json:"api_keys" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEYS"`
|
|
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_BRAVE_MAX_RESULTS"`
|
|
}
|
|
|
|
func (v *braveConfigV0) ToBraveConfig() (BraveConfig, BraveSecurity) {
|
|
return BraveConfig{
|
|
Enabled: v.Enabled,
|
|
MaxResults: v.MaxResults,
|
|
}, BraveSecurity{
|
|
APIKeys: MergeAPIKeys(v.APIKey, v.APIKeys),
|
|
}
|
|
}
|
|
|
|
type tavilyConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_TAVILY_ENABLED"`
|
|
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_TAVILY_API_KEY"`
|
|
APIKeys []string `json:"api_keys" env:"PICOCLAW_TOOLS_WEB_TAVILY_API_KEYS"`
|
|
BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_TAVILY_BASE_URL"`
|
|
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_TAVILY_MAX_RESULTS"`
|
|
}
|
|
|
|
func (v *tavilyConfigV0) ToTavilyConfig() (TavilyConfig, TavilySecurity) {
|
|
return TavilyConfig{
|
|
Enabled: v.Enabled,
|
|
BaseURL: v.BaseURL,
|
|
MaxResults: v.MaxResults,
|
|
}, TavilySecurity{
|
|
APIKeys: MergeAPIKeys(v.APIKey, v.APIKeys),
|
|
}
|
|
}
|
|
|
|
type perplexityConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_ENABLED"`
|
|
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_API_KEY"`
|
|
APIKeys []string `json:"api_keys" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_API_KEYS"`
|
|
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_MAX_RESULTS"`
|
|
}
|
|
|
|
func (v *perplexityConfigV0) ToPerplexityConfig() (PerplexityConfig, PerplexitySecurity) {
|
|
return PerplexityConfig{
|
|
Enabled: v.Enabled,
|
|
MaxResults: v.MaxResults,
|
|
}, PerplexitySecurity{
|
|
APIKeys: MergeAPIKeys(v.APIKey, v.APIKeys),
|
|
}
|
|
}
|
|
|
|
type glmSearchConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GLM_ENABLED"`
|
|
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_GLM_API_KEY"`
|
|
BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_GLM_BASE_URL"`
|
|
SearchEngine string `json:"search_engine" env:"PICOCLAW_TOOLS_WEB_GLM_SEARCH_ENGINE"`
|
|
}
|
|
|
|
func (v *glmSearchConfigV0) ToGLMSearchConfig() (GLMSearchConfig, GLMSearchSecurity) {
|
|
return GLMSearchConfig{
|
|
Enabled: v.Enabled,
|
|
apiKey: v.APIKey,
|
|
BaseURL: v.BaseURL,
|
|
SearchEngine: v.SearchEngine,
|
|
}, GLMSearchSecurity{
|
|
APIKey: v.APIKey,
|
|
}
|
|
}
|
|
|
|
func (v *webToolsConfigV0) ToWebToolsConfig() (WebToolsConfig, WebToolsSecurity) {
|
|
brave, braveSecurity := v.Brave.ToBraveConfig()
|
|
tavily, tavilySecurity := v.Tavily.ToTavilyConfig()
|
|
perplexity, perplexitySecurity := v.Perplexity.ToPerplexityConfig()
|
|
glmSearch, glmSearchSecurity := v.GLMSearch.ToGLMSearchConfig()
|
|
|
|
return WebToolsConfig{
|
|
ToolConfig: v.ToolConfig,
|
|
Brave: brave,
|
|
Tavily: tavily,
|
|
DuckDuckGo: v.DuckDuckGo,
|
|
Perplexity: perplexity,
|
|
SearXNG: v.SearXNG,
|
|
GLMSearch: glmSearch,
|
|
PreferNative: v.PreferNative,
|
|
Proxy: v.Proxy,
|
|
FetchLimitBytes: v.FetchLimitBytes,
|
|
Format: v.Format,
|
|
PrivateHostWhitelist: v.PrivateHostWhitelist,
|
|
}, WebToolsSecurity{
|
|
Brave: &braveSecurity,
|
|
Tavily: &tavilySecurity,
|
|
Perplexity: &perplexitySecurity,
|
|
GLMSearch: &glmSearchSecurity,
|
|
}
|
|
}
|
|
|
|
type skillsToolsConfigV0 struct {
|
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_SKILLS_"`
|
|
Registries skillsRegistriesConfigV0 ` json:"registries"`
|
|
Github skillsGithubConfigV0 ` json:"github"`
|
|
MaxConcurrentSearches int ` json:"max_concurrent_searches" env:"PICOCLAW_TOOLS_SKILLS_MAX_CONCURRENT_SEARCHES"`
|
|
SearchCache SearchCacheConfig ` json:"search_cache"`
|
|
}
|
|
|
|
type skillsRegistriesConfigV0 struct {
|
|
ClawHub clawHubRegistryConfigV0 `json:"clawhub"`
|
|
}
|
|
|
|
type clawHubRegistryConfigV0 struct {
|
|
Enabled bool `json:"enabled" env:"PICOCLAW_SKILLS_REGISTRIES_CLAWHUB_ENABLED"`
|
|
BaseURL string `json:"base_url" env:"PICOCLAW_SKILLS_REGISTRIES_CLAWHUB_BASE_URL"`
|
|
AuthToken string `json:"auth_token" env:"PICOCLAW_SKILLS_REGISTRIES_CLAWHUB_AUTH_TOKEN"`
|
|
SearchPath string `json:"search_path" env:"PICOCLAW_SKILLS_REGISTRIES_CLAWHUB_SEARCH_PATH"`
|
|
SkillsPath string `json:"skills_path" env:"PICOCLAW_SKILLS_REGISTRIES_CLAWHUB_SKILLS_PATH"`
|
|
}
|
|
|
|
func (v *clawHubRegistryConfigV0) ToClawHubRegistryConfig() (ClawHubRegistryConfig, ClawHubSecurity) {
|
|
return ClawHubRegistryConfig{
|
|
Enabled: v.Enabled,
|
|
BaseURL: v.BaseURL,
|
|
authToken: v.AuthToken,
|
|
SearchPath: v.SearchPath,
|
|
SkillsPath: v.SkillsPath,
|
|
}, ClawHubSecurity{
|
|
AuthToken: v.AuthToken,
|
|
}
|
|
}
|
|
|
|
type skillsGithubConfigV0 struct {
|
|
Token string `json:"token" env:"PICOCLAW_TOOLS_SKILLS_GITHUB_TOKEN"`
|
|
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_SKILLS_GITHUB_PROXY"`
|
|
}
|
|
|
|
func (v *skillsGithubConfigV0) ToSkillsGithubConfig() (SkillsGithubConfig, GithubSecurity) {
|
|
return SkillsGithubConfig{
|
|
token: v.Token,
|
|
Proxy: v.Proxy,
|
|
}, GithubSecurity{
|
|
Token: v.Token,
|
|
}
|
|
}
|
|
|
|
func (v *skillsRegistriesConfigV0) ToSkillsRegistriesConfig() (SkillsRegistriesConfig, *ClawHubSecurity) {
|
|
clawHub, clawHubSecurity := v.ClawHub.ToClawHubRegistryConfig()
|
|
|
|
return SkillsRegistriesConfig{
|
|
ClawHub: clawHub,
|
|
}, &clawHubSecurity
|
|
}
|
|
|
|
func (v *skillsToolsConfigV0) ToSkillsToolsConfig() (SkillsToolsConfig, SkillsSecurity) {
|
|
registries, registriesSecurity := v.Registries.ToSkillsRegistriesConfig()
|
|
github, githubSecurity := v.Github.ToSkillsGithubConfig()
|
|
|
|
return SkillsToolsConfig{
|
|
ToolConfig: v.ToolConfig,
|
|
Registries: registries,
|
|
Github: github,
|
|
MaxConcurrentSearches: v.MaxConcurrentSearches,
|
|
SearchCache: v.SearchCache,
|
|
}, SkillsSecurity{
|
|
Github: &githubSecurity,
|
|
ClawHub: registriesSecurity,
|
|
}
|
|
}
|