fix(pr): Restore mistakenly removed comments and formatting in config.go and types.go
This commit is contained in:
parent
6e1b3c845d
commit
b08d949856
2 changed files with 190 additions and 96 deletions
|
|
@ -10,18 +10,23 @@ import (
|
||||||
"github.com/caarlos0/env/v11"
|
"github.com/caarlos0/env/v11"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// rrCounter is a global counter for round-robin load balancing across models.
|
||||||
var rrCounter atomic.Uint64
|
var rrCounter atomic.Uint64
|
||||||
|
|
||||||
|
// FlexibleStringSlice is a []string that also accepts JSON numbers,
|
||||||
|
// so allow_from can contain both "123" and 123.
|
||||||
type FlexibleStringSlice []string
|
type FlexibleStringSlice []string
|
||||||
|
|
||||||
func (f *FlexibleStringSlice) UnmarshalJSON(data []byte) error {
|
func (f *FlexibleStringSlice) UnmarshalJSON(data []byte) error {
|
||||||
|
// Try []string first
|
||||||
var ss []string
|
var ss []string
|
||||||
if err := json.Unmarshal(data, &ss); err == nil {
|
if err := json.Unmarshal(data, &ss); err == nil {
|
||||||
*f = ss
|
*f = ss
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var raw []interface{}
|
// Try []interface{} to handle mixed types
|
||||||
|
var raw []any
|
||||||
if err := json.Unmarshal(data, &raw); err != nil {
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -47,13 +52,15 @@ type Config struct {
|
||||||
Session SessionConfig `json:"session,omitempty"`
|
Session SessionConfig `json:"session,omitempty"`
|
||||||
Channels ChannelsConfig `json:"channels"`
|
Channels ChannelsConfig `json:"channels"`
|
||||||
Providers ProvidersConfig `json:"providers,omitempty"`
|
Providers ProvidersConfig `json:"providers,omitempty"`
|
||||||
ModelList []ModelConfig `json:"model_list"`
|
ModelList []ModelConfig `json:"model_list"` // New model-centric provider configuration
|
||||||
Gateway GatewayConfig `json:"gateway"`
|
Gateway GatewayConfig `json:"gateway"`
|
||||||
Tools ToolsConfig `json:"tools"`
|
Tools ToolsConfig `json:"tools"`
|
||||||
Heartbeat HeartbeatConfig `json:"heartbeat"`
|
Heartbeat HeartbeatConfig `json:"heartbeat"`
|
||||||
Devices DevicesConfig `json:"devices"`
|
Devices DevicesConfig `json:"devices"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements custom JSON marshaling for Config
|
||||||
|
// to omit providers section when empty and session when empty
|
||||||
func (c Config) MarshalJSON() ([]byte, error) {
|
func (c Config) MarshalJSON() ([]byte, error) {
|
||||||
type Alias Config
|
type Alias Config
|
||||||
aux := &struct {
|
aux := &struct {
|
||||||
|
|
@ -64,10 +71,12 @@ func (c Config) MarshalJSON() ([]byte, error) {
|
||||||
Alias: (*Alias)(&c),
|
Alias: (*Alias)(&c),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only include providers if not empty
|
||||||
if !c.Providers.IsEmpty() {
|
if !c.Providers.IsEmpty() {
|
||||||
aux.Providers = &c.Providers
|
aux.Providers = &c.Providers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only include session if not empty
|
||||||
if c.Session.DMScope != "" || len(c.Session.IdentityLinks) > 0 {
|
if c.Session.DMScope != "" || len(c.Session.IdentityLinks) > 0 {
|
||||||
aux.Session = &c.Session
|
aux.Session = &c.Session
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +89,9 @@ type AgentsConfig struct {
|
||||||
List []AgentConfig `json:"list,omitempty"`
|
List []AgentConfig `json:"list,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AgentModelConfig supports both string and structured model config.
|
||||||
|
// String format: "gpt-4" (just primary, no fallbacks)
|
||||||
|
// Object format: {"primary": "gpt-4", "fallbacks": ["claude-haiku"]}
|
||||||
type AgentModelConfig struct {
|
type AgentModelConfig struct {
|
||||||
Primary string `json:"primary,omitempty"`
|
Primary string `json:"primary,omitempty"`
|
||||||
Fallbacks []string `json:"fallbacks,omitempty"`
|
Fallbacks []string `json:"fallbacks,omitempty"`
|
||||||
|
|
@ -89,6 +101,7 @@ func (m *AgentModelConfig) UnmarshalJSON(data []byte) error {
|
||||||
var s string
|
var s string
|
||||||
if err := json.Unmarshal(data, &s); err == nil {
|
if err := json.Unmarshal(data, &s); err == nil {
|
||||||
m.Primary = s
|
m.Primary = s
|
||||||
|
m.Fallbacks = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
type raw struct {
|
type raw struct {
|
||||||
|
|
@ -177,6 +190,8 @@ type ChannelsConfig struct {
|
||||||
Slack SlackConfig `json:"slack"`
|
Slack SlackConfig `json:"slack"`
|
||||||
LINE LINEConfig `json:"line"`
|
LINE LINEConfig `json:"line"`
|
||||||
OneBot OneBotConfig `json:"onebot"`
|
OneBot OneBotConfig `json:"onebot"`
|
||||||
|
WeCom WeComConfig `json:"wecom"`
|
||||||
|
WeComApp WeComAppConfig `json:"wecom_app"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WhatsAppConfig struct {
|
type WhatsAppConfig struct {
|
||||||
|
|
@ -264,9 +279,35 @@ type OneBotConfig struct {
|
||||||
MaxMessageLength int `json:"max_message_length,omitempty" env:"PICOCLAW_CHANNELS_ONEBOT_MAX_MESSAGE_LENGTH"`
|
MaxMessageLength int `json:"max_message_length,omitempty" env:"PICOCLAW_CHANNELS_ONEBOT_MAX_MESSAGE_LENGTH"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WeComConfig 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WeComAppConfig 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"`
|
||||||
|
}
|
||||||
|
|
||||||
type HeartbeatConfig struct {
|
type HeartbeatConfig struct {
|
||||||
Enabled bool `json:"enabled" env:"PICOCLAW_HEARTBEAT_ENABLED"`
|
Enabled bool `json:"enabled" env:"PICOCLAW_HEARTBEAT_ENABLED"`
|
||||||
Interval int `json:"interval" env:"PICOCLAW_HEARTBEAT_INTERVAL"`
|
Interval int `json:"interval" env:"PICOCLAW_HEARTBEAT_INTERVAL"` // minutes, min 5
|
||||||
}
|
}
|
||||||
|
|
||||||
type DevicesConfig struct {
|
type DevicesConfig struct {
|
||||||
|
|
@ -294,6 +335,8 @@ type ProvidersConfig struct {
|
||||||
Qwen ProviderConfig `json:"qwen"`
|
Qwen ProviderConfig `json:"qwen"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 ProvidersConfig) IsEmpty() bool {
|
func (p ProvidersConfig) IsEmpty() bool {
|
||||||
return p.Anthropic.APIKey == "" && p.Anthropic.APIBase == "" &&
|
return p.Anthropic.APIKey == "" && p.Anthropic.APIBase == "" &&
|
||||||
p.OpenAI.APIKey == "" && p.OpenAI.APIBase == "" &&
|
p.OpenAI.APIKey == "" && p.OpenAI.APIBase == "" &&
|
||||||
|
|
@ -314,6 +357,8 @@ func (p ProvidersConfig) IsEmpty() bool {
|
||||||
p.Qwen.APIKey == "" && p.Qwen.APIBase == ""
|
p.Qwen.APIKey == "" && p.Qwen.APIBase == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements custom JSON marshaling for ProvidersConfig
|
||||||
|
// to omit the entire section when empty
|
||||||
func (p ProvidersConfig) MarshalJSON() ([]byte, error) {
|
func (p ProvidersConfig) MarshalJSON() ([]byte, error) {
|
||||||
if p.IsEmpty() {
|
if p.IsEmpty() {
|
||||||
return []byte("null"), nil
|
return []byte("null"), nil
|
||||||
|
|
@ -327,7 +372,7 @@ type ProviderConfig struct {
|
||||||
APIBase string `json:"api_base" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_BASE"`
|
APIBase string `json:"api_base" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_BASE"`
|
||||||
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_PROXY"`
|
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_PROXY"`
|
||||||
AuthMethod string `json:"auth_method,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_AUTH_METHOD"`
|
AuthMethod string `json:"auth_method,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_AUTH_METHOD"`
|
||||||
ConnectMode string `json:"connect_mode,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_CONNECT_MODE"`
|
ConnectMode string `json:"connect_mode,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_CONNECT_MODE"` // only for Github Copilot, `stdio` or `grpc`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenAIProviderConfig struct {
|
type OpenAIProviderConfig struct {
|
||||||
|
|
@ -335,19 +380,32 @@ type OpenAIProviderConfig struct {
|
||||||
WebSearch bool `json:"web_search" env:"PICOCLAW_PROVIDERS_OPENAI_WEB_SEARCH"`
|
WebSearch bool `json:"web_search" env:"PICOCLAW_PROVIDERS_OPENAI_WEB_SEARCH"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ModelConfig represents a model-centric provider configuration.
|
||||||
|
// It allows adding new providers (especially OpenAI-compatible ones) via configuration only.
|
||||||
|
// The model field uses protocol prefix format: [protocol/]model-identifier
|
||||||
|
// Supported protocols: openai, anthropic, antigravity, claude-cli, codex-cli, github-copilot
|
||||||
|
// Default protocol is "openai" if no prefix is specified.
|
||||||
type ModelConfig struct {
|
type ModelConfig struct {
|
||||||
ModelName string `json:"model_name"`
|
// Required fields
|
||||||
Model string `json:"model"`
|
ModelName string `json:"model_name"` // User-facing alias for the model
|
||||||
APIBase string `json:"api_base,omitempty"`
|
Model string `json:"model"` // Protocol/model-identifier (e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4.6")
|
||||||
APIKey string `json:"api_key"`
|
|
||||||
Proxy string `json:"proxy,omitempty"`
|
// HTTP-based providers
|
||||||
AuthMethod string `json:"auth_method,omitempty"`
|
APIBase string `json:"api_base,omitempty"` // API endpoint URL
|
||||||
ConnectMode string `json:"connect_mode,omitempty"`
|
APIKey string `json:"api_key"` // API authentication key
|
||||||
Workspace string `json:"workspace,omitempty"`
|
Proxy string `json:"proxy,omitempty"` // HTTP proxy URL
|
||||||
RPM int `json:"rpm,omitempty"`
|
|
||||||
MaxTokensField string `json:"max_tokens_field,omitempty"`
|
// 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the ModelConfig has all required fields.
|
||||||
func (c *ModelConfig) Validate() error {
|
func (c *ModelConfig) Validate() error {
|
||||||
if c.ModelName == "" {
|
if c.ModelName == "" {
|
||||||
return fmt.Errorf("model_name is required")
|
return fmt.Errorf("model_name is required")
|
||||||
|
|
@ -369,6 +427,13 @@ type BraveConfig struct {
|
||||||
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_BRAVE_MAX_RESULTS"`
|
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_BRAVE_MAX_RESULTS"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TavilyConfig struct {
|
||||||
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_TAVILY_ENABLED"`
|
||||||
|
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_TAVILY_API_KEY"`
|
||||||
|
BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_TAVILY_BASE_URL"`
|
||||||
|
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_TAVILY_MAX_RESULTS"`
|
||||||
|
}
|
||||||
|
|
||||||
type DuckDuckGoConfig struct {
|
type DuckDuckGoConfig struct {
|
||||||
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED"`
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED"`
|
||||||
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"`
|
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"`
|
||||||
|
|
@ -382,12 +447,13 @@ type PerplexityConfig struct {
|
||||||
|
|
||||||
type WebToolsConfig struct {
|
type WebToolsConfig struct {
|
||||||
Brave BraveConfig `json:"brave"`
|
Brave BraveConfig `json:"brave"`
|
||||||
|
Tavily TavilyConfig `json:"tavily"`
|
||||||
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"`
|
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"`
|
||||||
Perplexity PerplexityConfig `json:"perplexity"`
|
Perplexity PerplexityConfig `json:"perplexity"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CronToolsConfig struct {
|
type CronToolsConfig struct {
|
||||||
ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"`
|
ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"` // 0 means no timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecConfig struct {
|
type ExecConfig struct {
|
||||||
|
|
@ -430,10 +496,13 @@ type ClawHubRegistryConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(path string) (*Config, error) {
|
func LoadConfig(path string) (*Config, error) {
|
||||||
cfg := &Config{}
|
cfg := DefaultConfig()
|
||||||
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,6 +514,16 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-migrate: if only legacy providers config exists, convert to model_list
|
||||||
|
if len(cfg.ModelList) == 0 && cfg.HasProvidersConfig() {
|
||||||
|
cfg.ModelList = ConvertProvidersToModelList(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate model_list for uniqueness and required fields
|
||||||
|
if err := cfg.ValidateModelList(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -455,11 +534,11 @@ func SaveConfig(path string, cfg *Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(path)
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.WriteFile(path, data, 0600)
|
return os.WriteFile(path, data, 0o600)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) WorkspacePath() string {
|
func (c *Config) WorkspacePath() string {
|
||||||
|
|
@ -527,6 +606,9 @@ func expandHome(path string) string {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetModelConfig returns the ModelConfig for the given model name.
|
||||||
|
// If multiple configs exist with the same model_name, it uses round-robin
|
||||||
|
// selection for load balancing. Returns an error if the model is not found.
|
||||||
func (c *Config) GetModelConfig(modelName string) (*ModelConfig, error) {
|
func (c *Config) GetModelConfig(modelName string) (*ModelConfig, error) {
|
||||||
matches := c.findMatches(modelName)
|
matches := c.findMatches(modelName)
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
|
|
@ -536,10 +618,12 @@ func (c *Config) GetModelConfig(modelName string) (*ModelConfig, error) {
|
||||||
return &matches[0], nil
|
return &matches[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Multiple configs - use round-robin for load balancing
|
||||||
idx := rrCounter.Add(1) % uint64(len(matches))
|
idx := rrCounter.Add(1) % uint64(len(matches))
|
||||||
return &matches[idx], nil
|
return &matches[idx], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findMatches finds all ModelConfig entries with the given model_name.
|
||||||
func (c *Config) findMatches(modelName string) []ModelConfig {
|
func (c *Config) findMatches(modelName string) []ModelConfig {
|
||||||
var matches []ModelConfig
|
var matches []ModelConfig
|
||||||
for i := range c.ModelList {
|
for i := range c.ModelList {
|
||||||
|
|
@ -550,6 +634,7 @@ func (c *Config) findMatches(modelName string) []ModelConfig {
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasProvidersConfig checks if any provider in the old providers config has configuration.
|
||||||
func (c *Config) HasProvidersConfig() bool {
|
func (c *Config) HasProvidersConfig() bool {
|
||||||
v := c.Providers
|
v := c.Providers
|
||||||
return v.Anthropic.APIKey != "" || v.Anthropic.APIBase != "" ||
|
return v.Anthropic.APIKey != "" || v.Anthropic.APIBase != "" ||
|
||||||
|
|
@ -571,6 +656,9 @@ func (c *Config) HasProvidersConfig() bool {
|
||||||
v.Qwen.APIKey != "" || v.Qwen.APIBase != ""
|
v.Qwen.APIKey != "" || v.Qwen.APIBase != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateModelList validates all ModelConfig entries in the model_list.
|
||||||
|
// It checks that each model config is valid.
|
||||||
|
// Note: Multiple entries with the same model_name are allowed for load balancing.
|
||||||
func (c *Config) ValidateModelList() error {
|
func (c *Config) ValidateModelList() error {
|
||||||
for i := range c.ModelList {
|
for i := range c.ModelList {
|
||||||
if err := c.ModelList[i].Validate(); err != nil {
|
if err := c.ModelList[i].Validate(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,39 @@ import (
|
||||||
"github.com/sipeed/picoclaw/pkg/providers/protocoltypes"
|
"github.com/sipeed/picoclaw/pkg/providers/protocoltypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ToolCall = protocoltypes.ToolCall
|
type (
|
||||||
type FunctionCall = protocoltypes.FunctionCall
|
ToolCall = protocoltypes.ToolCall
|
||||||
type ExtraContent = protocoltypes.ExtraContent
|
FunctionCall = protocoltypes.FunctionCall
|
||||||
type GoogleExtra = protocoltypes.GoogleExtra
|
LLMResponse = protocoltypes.LLMResponse
|
||||||
type LLMResponse = protocoltypes.LLMResponse
|
UsageInfo = protocoltypes.UsageInfo
|
||||||
type UsageInfo = protocoltypes.UsageInfo
|
Message = protocoltypes.Message
|
||||||
type Message = protocoltypes.Message
|
ToolDefinition = protocoltypes.ToolDefinition
|
||||||
type ToolDefinition = protocoltypes.ToolDefinition
|
ToolFunctionDefinition = protocoltypes.ToolFunctionDefinition
|
||||||
type ToolFunctionDefinition = protocoltypes.ToolFunctionDefinition
|
ExtraContent = protocoltypes.ExtraContent
|
||||||
|
GoogleExtra = protocoltypes.GoogleExtra
|
||||||
|
)
|
||||||
|
|
||||||
type LLMProvider interface {
|
type LLMProvider interface {
|
||||||
Chat(ctx context.Context, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) (*LLMResponse, error)
|
Chat(
|
||||||
|
ctx context.Context,
|
||||||
|
messages []Message,
|
||||||
|
tools []ToolDefinition,
|
||||||
|
model string,
|
||||||
|
options map[string]any,
|
||||||
|
) (*LLMResponse, error)
|
||||||
GetDefaultModel() string
|
GetDefaultModel() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FailoverReason classifies why an LLM request failed for fallback decisions.
|
||||||
type FailoverReason string
|
type FailoverReason string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
FailoverAuth FailoverReason = "auth"
|
||||||
|
FailoverRateLimit FailoverReason = "rate_limit"
|
||||||
|
FailoverBilling FailoverReason = "billing"
|
||||||
FailoverTimeout FailoverReason = "timeout"
|
FailoverTimeout FailoverReason = "timeout"
|
||||||
FailoverStatus FailoverReason = "status"
|
|
||||||
FailoverEmpty FailoverReason = "empty"
|
|
||||||
FailoverFormat FailoverReason = "format"
|
FailoverFormat FailoverReason = "format"
|
||||||
FailoverOverloaded FailoverReason = "overloaded"
|
FailoverOverloaded FailoverReason = "overloaded"
|
||||||
FailoverAuth FailoverReason = "auth"
|
|
||||||
FailoverBilling FailoverReason = "billing"
|
|
||||||
FailoverRateLimit FailoverReason = "rate_limit"
|
|
||||||
FailoverContextWindow FailoverReason = "context_window"
|
|
||||||
FailoverUnknown FailoverReason = "unknown"
|
FailoverUnknown FailoverReason = "unknown"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue