diff --git a/pkg/config/info.go b/pkg/config/info.go index 085ead1a5..ba30b8d48 100644 --- a/pkg/config/info.go +++ b/pkg/config/info.go @@ -4,6 +4,21 @@ import ( "strings" ) +// ChannelInfo describes a channel and the credential keys it expects. +type ChannelInfo struct { + Name string `json:"name"` + Enabled bool `json:"enabled"` + RequiredCredentials []string `json:"required_credentials"` // config keys needed (tokens/secrets) +} + +// ProviderInfo describes a provider and its credential requirements. +type ProviderInfo struct { + Name string `json:"name"` + RequiredCredentials []string `json:"required_credentials"` + OptionalCredentials []string `json:"optional_credentials"` + HasCredentials bool `json:"has_credentials"` +} + // GetAllChannelNames returns all channel keys available in ChannelsConfig. func GetAllChannelNames() []string { return []string{ @@ -22,48 +37,59 @@ func GetAllChannelNames() []string { } } -// GetEnabledChannels returns the list of channels that are enabled in the config. -func GetEnabledChannels(c *Config) []string { - var res []string +var channelRequirements = map[string][]string{ + "whatsapp": {"bridge_url"}, + "telegram": {"token"}, + "feishu": {"app_id", "app_secret"}, + "discord": {"token"}, + "maixcam": {"host", "port"}, + "qq": {"app_id", "app_secret"}, + "dingtalk": {"client_id", "client_secret"}, + "slack": {"bot_token", "app_token"}, + "line": {"channel_secret", "channel_access_token"}, + "onebot": {"ws_url", "access_token"}, + "wecom": {"token", "encoding_aes_key"}, + "wecom_app": {"corp_id", "corp_secret", "agent_id", "token"}, +} + +// GetChannelRequirements returns the list of credential keys expected for that channel. +// These are the config field names (not values) that are typically required to operate +// the channel (e.g. "token", "bot_token"). Returns nil if the channel is unknown. +func GetChannelRequirements(channel string) []string { + return channelRequirements[strings.ToLower(channel)] +} + +// GetEnabledChannelsInfo returns structured information about all known channels +// including whether they are enabled in the provided config and which credentials +// they expect. +func GetEnabledChannelsInfo(c *Config) []ChannelInfo { + var res []ChannelInfo if c == nil { + // return requirements only (not enabled) if config not provided + for _, name := range GetAllChannelNames() { + res = append(res, ChannelInfo{Name: name, Enabled: false, RequiredCredentials: GetChannelRequirements(name)}) + } return res } - if c.Channels.WhatsApp.Enabled { - res = append(res, "whatsapp") - } - if c.Channels.Telegram.Enabled { - res = append(res, "telegram") - } - if c.Channels.Feishu.Enabled { - res = append(res, "feishu") - } - if c.Channels.Discord.Enabled { - res = append(res, "discord") - } - if c.Channels.MaixCam.Enabled { - res = append(res, "maixcam") - } - if c.Channels.QQ.Enabled { - res = append(res, "qq") - } - if c.Channels.DingTalk.Enabled { - res = append(res, "dingtalk") - } - if c.Channels.Slack.Enabled { - res = append(res, "slack") - } - if c.Channels.LINE.Enabled { - res = append(res, "line") - } - if c.Channels.OneBot.Enabled { - res = append(res, "onebot") - } - if c.Channels.WeCom.Enabled { - res = append(res, "wecom") - } - if c.Channels.WeComApp.Enabled { - res = append(res, "wecom_app") + + // helper to append + add := func(name string, enabled bool) { + res = append(res, ChannelInfo{Name: name, Enabled: enabled, RequiredCredentials: GetChannelRequirements(name)}) } + + add("whatsapp", c.Channels.WhatsApp.Enabled) + add("telegram", c.Channels.Telegram.Enabled) + add("feishu", c.Channels.Feishu.Enabled) + add("discord", c.Channels.Discord.Enabled) + add("maixcam", c.Channels.MaixCam.Enabled) + add("qq", c.Channels.QQ.Enabled) + add("dingtalk", c.Channels.DingTalk.Enabled) + add("slack", c.Channels.Slack.Enabled) + add("line", c.Channels.LINE.Enabled) + add("onebot", c.Channels.OneBot.Enabled) + add("wecom", c.Channels.WeCom.Enabled) + add("wecom_app", c.Channels.WeComApp.Enabled) + return res } @@ -111,144 +137,110 @@ func GetSupportedModelNames(c *Config) []string { return res } -// GetSupportedProviders returns a deduplicated list of provider identifiers. -// It includes known provider fields and protocols discovered in model_list entries -// and the agents default provider if set. -func GetSupportedProviders(c *Config) []string { - known := []string{ - "anthropic", "openai", "openrouter", "groq", "zhipu", "vllm", - "gemini", "nvidia", "ollama", "moonshot", "shengsuanyun", - "deepseek", "cerebras", "volcengine", "github_copilot", "antigravity", - "qwen", - } +// Known providers and their typical credential requirements. +var knownProviders = map[string]ProviderInfo{ + "anthropic": {Name: "anthropic", RequiredCredentials: []string{"api_key"}, OptionalCredentials: []string{"api_base"}}, + "openai": {Name: "openai", RequiredCredentials: []string{"api_key"}, OptionalCredentials: []string{"api_base"}}, + "openrouter": {Name: "openrouter", RequiredCredentials: []string{"api_key"}, OptionalCredentials: []string{"api_base"}}, + "groq": {Name: "groq", RequiredCredentials: []string{"api_key"}}, + "zhipu": {Name: "zhipu", RequiredCredentials: []string{"api_key"}}, + "vllm": {Name: "vllm", RequiredCredentials: []string{"api_key"}}, + "gemini": {Name: "gemini", RequiredCredentials: []string{"api_key"}}, + "nvidia": {Name: "nvidia", RequiredCredentials: []string{"api_key"}}, + "ollama": {Name: "ollama", RequiredCredentials: []string{"api_key"}}, + "moonshot": {Name: "moonshot", RequiredCredentials: []string{"api_key"}}, + "shengsuanyun": {Name: "shengsuanyun", RequiredCredentials: []string{"api_key"}}, + "deepseek": {Name: "deepseek", RequiredCredentials: []string{"api_key"}}, + "cerebras": {Name: "cerebras", RequiredCredentials: []string{"api_key"}}, + "volcengine": {Name: "volcengine", RequiredCredentials: []string{"api_key"}}, + "github_copilot": {Name: "github_copilot", RequiredCredentials: []string{"api_key"}}, + "antigravity": {Name: "antigravity", RequiredCredentials: []string{"api_key"}}, + "qwen": {Name: "qwen", RequiredCredentials: []string{"api_key"}}, +} - set := make(map[string]struct{}) - for _, k := range known { - set[k] = struct{}{} - } +// GetProvidersInfo returns provider metadata including required/optional fields +// and whether credentials are present in the current config. +func GetProvidersInfo(c *Config) []ProviderInfo { + // start from known providers map + res := make([]ProviderInfo, 0, len(knownProviders)) + has := map[string]bool{} - // Add provider from agents defaults if present - if c != nil && c.Agents.Defaults.Provider != "" { - set[strings.ToLower(c.Agents.Defaults.Provider)] = struct{}{} - } - - // Discover protocols used in model_list (protocol/model format) if c != nil { + // record legacy providers that have keys + if c.Providers.Anthropic.APIKey != "" || c.Providers.Anthropic.APIBase != "" { + has["anthropic"] = true + } + if c.Providers.OpenAI.APIKey != "" || c.Providers.OpenAI.APIBase != "" { + has["openai"] = true + } + if c.Providers.OpenRouter.APIKey != "" || c.Providers.OpenRouter.APIBase != "" { + has["openrouter"] = true + } + if c.Providers.Groq.APIKey != "" || c.Providers.Groq.APIBase != "" { + has["groq"] = true + } + if c.Providers.Zhipu.APIKey != "" || c.Providers.Zhipu.APIBase != "" { + has["zhipu"] = true + } + if c.Providers.VLLM.APIKey != "" || c.Providers.VLLM.APIBase != "" { + has["vllm"] = true + } + if c.Providers.Gemini.APIKey != "" || c.Providers.Gemini.APIBase != "" { + has["gemini"] = true + } + if c.Providers.Nvidia.APIKey != "" || c.Providers.Nvidia.APIBase != "" { + has["nvidia"] = true + } + if c.Providers.Ollama.APIKey != "" || c.Providers.Ollama.APIBase != "" { + has["ollama"] = true + } + if c.Providers.Moonshot.APIKey != "" || c.Providers.Moonshot.APIBase != "" { + has["moonshot"] = true + } + if c.Providers.ShengSuanYun.APIKey != "" || c.Providers.ShengSuanYun.APIBase != "" { + has["shengsuanyun"] = true + } + if c.Providers.DeepSeek.APIKey != "" || c.Providers.DeepSeek.APIBase != "" { + has["deepseek"] = true + } + if c.Providers.Cerebras.APIKey != "" || c.Providers.Cerebras.APIBase != "" { + has["cerebras"] = true + } + if c.Providers.VolcEngine.APIKey != "" || c.Providers.VolcEngine.APIBase != "" { + has["volcengine"] = true + } + if c.Providers.GitHubCopilot.APIKey != "" || c.Providers.GitHubCopilot.APIBase != "" { + has["github_copilot"] = true + } + if c.Providers.Antigravity.APIKey != "" || c.Providers.Antigravity.APIBase != "" { + has["antigravity"] = true + } + if c.Providers.Qwen.APIKey != "" || c.Providers.Qwen.APIBase != "" { + has["qwen"] = true + } + + // Model-list based credentials: map model protocol -> present for _, m := range c.ModelList { - if m.Model == "" { - continue - } - p := parseProtocol(m.Model) - if p != "" { - set[p] = struct{}{} + if m.APIKey != "" || m.APIBase != "" { + p := parseProtocol(m.Model) + if p == "" { + p = "openai" + } + has[p] = true } } } - res := make([]string, 0, len(set)) - for k := range set { - res = append(res, k) - } - return res -} - -// GetProvidersWithCredentials returns providers that have API key or API base set -// in the legacy ProvidersConfig, as well as protocols with credentials defined -// in model_list entries (ModelConfig.APIKey/APIBase). -func GetProvidersWithCredentials(c *Config) []string { - if c == nil { - return nil - } - set := make(map[string]struct{}) - - // Legacy providers - if c.Providers.Anthropic.APIKey != "" || c.Providers.Anthropic.APIBase != "" { - set["anthropic"] = struct{}{} - } - if c.Providers.OpenAI.APIKey != "" || c.Providers.OpenAI.APIBase != "" { - set["openai"] = struct{}{} - } - if c.Providers.OpenRouter.APIKey != "" || c.Providers.OpenRouter.APIBase != "" { - set["openrouter"] = struct{}{} - } - if c.Providers.Groq.APIKey != "" || c.Providers.Groq.APIBase != "" { - set["groq"] = struct{}{} - } - if c.Providers.Zhipu.APIKey != "" || c.Providers.Zhipu.APIBase != "" { - set["zhipu"] = struct{}{} - } - if c.Providers.VLLM.APIKey != "" || c.Providers.VLLM.APIBase != "" { - set["vllm"] = struct{}{} - } - if c.Providers.Gemini.APIKey != "" || c.Providers.Gemini.APIBase != "" { - set["gemini"] = struct{}{} - } - if c.Providers.Nvidia.APIKey != "" || c.Providers.Nvidia.APIBase != "" { - set["nvidia"] = struct{}{} - } - if c.Providers.Ollama.APIKey != "" || c.Providers.Ollama.APIBase != "" { - set["ollama"] = struct{}{} - } - if c.Providers.Moonshot.APIKey != "" || c.Providers.Moonshot.APIBase != "" { - set["moonshot"] = struct{}{} - } - if c.Providers.ShengSuanYun.APIKey != "" || c.Providers.ShengSuanYun.APIBase != "" { - set["shengsuanyun"] = struct{}{} - } - if c.Providers.DeepSeek.APIKey != "" || c.Providers.DeepSeek.APIBase != "" { - set["deepseek"] = struct{}{} - } - if c.Providers.Cerebras.APIKey != "" || c.Providers.Cerebras.APIBase != "" { - set["cerebras"] = struct{}{} - } - if c.Providers.VolcEngine.APIKey != "" || c.Providers.VolcEngine.APIBase != "" { - set["volcengine"] = struct{}{} - } - if c.Providers.GitHubCopilot.APIKey != "" || c.Providers.GitHubCopilot.APIBase != "" { - set["github_copilot"] = struct{}{} - } - if c.Providers.Antigravity.APIKey != "" || c.Providers.Antigravity.APIBase != "" { - set["antigravity"] = struct{}{} - } - if c.Providers.Qwen.APIKey != "" || c.Providers.Qwen.APIBase != "" { - set["qwen"] = struct{}{} - } - - // Model-list based providers - for _, m := range c.ModelList { - if m.APIKey != "" || m.APIBase != "" { - p := parseProtocol(m.Model) - if p == "" { - p = "openai" - } - set[p] = struct{}{} + for k, info := range knownProviders { + pi := info + // if knownProviders entry didn't set optional/required copies, ensure Name set + if pi.Name == "" { + pi.Name = k } - } - - res := make([]string, 0, len(set)) - for k := range set { - res = append(res, k) - } - return res -} - -// GetWebTools returns the names of web tools configured (those present in Tools.Web). -func GetWebTools(c *Config) []string { - if c == nil { - return nil - } - var res []string - if c.Tools.Web.Brave.Enabled { - res = append(res, "brave") - } - if c.Tools.Web.Tavily.Enabled { - res = append(res, "tavily") - } - if c.Tools.Web.DuckDuckGo.Enabled { - res = append(res, "duckduckgo") - } - if c.Tools.Web.Perplexity.Enabled { - res = append(res, "perplexity") + if has[k] { + pi.HasCredentials = true + } + res = append(res, pi) } return res }