- Default provider changed from Zhipu to Agnes AI (agnes-2.0-flash) - Add --provider flag: agnes (default), zhipu, custom - Add AGNES_API_KEY env var support (still compatible with ZHIPU_API_KEY) - Add provider presets system for easy switching between AI backends - Add .gitignore to prevent config.json with API keys from being committed - Update usage examples Co-Authored-By: Claude <noreply@anthropic.com>
142 lines
3.5 KiB
Go
142 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Config holds the application configuration.
|
|
type Config struct {
|
|
APIKey string `json:"api_key"`
|
|
Model string `json:"model"`
|
|
BaseURL string `json:"base_url"`
|
|
Provider string `json:"provider"` // "agnes" or "zhipu" or "custom"
|
|
}
|
|
|
|
// Provider presets
|
|
var providerPresets = map[string]struct {
|
|
Model string
|
|
BaseURL string
|
|
}{
|
|
"agnes": {
|
|
Model: "agnes-2.0-flash",
|
|
BaseURL: "https://apihub.agnes-ai.com/v1/chat/completions",
|
|
},
|
|
"zhipu": {
|
|
Model: "glm-4.6v-flash",
|
|
BaseURL: "https://open.bigmodel.cn/api/paas/v4/chat/completions",
|
|
},
|
|
}
|
|
|
|
// DefaultConfig returns a Config with sensible defaults (Agnes AI).
|
|
func DefaultConfig() *Config {
|
|
return &Config{
|
|
Provider: "agnes",
|
|
Model: "agnes-2.0-flash",
|
|
BaseURL: "https://apihub.agnes-ai.com/v1/chat/completions",
|
|
}
|
|
}
|
|
|
|
// ApplyProvider applies a named provider preset, respecting user overrides.
|
|
func (c *Config) ApplyProvider(provider string) {
|
|
if preset, ok := providerPresets[provider]; ok {
|
|
c.Provider = provider
|
|
// Only override model/baseURL if user hasn't set custom ones
|
|
if c.Model == "" || c.Model == "agnes-2.0-flash" || c.Model == "glm-4.6v-flash" {
|
|
c.Model = preset.Model
|
|
}
|
|
if c.BaseURL == "" || c.BaseURL == "https://apihub.agnes-ai.com/v1/chat/completions" ||
|
|
c.BaseURL == "https://open.bigmodel.cn/api/paas/v4/chat/completions" {
|
|
c.BaseURL = preset.BaseURL
|
|
}
|
|
}
|
|
}
|
|
|
|
// LoadConfig loads configuration from multiple sources in priority order:
|
|
// 1. Command-line --apikey flag (highest)
|
|
// 2. ZHIPU_API_KEY environment variable
|
|
// 3. config.json in the same directory as the executable
|
|
// 4. Default built-in key (lowest)
|
|
func LoadConfig(flagKey, configPath string) (*Config, error) {
|
|
cfg := DefaultConfig()
|
|
|
|
// Load from config file if it exists (lowest priority)
|
|
if configPath == "" {
|
|
execDir := getExecDir()
|
|
configPath = filepath.Join(execDir, "config.json")
|
|
}
|
|
|
|
if data, err := os.ReadFile(configPath); err == nil {
|
|
var fileCfg Config
|
|
if err := json.Unmarshal(data, &fileCfg); err == nil {
|
|
if fileCfg.APIKey != "" {
|
|
cfg.APIKey = fileCfg.APIKey
|
|
}
|
|
if fileCfg.Model != "" {
|
|
cfg.Model = fileCfg.Model
|
|
}
|
|
if fileCfg.BaseURL != "" {
|
|
cfg.BaseURL = fileCfg.BaseURL
|
|
}
|
|
}
|
|
}
|
|
|
|
// Override with environment variable
|
|
if envKey := os.Getenv("AGNES_API_KEY"); envKey != "" {
|
|
cfg.APIKey = envKey
|
|
}
|
|
if envKey := os.Getenv("ZHIPU_API_KEY"); envKey != "" {
|
|
cfg.APIKey = envKey
|
|
}
|
|
|
|
// Highest priority: command-line flag
|
|
if flagKey != "" {
|
|
cfg.APIKey = flagKey
|
|
}
|
|
|
|
if cfg.APIKey == "" {
|
|
return cfg, fmt.Errorf("API key not configured. Set it via:\n" +
|
|
" 1. --apikey flag\n" +
|
|
" 2. AGNES_API_KEY environment variable\n" +
|
|
" 3. config.json file in the tools directory")
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
// SaveConfigFile saves config to a JSON file.
|
|
func SaveConfigFile(path, apiKey string) error {
|
|
cfg := DefaultConfig()
|
|
cfg.APIKey = apiKey
|
|
|
|
data, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(path, data, 0600)
|
|
}
|
|
|
|
func getExecDir() string {
|
|
execPath, err := os.Executable()
|
|
if err != nil {
|
|
return "."
|
|
}
|
|
return filepath.Dir(execPath)
|
|
}
|
|
|
|
// MaskKey masks the API key for safe display, showing only first 8 and last 4 chars.
|
|
func MaskKey(key string) string {
|
|
if len(key) <= 12 {
|
|
return strings.Repeat("*", len(key))
|
|
}
|
|
return key[:8] + "..." + key[len(key)-4:]
|
|
}
|