From 598971e282b97557a43cd637e63243a7bdf4bd94 Mon Sep 17 00:00:00 2001 From: Kohei Date: Sat, 28 Feb 2026 00:29:09 +0900 Subject: [PATCH] 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 --- README.ja.md | 1 - README.md | 1 - config/config.example.json | 109 ---------------------------- pkg/config/config.go | 144 ++++++++++++++++++------------------- pkg/gateway/schema.go | 24 ++----- pkg/gateway/server_test.go | 65 +++++------------ 6 files changed, 94 insertions(+), 250 deletions(-) delete mode 100644 config/config.example.json diff --git a/README.ja.md b/README.ja.md index 489484f90..d28f05dc2 100644 --- a/README.ja.md +++ b/README.ja.md @@ -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 diff --git a/README.md b/README.md index 2ef5cd28d..420a95494 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/config/config.example.json b/config/config.example.json deleted file mode 100644 index cb52cd983..000000000 --- a/config/config.example.json +++ /dev/null @@ -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 - } -} diff --git a/pkg/config/config.go b/pkg/config/config.go index 9a69f4d86..ef2b65eae 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -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 { diff --git a/pkg/gateway/schema.go b/pkg/gateway/schema.go index f57a0ee65..a41e334ae 100644 --- a/pkg/gateway/schema.go +++ b/pkg/gateway/schema.go @@ -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") } diff --git a/pkg/gateway/server_test.go b/pkg/gateway/server_test.go index 0f8970d91..4dbfd7d0c 100644 --- a/pkg/gateway/server_test.go +++ b/pkg/gateway/server_test.go @@ -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 ---