refactor: replace snakeToTitle with explicit label struct tags
Use label:"..." tags on all exported config fields so that schema labels are defined alongside the structs instead of being derived at runtime. Remove snakeToTitle, the acronyms map, and config.example.json (now generated by DefaultConfig via onboard). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
df3035aa14
commit
598971e282
6 changed files with 94 additions and 250 deletions
|
|
@ -510,7 +510,6 @@ clawdroid/
|
|||
│ ├── core/ # 共有コア (data, domain, model, ui)
|
||||
│ └── feature/ # 機能モジュール (chat, settings)
|
||||
├── workspace/ # テンプレートファイル (IDENTITY.md, SOUL.md 等)
|
||||
├── config/ # config.example.json
|
||||
├── Makefile
|
||||
├── go.mod
|
||||
└── .goreleaser.yaml
|
||||
|
|
|
|||
|
|
@ -512,7 +512,6 @@ clawdroid/
|
|||
│ ├── core/ # Shared core (data, domain, model, ui)
|
||||
│ └── feature/ # Feature modules (chat, settings)
|
||||
├── workspace/ # Template files (IDENTITY.md, SOUL.md, etc.)
|
||||
├── config/ # config.example.json
|
||||
├── Makefile
|
||||
├── go.mod
|
||||
└── .goreleaser.yaml
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
{
|
||||
"llm": {
|
||||
"model": "",
|
||||
"api_key": "",
|
||||
"base_url": ""
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"workspace": "~/.clawdroid/workspace",
|
||||
"data_dir": "~/.clawdroid/data",
|
||||
"restrict_to_workspace": true,
|
||||
"max_tokens": 8192,
|
||||
"context_window": 128000,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 10
|
||||
}
|
||||
},
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"token": "YOUR_TELEGRAM_BOT_TOKEN",
|
||||
"proxy": "",
|
||||
"allow_from": ["YOUR_USER_ID"]
|
||||
},
|
||||
"discord": {
|
||||
"enabled": false,
|
||||
"token": "YOUR_DISCORD_BOT_TOKEN",
|
||||
"allow_from": []
|
||||
},
|
||||
"whatsapp": {
|
||||
"enabled": false,
|
||||
"bridge_url": "ws://localhost:3001",
|
||||
"allow_from": []
|
||||
},
|
||||
"slack": {
|
||||
"enabled": false,
|
||||
"bot_token": "xoxb-YOUR-BOT-TOKEN",
|
||||
"app_token": "xapp-YOUR-APP-TOKEN",
|
||||
"allow_from": []
|
||||
},
|
||||
"line": {
|
||||
"enabled": false,
|
||||
"channel_secret": "YOUR_LINE_CHANNEL_SECRET",
|
||||
"channel_access_token": "YOUR_LINE_CHANNEL_ACCESS_TOKEN",
|
||||
"webhook_host": "127.0.0.1",
|
||||
"webhook_port": 18791,
|
||||
"webhook_path": "/webhook/line",
|
||||
"allow_from": []
|
||||
},
|
||||
"websocket": {
|
||||
"enabled": true,
|
||||
"host": "127.0.0.1",
|
||||
"port": 18793,
|
||||
"path": "/ws",
|
||||
"allow_from": []
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"exec": {
|
||||
"enabled": false
|
||||
},
|
||||
"android": {
|
||||
"enabled": true
|
||||
},
|
||||
"memory": {
|
||||
"enabled": true
|
||||
},
|
||||
"web": {
|
||||
"brave": {
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
}
|
||||
},
|
||||
"mcp": {
|
||||
"example-stdio": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@example/mcp-server"],
|
||||
"description": "Example local MCP server (stdio)",
|
||||
"enabled": false
|
||||
},
|
||||
"example-http": {
|
||||
"url": "https://mcp.example.com/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer YOUR_TOKEN"
|
||||
},
|
||||
"description": "Example remote MCP server (HTTP)",
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"heartbeat": {
|
||||
"enabled": true,
|
||||
"interval": 30
|
||||
},
|
||||
"gateway": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 18790,
|
||||
"api_key": ""
|
||||
},
|
||||
"rate_limits": {
|
||||
"max_tool_calls_per_minute": 30,
|
||||
"max_requests_per_minute": 15
|
||||
}
|
||||
}
|
||||
|
|
@ -44,123 +44,123 @@ func (f *FlexibleStringSlice) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
type LLMConfig struct {
|
||||
Model string `json:"model" env:"CLAWDROID_LLM_MODEL"`
|
||||
APIKey string `json:"api_key" env:"CLAWDROID_LLM_API_KEY"`
|
||||
BaseURL string `json:"base_url" env:"CLAWDROID_LLM_BASE_URL"`
|
||||
Model string `json:"model" label:"Model" env:"CLAWDROID_LLM_MODEL"`
|
||||
APIKey string `json:"api_key" label:"API Key" env:"CLAWDROID_LLM_API_KEY"`
|
||||
BaseURL string `json:"base_url" label:"Base URL" env:"CLAWDROID_LLM_BASE_URL"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
LLM LLMConfig `json:"llm"`
|
||||
Agents AgentsConfig `json:"agents"`
|
||||
Channels ChannelsConfig `json:"channels"`
|
||||
Gateway GatewayConfig `json:"gateway"`
|
||||
Tools ToolsConfig `json:"tools"`
|
||||
Heartbeat HeartbeatConfig `json:"heartbeat"`
|
||||
RateLimits RateLimitsConfig `json:"rate_limits"`
|
||||
LLM LLMConfig `json:"llm" label:"LLM"`
|
||||
Agents AgentsConfig `json:"agents" label:"Agent Defaults"`
|
||||
Channels ChannelsConfig `json:"channels" label:"Messaging Channels"`
|
||||
Gateway GatewayConfig `json:"gateway" label:"Gateway"`
|
||||
Tools ToolsConfig `json:"tools" label:"Tool Settings"`
|
||||
Heartbeat HeartbeatConfig `json:"heartbeat" label:"Heartbeat"`
|
||||
RateLimits RateLimitsConfig `json:"rate_limits" label:"Rate Limits"`
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
type AgentsConfig struct {
|
||||
Defaults AgentDefaults `json:"defaults"`
|
||||
Defaults AgentDefaults `json:"defaults" label:"Defaults"`
|
||||
}
|
||||
|
||||
type AgentDefaults struct {
|
||||
Workspace string `json:"workspace" env:"CLAWDROID_AGENTS_DEFAULTS_WORKSPACE"`
|
||||
DataDir string `json:"data_dir" env:"CLAWDROID_AGENTS_DEFAULTS_DATA_DIR"`
|
||||
RestrictToWorkspace bool `json:"restrict_to_workspace" env:"CLAWDROID_AGENTS_DEFAULTS_RESTRICT_TO_WORKSPACE"`
|
||||
MaxTokens int `json:"max_tokens" env:"CLAWDROID_AGENTS_DEFAULTS_MAX_TOKENS"`
|
||||
ContextWindow int `json:"context_window" env:"CLAWDROID_AGENTS_DEFAULTS_CONTEXT_WINDOW"`
|
||||
Temperature float64 `json:"temperature" env:"CLAWDROID_AGENTS_DEFAULTS_TEMPERATURE"`
|
||||
MaxToolIterations int `json:"max_tool_iterations" env:"CLAWDROID_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"`
|
||||
Workspace string `json:"workspace" label:"Workspace" env:"CLAWDROID_AGENTS_DEFAULTS_WORKSPACE"`
|
||||
DataDir string `json:"data_dir" label:"Data Directory" env:"CLAWDROID_AGENTS_DEFAULTS_DATA_DIR"`
|
||||
RestrictToWorkspace bool `json:"restrict_to_workspace" label:"Restrict to Workspace" env:"CLAWDROID_AGENTS_DEFAULTS_RESTRICT_TO_WORKSPACE"`
|
||||
MaxTokens int `json:"max_tokens" label:"Max Tokens" env:"CLAWDROID_AGENTS_DEFAULTS_MAX_TOKENS"`
|
||||
ContextWindow int `json:"context_window" label:"Context Window" env:"CLAWDROID_AGENTS_DEFAULTS_CONTEXT_WINDOW"`
|
||||
Temperature float64 `json:"temperature" label:"Temperature" env:"CLAWDROID_AGENTS_DEFAULTS_TEMPERATURE"`
|
||||
MaxToolIterations int `json:"max_tool_iterations" label:"Max Tool Iterations" env:"CLAWDROID_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"`
|
||||
}
|
||||
|
||||
type ChannelsConfig struct {
|
||||
WhatsApp WhatsAppConfig `json:"whatsapp"`
|
||||
Telegram TelegramConfig `json:"telegram"`
|
||||
Discord DiscordConfig `json:"discord"`
|
||||
Slack SlackConfig `json:"slack"`
|
||||
LINE LINEConfig `json:"line"`
|
||||
WebSocket WebSocketConfig `json:"websocket"`
|
||||
WhatsApp WhatsAppConfig `json:"whatsapp" label:"WhatsApp"`
|
||||
Telegram TelegramConfig `json:"telegram" label:"Telegram"`
|
||||
Discord DiscordConfig `json:"discord" label:"Discord"`
|
||||
Slack SlackConfig `json:"slack" label:"Slack"`
|
||||
LINE LINEConfig `json:"line" label:"LINE"`
|
||||
WebSocket WebSocketConfig `json:"websocket" label:"WebSocket"`
|
||||
}
|
||||
|
||||
type WhatsAppConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_CHANNELS_WHATSAPP_ENABLED"`
|
||||
BridgeURL string `json:"bridge_url" env:"CLAWDROID_CHANNELS_WHATSAPP_BRIDGE_URL"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"CLAWDROID_CHANNELS_WHATSAPP_ALLOW_FROM"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_CHANNELS_WHATSAPP_ENABLED"`
|
||||
BridgeURL string `json:"bridge_url" label:"Bridge URL" env:"CLAWDROID_CHANNELS_WHATSAPP_BRIDGE_URL"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" label:"Allow From" env:"CLAWDROID_CHANNELS_WHATSAPP_ALLOW_FROM"`
|
||||
}
|
||||
|
||||
type TelegramConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_CHANNELS_TELEGRAM_ENABLED"`
|
||||
Token string `json:"token" env:"CLAWDROID_CHANNELS_TELEGRAM_TOKEN"`
|
||||
Proxy string `json:"proxy" env:"CLAWDROID_CHANNELS_TELEGRAM_PROXY"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"CLAWDROID_CHANNELS_TELEGRAM_ALLOW_FROM"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_CHANNELS_TELEGRAM_ENABLED"`
|
||||
Token string `json:"token" label:"Token" env:"CLAWDROID_CHANNELS_TELEGRAM_TOKEN"`
|
||||
Proxy string `json:"proxy" label:"Proxy" env:"CLAWDROID_CHANNELS_TELEGRAM_PROXY"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" label:"Allow From" env:"CLAWDROID_CHANNELS_TELEGRAM_ALLOW_FROM"`
|
||||
}
|
||||
|
||||
type DiscordConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_CHANNELS_DISCORD_ENABLED"`
|
||||
Token string `json:"token" env:"CLAWDROID_CHANNELS_DISCORD_TOKEN"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"CLAWDROID_CHANNELS_DISCORD_ALLOW_FROM"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_CHANNELS_DISCORD_ENABLED"`
|
||||
Token string `json:"token" label:"Token" env:"CLAWDROID_CHANNELS_DISCORD_TOKEN"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" label:"Allow From" env:"CLAWDROID_CHANNELS_DISCORD_ALLOW_FROM"`
|
||||
}
|
||||
|
||||
type SlackConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_CHANNELS_SLACK_ENABLED"`
|
||||
BotToken string `json:"bot_token" env:"CLAWDROID_CHANNELS_SLACK_BOT_TOKEN"`
|
||||
AppToken string `json:"app_token" env:"CLAWDROID_CHANNELS_SLACK_APP_TOKEN"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"CLAWDROID_CHANNELS_SLACK_ALLOW_FROM"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_CHANNELS_SLACK_ENABLED"`
|
||||
BotToken string `json:"bot_token" label:"Bot Token" env:"CLAWDROID_CHANNELS_SLACK_BOT_TOKEN"`
|
||||
AppToken string `json:"app_token" label:"App Token" env:"CLAWDROID_CHANNELS_SLACK_APP_TOKEN"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" label:"Allow From" env:"CLAWDROID_CHANNELS_SLACK_ALLOW_FROM"`
|
||||
}
|
||||
|
||||
type LINEConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_CHANNELS_LINE_ENABLED"`
|
||||
ChannelSecret string `json:"channel_secret" env:"CLAWDROID_CHANNELS_LINE_CHANNEL_SECRET"`
|
||||
ChannelAccessToken string `json:"channel_access_token" env:"CLAWDROID_CHANNELS_LINE_CHANNEL_ACCESS_TOKEN"`
|
||||
WebhookHost string `json:"webhook_host" env:"CLAWDROID_CHANNELS_LINE_WEBHOOK_HOST"`
|
||||
WebhookPort int `json:"webhook_port" env:"CLAWDROID_CHANNELS_LINE_WEBHOOK_PORT"`
|
||||
WebhookPath string `json:"webhook_path" env:"CLAWDROID_CHANNELS_LINE_WEBHOOK_PATH"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"CLAWDROID_CHANNELS_LINE_ALLOW_FROM"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_CHANNELS_LINE_ENABLED"`
|
||||
ChannelSecret string `json:"channel_secret" label:"Channel Secret" env:"CLAWDROID_CHANNELS_LINE_CHANNEL_SECRET"`
|
||||
ChannelAccessToken string `json:"channel_access_token" label:"Channel Access Token" env:"CLAWDROID_CHANNELS_LINE_CHANNEL_ACCESS_TOKEN"`
|
||||
WebhookHost string `json:"webhook_host" label:"Webhook Host" env:"CLAWDROID_CHANNELS_LINE_WEBHOOK_HOST"`
|
||||
WebhookPort int `json:"webhook_port" label:"Webhook Port" env:"CLAWDROID_CHANNELS_LINE_WEBHOOK_PORT"`
|
||||
WebhookPath string `json:"webhook_path" label:"Webhook Path" env:"CLAWDROID_CHANNELS_LINE_WEBHOOK_PATH"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" label:"Allow From" env:"CLAWDROID_CHANNELS_LINE_ALLOW_FROM"`
|
||||
}
|
||||
|
||||
type WebSocketConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_CHANNELS_WEBSOCKET_ENABLED"`
|
||||
Host string `json:"host" env:"CLAWDROID_CHANNELS_WEBSOCKET_HOST"`
|
||||
Port int `json:"port" env:"CLAWDROID_CHANNELS_WEBSOCKET_PORT"`
|
||||
Path string `json:"path" env:"CLAWDROID_CHANNELS_WEBSOCKET_PATH"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" env:"CLAWDROID_CHANNELS_WEBSOCKET_ALLOW_FROM"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_CHANNELS_WEBSOCKET_ENABLED"`
|
||||
Host string `json:"host" label:"Host" env:"CLAWDROID_CHANNELS_WEBSOCKET_HOST"`
|
||||
Port int `json:"port" label:"Port" env:"CLAWDROID_CHANNELS_WEBSOCKET_PORT"`
|
||||
Path string `json:"path" label:"Path" env:"CLAWDROID_CHANNELS_WEBSOCKET_PATH"`
|
||||
AllowFrom FlexibleStringSlice `json:"allow_from" label:"Allow From" env:"CLAWDROID_CHANNELS_WEBSOCKET_ALLOW_FROM"`
|
||||
}
|
||||
|
||||
type HeartbeatConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_HEARTBEAT_ENABLED"`
|
||||
Interval int `json:"interval" env:"CLAWDROID_HEARTBEAT_INTERVAL"` // minutes, min 5
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_HEARTBEAT_ENABLED"`
|
||||
Interval int `json:"interval" label:"Interval" env:"CLAWDROID_HEARTBEAT_INTERVAL"` // minutes, min 5
|
||||
}
|
||||
|
||||
type RateLimitsConfig struct {
|
||||
MaxToolCallsPerMinute int `json:"max_tool_calls_per_minute" env:"CLAWDROID_RATE_LIMITS_MAX_TOOL_CALLS_PER_MINUTE"` // 0 = unlimited
|
||||
MaxRequestsPerMinute int `json:"max_requests_per_minute" env:"CLAWDROID_RATE_LIMITS_MAX_REQUESTS_PER_MINUTE"` // 0 = unlimited
|
||||
MaxToolCallsPerMinute int `json:"max_tool_calls_per_minute" label:"Max Tool Calls Per Minute" env:"CLAWDROID_RATE_LIMITS_MAX_TOOL_CALLS_PER_MINUTE"` // 0 = unlimited
|
||||
MaxRequestsPerMinute int `json:"max_requests_per_minute" label:"Max Requests Per Minute" env:"CLAWDROID_RATE_LIMITS_MAX_REQUESTS_PER_MINUTE"` // 0 = unlimited
|
||||
}
|
||||
|
||||
type GatewayConfig struct {
|
||||
Host string `json:"host" env:"CLAWDROID_GATEWAY_HOST"`
|
||||
Port int `json:"port" env:"CLAWDROID_GATEWAY_PORT"`
|
||||
APIKey string `json:"api_key" env:"CLAWDROID_GATEWAY_API_KEY"`
|
||||
Host string `json:"host" label:"Host" env:"CLAWDROID_GATEWAY_HOST"`
|
||||
Port int `json:"port" label:"Port" env:"CLAWDROID_GATEWAY_PORT"`
|
||||
APIKey string `json:"api_key" label:"API Key" env:"CLAWDROID_GATEWAY_API_KEY"`
|
||||
}
|
||||
|
||||
type BraveConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_TOOLS_WEB_BRAVE_ENABLED"`
|
||||
APIKey string `json:"api_key" env:"CLAWDROID_TOOLS_WEB_BRAVE_API_KEY"`
|
||||
MaxResults int `json:"max_results" env:"CLAWDROID_TOOLS_WEB_BRAVE_MAX_RESULTS"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_TOOLS_WEB_BRAVE_ENABLED"`
|
||||
APIKey string `json:"api_key" label:"API Key" env:"CLAWDROID_TOOLS_WEB_BRAVE_API_KEY"`
|
||||
MaxResults int `json:"max_results" label:"Max Results" env:"CLAWDROID_TOOLS_WEB_BRAVE_MAX_RESULTS"`
|
||||
}
|
||||
|
||||
type DuckDuckGoConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_TOOLS_WEB_DUCKDUCKGO_ENABLED"`
|
||||
MaxResults int `json:"max_results" env:"CLAWDROID_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_TOOLS_WEB_DUCKDUCKGO_ENABLED"`
|
||||
MaxResults int `json:"max_results" label:"Max Results" env:"CLAWDROID_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"`
|
||||
}
|
||||
|
||||
type WebToolsConfig struct {
|
||||
Brave BraveConfig `json:"brave"`
|
||||
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"`
|
||||
Brave BraveConfig `json:"brave" label:"Brave Search"`
|
||||
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo" label:"DuckDuckGo"`
|
||||
}
|
||||
|
||||
type ExecToolsConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_TOOLS_EXEC_ENABLED"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_TOOLS_EXEC_ENABLED"`
|
||||
}
|
||||
|
||||
type MCPServerConfig struct {
|
||||
|
|
@ -178,19 +178,19 @@ type MCPServerConfig struct {
|
|||
}
|
||||
|
||||
type AndroidToolsConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_TOOLS_ANDROID_ENABLED"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_TOOLS_ANDROID_ENABLED"`
|
||||
}
|
||||
|
||||
type MemoryToolsConfig struct {
|
||||
Enabled bool `json:"enabled" env:"CLAWDROID_TOOLS_MEMORY_ENABLED"`
|
||||
Enabled bool `json:"enabled" label:"Enabled" env:"CLAWDROID_TOOLS_MEMORY_ENABLED"`
|
||||
}
|
||||
|
||||
type ToolsConfig struct {
|
||||
Web WebToolsConfig `json:"web"`
|
||||
Exec ExecToolsConfig `json:"exec"`
|
||||
Android AndroidToolsConfig `json:"android"`
|
||||
Memory MemoryToolsConfig `json:"memory"`
|
||||
MCP map[string]MCPServerConfig `json:"mcp,omitempty"`
|
||||
Web WebToolsConfig `json:"web" label:"Web Search"`
|
||||
Exec ExecToolsConfig `json:"exec" label:"Shell Exec"`
|
||||
Android AndroidToolsConfig `json:"android" label:"Android"`
|
||||
Memory MemoryToolsConfig `json:"memory" label:"Memory"`
|
||||
MCP map[string]MCPServerConfig `json:"mcp,omitempty" label:"MCP Servers"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
|
|
|
|||
|
|
@ -28,12 +28,6 @@ type SchemaResponse struct {
|
|||
Sections []SchemaSection `json:"sections"`
|
||||
}
|
||||
|
||||
// acronyms maps lowercase abbreviations to their uppercase forms for label generation.
|
||||
var acronyms = map[string]string{
|
||||
"api": "API", "llm": "LLM", "url": "URL", "ws": "WS",
|
||||
"id": "ID", "mcp": "MCP",
|
||||
}
|
||||
|
||||
// secretKeys lists JSON keys that contain sensitive values.
|
||||
var secretKeys = map[string]bool{
|
||||
"api_key": true,
|
||||
|
|
@ -72,7 +66,7 @@ func BuildSchema(defaultCfg *config.Config) SchemaResponse {
|
|||
|
||||
section := SchemaSection{
|
||||
Key: jsonTag,
|
||||
Label: snakeToTitle(jsonTag),
|
||||
Label: labelTag(field),
|
||||
}
|
||||
|
||||
fieldVal := cfgVal.Field(i)
|
||||
|
|
@ -145,7 +139,7 @@ func buildFields(t reflect.Type, v reflect.Value, prefix string) []SchemaField {
|
|||
|
||||
fields = append(fields, SchemaField{
|
||||
Key: fullKey,
|
||||
Label: snakeToTitle(jk),
|
||||
Label: labelTag(sf),
|
||||
Type: schemaType,
|
||||
Secret: secretKeys[jk],
|
||||
Default: defVal,
|
||||
|
|
@ -195,15 +189,7 @@ func jsonKey(f reflect.StructField) string {
|
|||
return parts[0]
|
||||
}
|
||||
|
||||
// snakeToTitle converts a snake_case string to Title Case, applying acronym rules.
|
||||
func snakeToTitle(s string) string {
|
||||
parts := strings.Split(s, "_")
|
||||
for i, p := range parts {
|
||||
if upper, ok := acronyms[strings.ToLower(p)]; ok {
|
||||
parts[i] = upper
|
||||
} else if len(p) > 0 {
|
||||
parts[i] = strings.ToUpper(p[:1]) + p[1:]
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
// labelTag reads the "label" struct tag from a field.
|
||||
func labelTag(f reflect.StructField) string {
|
||||
return f.Tag.Get("label")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,20 +196,22 @@ func TestBuildSchema_SecretFlag(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBuildSchema_Labels(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"api_key", "API Key"},
|
||||
{"base_url", "Base URL"},
|
||||
{"max_tokens", "Max Tokens"},
|
||||
{"bot_token", "Bot Token"},
|
||||
schema := BuildSchema(config.DefaultConfig())
|
||||
|
||||
wantLabels := map[string]string{
|
||||
"api_key": "API Key",
|
||||
"base_url": "Base URL",
|
||||
"defaults.max_tokens": "Max Tokens",
|
||||
"slack.bot_token": "Bot Token",
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
got := snakeToTitle(tc.input)
|
||||
if got != tc.want {
|
||||
t.Errorf("snakeToTitle(%q) = %q, want %q", tc.input, got, tc.want)
|
||||
for _, sec := range schema.Sections {
|
||||
for _, f := range sec.Fields {
|
||||
if want, ok := wantLabels[f.Key]; ok {
|
||||
if f.Label != want {
|
||||
t.Errorf("field %q label = %q, want %q", f.Key, f.Label, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1177,7 +1179,10 @@ func TestBuildSchema_SectionLabels(t *testing.T) {
|
|||
|
||||
wantLabels := map[string]string{
|
||||
"llm": "LLM",
|
||||
"agents": "Agent Defaults",
|
||||
"channels": "Messaging Channels",
|
||||
"gateway": "Gateway",
|
||||
"tools": "Tool Settings",
|
||||
"heartbeat": "Heartbeat",
|
||||
"rate_limits": "Rate Limits",
|
||||
}
|
||||
|
|
@ -2367,42 +2372,6 @@ func TestBuildSchema_GatewaySectionFields(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- #31: snakeToTitle with long compound words ---
|
||||
|
||||
func TestSnakeToTitle_LongCompound(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"channel_access_token", "Channel Access Token"},
|
||||
{"max_tool_calls_per_minute", "Max Tool Calls Per Minute"},
|
||||
{"restrict_to_workspace", "Restrict To Workspace"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := snakeToTitle(tc.input)
|
||||
if got != tc.want {
|
||||
t.Errorf("snakeToTitle(%q) = %q, want %q", tc.input, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- #32: snakeToTitle with empty string ---
|
||||
|
||||
func TestSnakeToTitle_EmptyString(t *testing.T) {
|
||||
got := snakeToTitle("")
|
||||
if got != "" {
|
||||
t.Errorf("snakeToTitle(\"\") = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
// --- #33: snakeToTitle with "mcp" acronym ---
|
||||
|
||||
func TestSnakeToTitle_MCPAcronym(t *testing.T) {
|
||||
got := snakeToTitle("mcp")
|
||||
if got != "MCP" {
|
||||
t.Errorf("snakeToTitle(\"mcp\") = %q, want %q", got, "MCP")
|
||||
}
|
||||
}
|
||||
|
||||
// --- #34: goTypeToSchema unknown type ---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue