85 lines
2.7 KiB
Go
85 lines
2.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
// RoutingConfig holds the runtime configuration for the routing pod.
|
|
// Separate from Config because the routing pod's surface differs from the supervisor's.
|
|
type RoutingConfig struct {
|
|
Port string // ROUTING_PORT, default 3210
|
|
MCPAuthToken string // ROUTING_MCP_TOKEN, optional bearer token
|
|
LiteLLMBaseURL string // LITELLM_BASE_URL, default http://piguard:4000
|
|
LiteLLMAPIKey string // LITELLM_API_KEY
|
|
BrainURL string // BRAIN_URL, default http://ingestion.supervisor:3300
|
|
LocalModel string // HYPERGUILD_LOCAL_MODEL, default qwen35
|
|
ClaudeModel string // HYPERGUILD_CLAUDE_MODEL, default claude-sonnet-4-6
|
|
// RouteLocalFloor and RouteLocalCeil intentionally invert the usual
|
|
// floor < ceil mathematical convention: Floor (default 0.90) is the
|
|
// UPPER boundary — at/above it, always route local; Ceil (default 0.70)
|
|
// is the LOWER boundary — below it, always route Claude. The band in
|
|
// between is the 50/50 sample zone. The naming follows the spec's policy
|
|
// vocabulary; see internal/routing/policy.go for the consumer.
|
|
RouteLocalFloor float64 // HYPERGUILD_ROUTE_LOCAL_FLOOR, default 0.90
|
|
RouteLocalCeil float64 // HYPERGUILD_ROUTE_LOCAL_CEIL, default 0.70
|
|
PassRateTTLSeconds int // HYPERGUILD_PASS_RATE_TTL_SECONDS, default 60
|
|
}
|
|
|
|
func LoadRouting() (RoutingConfig, error) {
|
|
cfg := RoutingConfig{
|
|
Port: envOr("ROUTING_PORT", "3210"),
|
|
MCPAuthToken: os.Getenv("ROUTING_MCP_TOKEN"),
|
|
LiteLLMBaseURL: envOr("LITELLM_BASE_URL", "http://piguard:4000"),
|
|
LiteLLMAPIKey: os.Getenv("LITELLM_API_KEY"),
|
|
BrainURL: envOr("BRAIN_URL", "http://ingestion.supervisor:3300"),
|
|
LocalModel: envOr("HYPERGUILD_LOCAL_MODEL", "qwen35"),
|
|
ClaudeModel: envOr("HYPERGUILD_CLAUDE_MODEL", "claude-sonnet-4-6"),
|
|
}
|
|
|
|
floor, err := parseFloatEnv("HYPERGUILD_ROUTE_LOCAL_FLOOR", 0.90)
|
|
if err != nil {
|
|
return RoutingConfig{}, err
|
|
}
|
|
cfg.RouteLocalFloor = floor
|
|
|
|
ceil, err := parseFloatEnv("HYPERGUILD_ROUTE_LOCAL_CEIL", 0.70)
|
|
if err != nil {
|
|
return RoutingConfig{}, err
|
|
}
|
|
cfg.RouteLocalCeil = ceil
|
|
|
|
ttl, err := parseIntEnv("HYPERGUILD_PASS_RATE_TTL_SECONDS", 60)
|
|
if err != nil {
|
|
return RoutingConfig{}, err
|
|
}
|
|
cfg.PassRateTTLSeconds = ttl
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func parseFloatEnv(key string, def float64) (float64, error) {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return def, nil
|
|
}
|
|
f, err := strconv.ParseFloat(v, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("config: %s: %w", key, err)
|
|
}
|
|
return f, nil
|
|
}
|
|
|
|
func parseIntEnv(key string, def int) (int, error) {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return def, nil
|
|
}
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("config: %s: %w", key, err)
|
|
}
|
|
return n, nil
|
|
}
|