初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

+179
View File
@@ -0,0 +1,179 @@
// Package config 管理博闻配置。
package config
import (
"fmt"
"os"
"strconv"
"strings"
"time"
)
// Config 顶层配置。
type Config struct {
Server ServerConfig `yaml:"server"`
Providers ProvidersConfig `yaml:"providers"`
Balancer BalancerConfig `yaml:"balancer"`
Cache CacheConfig `yaml:"cache"`
Metrics MetricsConfig `yaml:"metrics"`
}
// ServerConfig 服务端配置。
type ServerConfig struct {
Port int `yaml:"port"`
ReadTimeout time.Duration `yaml:"read_timeout"`
WriteTimeout time.Duration `yaml:"write_timeout"`
}
// ProvidersConfig 聚合所有搜索提供商配置。
type ProvidersConfig struct {
Tavily ProviderConfig `yaml:"tavily"`
Brave ProviderConfig `yaml:"brave"`
}
// ProviderConfig 单个搜索提供商配置。
type ProviderConfig struct {
Enabled bool `yaml:"enabled"`
Timeout time.Duration `yaml:"timeout"`
MaxResults int `yaml:"max_results"`
HealthCheck HealthCheckConf `yaml:"health_check"`
// APIKeys 从环境变量注入,不在 YAML 中
APIKeys []string `yaml:"-"`
}
// HealthCheckConf 健康检查配置。
type HealthCheckConf struct {
Interval time.Duration `yaml:"interval"`
Timeout time.Duration `yaml:"timeout"`
FailureThreshold int `yaml:"failure_threshold"`
}
// BalancerConfig 负载均衡配置。
type BalancerConfig struct {
Strategy string `yaml:"strategy"`
RetryCount int `yaml:"retry_count"`
CircuitBreaker CircuitBreakerCfg `yaml:"circuit_breaker"`
}
// CircuitBreakerCfg 熔断器配置。
type CircuitBreakerCfg struct {
Enabled bool `yaml:"enabled"`
FailureThreshold int `yaml:"failure_threshold"`
Cooldown time.Duration `yaml:"cooldown"`
}
// CacheConfig 缓存配置。
type CacheConfig struct {
Enabled bool `yaml:"enabled"`
TTL time.Duration `yaml:"ttl"`
MaxItems int `yaml:"max_items"`
Redis RedisConfig `yaml:"redis"`
}
// RedisConfig Redis 缓存配置(二级缓存层,可开关)。
type RedisConfig struct {
Enabled bool `yaml:"enabled"`
Addr string `yaml:"addr"`
Password string `yaml:"password"`
DB int `yaml:"db"`
}
// MetricsConfig 监控指标配置。
type MetricsConfig struct {
Enabled bool `yaml:"enabled"`
Port int `yaml:"port"`
Path string `yaml:"path"`
}
// envVarKeys 定义各 provider 的 API Key 环境变量名。
var envVarKeys = map[string]string{
"tavily": "TAVILY_API_KEYS",
"brave": "BRAVE_API_KEYS",
}
// Load 加载配置,API Keys 从环境变量读取。
// configPath 为可选 YAML 配置文件路径。
func Load(configPath string) (*Config, error) {
cfg := &Config{}
setDefaults(cfg)
// 从 YAML 文件加载(如存在)
if configPath != "" {
if err := loadFromFile(configPath, cfg); err != nil {
return nil, err
}
}
// 从环境变量加载 API Keys
cfg.Providers.Tavily.APIKeys = parseEnvKeys(envVarKeys["tavily"])
cfg.Providers.Brave.APIKeys = parseEnvKeys(envVarKeys["brave"])
// 从环境变量加载 Redis 配置
if env := os.Getenv("MCP_WEB_SEARCH_REDIS_ENABLED"); env != "" {
cfg.Cache.Redis.Enabled, _ = strconv.ParseBool(env)
}
if env := os.Getenv("MCP_WEB_SEARCH_REDIS_ADDR"); env != "" {
cfg.Cache.Redis.Addr = env
}
if env := os.Getenv("MCP_WEB_SEARCH_REDIS_PASSWORD"); env != "" {
cfg.Cache.Redis.Password = env
}
if env := os.Getenv("MCP_WEB_SEARCH_REDIS_DB"); env != "" {
if db, err := strconv.Atoi(env); err == nil {
cfg.Cache.Redis.DB = db
}
}
return cfg, nil
}
func setDefaults(cfg *Config) {
cfg.Server.Port = 8080
cfg.Server.ReadTimeout = 30 * time.Second
cfg.Server.WriteTimeout = 30 * time.Second
// Provider 默认启用(可通过 YAML 中 enabled: false 关闭)
cfg.Providers.Tavily.Enabled = true
cfg.Providers.Tavily.Timeout = 10 * time.Second
cfg.Providers.Tavily.MaxResults = 10
cfg.Providers.Brave.Enabled = true
cfg.Providers.Brave.Timeout = 10 * time.Second
cfg.Providers.Brave.MaxResults = 10
cfg.Balancer.Strategy = "weighted_round_robin"
cfg.Balancer.RetryCount = 2
cfg.Balancer.CircuitBreaker.Enabled = true
cfg.Balancer.CircuitBreaker.FailureThreshold = 5
cfg.Balancer.CircuitBreaker.Cooldown = 30 * time.Second
cfg.Cache.Enabled = true
cfg.Cache.TTL = 5 * time.Minute
cfg.Cache.MaxItems = 1000
cfg.Cache.Redis.Enabled = false
cfg.Cache.Redis.Addr = "127.0.0.1:6379"
cfg.Cache.Redis.Password = ""
cfg.Cache.Redis.DB = 0
cfg.Metrics.Enabled = true
cfg.Metrics.Port = 9090
cfg.Metrics.Path = "/metrics"
}
func loadFromFile(path string, cfg *Config) error {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("read config: %w", err)
}
return yamlUnmarshal(data, cfg)
}
func parseEnvKeys(envName string) []string {
val := os.Getenv(envName)
if val == "" {
return nil
}
parts := strings.Split(val, ",")
keys := make([]string, 0, len(parts))
for _, p := range parts {
if trimmed := strings.TrimSpace(p); trimmed != "" {
keys = append(keys, trimmed)
}
}
return keys
}