2026-08-21 15:58:46 -07:00
|
|
|
package config
|
|
|
|
|
|
2026-08-21 18:07:12 -07:00
|
|
|
import (
|
2026-09-09 00:31:52 +00:00
|
|
|
"fmt"
|
2026-08-21 18:07:12 -07:00
|
|
|
"os"
|
2026-09-09 00:31:52 +00:00
|
|
|
"strconv"
|
2026-08-21 21:51:41 -07:00
|
|
|
"strings"
|
2026-08-21 18:07:12 -07:00
|
|
|
)
|
|
|
|
|
|
2026-09-09 00:31:52 +00:00
|
|
|
// Environment represents the deployment environment.
|
|
|
|
|
type Environment string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
EnvDev Environment = "dev"
|
|
|
|
|
EnvStaging Environment = "staging"
|
|
|
|
|
EnvProd Environment = "prod"
|
|
|
|
|
)
|
|
|
|
|
|
2026-08-21 18:07:12 -07:00
|
|
|
// TemporalConfig holds Temporal cluster configuration.
|
|
|
|
|
type TemporalConfig struct {
|
2026-09-09 00:31:52 +00:00
|
|
|
HostPort string // env: TEMPORAL_HOSTPORT
|
|
|
|
|
Namespace string // env: TEMPORAL_NAMESPACE
|
|
|
|
|
TLSCert string // env: TEMPORAL_TLS_CERT (file path)
|
|
|
|
|
TLSKey string // env: TEMPORAL_TLS_KEY (file path)
|
|
|
|
|
TaskQueue string // env: TEMPORAL_TASK_QUEUE
|
|
|
|
|
WorkerCount int // env: TEMPORAL_WORKER_COUNT
|
2026-08-21 18:07:12 -07:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 00:31:52 +00:00
|
|
|
// MemoryServiceConfig holds memory service connection settings.
|
|
|
|
|
type MemoryServiceConfig struct {
|
|
|
|
|
URL string // env: MEMORY_SERVICE_URL
|
|
|
|
|
JWTToken string // env: MEMORY_SERVICE_JWT_TOKEN
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LLMConfig holds LLM provider settings.
|
|
|
|
|
type LLMConfig struct {
|
|
|
|
|
BaseURL string // env: LOCAL_LLM_BASE_URL
|
|
|
|
|
AnthropicKey string // env: ANTHROPIC_API_KEY
|
|
|
|
|
AuthToken string // env: LLM_AUTH_TOKEN
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppConfig holds all application configuration.
|
2026-08-21 18:07:12 -07:00
|
|
|
type AppConfig struct {
|
2026-09-09 00:31:52 +00:00
|
|
|
Env Environment
|
|
|
|
|
Temporal TemporalConfig
|
|
|
|
|
MemoryService MemoryServiceConfig
|
|
|
|
|
LLM LLMConfig
|
|
|
|
|
LogLevel string // env: LOG_LEVEL
|
2026-08-21 18:07:12 -07:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 00:31:52 +00:00
|
|
|
// LoadConfig loads configuration from environment variables with validation.
|
2026-08-21 18:07:12 -07:00
|
|
|
func LoadConfig() (AppConfig, error) {
|
|
|
|
|
cfg := AppConfig{
|
2026-09-09 00:31:52 +00:00
|
|
|
Env: parseEnv(getEnvOrDefault("APP_ENV", "dev")),
|
2026-08-21 18:07:12 -07:00
|
|
|
Temporal: TemporalConfig{
|
2026-09-09 00:31:52 +00:00
|
|
|
HostPort: addDefaultPort(getEnvOrDefault("TEMPORAL_HOSTPORT", defaultTemporalHost())),
|
|
|
|
|
Namespace: getEnvOrDefault("TEMPORAL_NAMESPACE", "poimen-harness"),
|
|
|
|
|
TLSCert: os.Getenv("TEMPORAL_TLS_CERT"),
|
|
|
|
|
TLSKey: os.Getenv("TEMPORAL_TLS_KEY"),
|
|
|
|
|
TaskQueue: getEnvOrDefault("TEMPORAL_TASK_QUEUE", "poimen-taskqueue"),
|
|
|
|
|
WorkerCount: getEnvIntOrDefault("TEMPORAL_WORKER_COUNT", 10),
|
2026-08-21 18:07:12 -07:00
|
|
|
},
|
2026-09-09 00:31:52 +00:00
|
|
|
MemoryService: MemoryServiceConfig{
|
|
|
|
|
URL: os.Getenv("MEMORY_SERVICE_URL"),
|
|
|
|
|
JWTToken: os.Getenv("MEMORY_SERVICE_JWT_TOKEN"),
|
|
|
|
|
},
|
|
|
|
|
LLM: LLMConfig{
|
|
|
|
|
BaseURL: os.Getenv("LOCAL_LLM_BASE_URL"),
|
|
|
|
|
AnthropicKey: os.Getenv("ANTHROPIC_API_KEY"),
|
|
|
|
|
AuthToken: os.Getenv("LLM_AUTH_TOKEN"),
|
|
|
|
|
},
|
|
|
|
|
LogLevel: getEnvOrDefault("LOG_LEVEL", "info"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := cfg.Validate(); err != nil {
|
|
|
|
|
return AppConfig{}, err
|
2026-08-21 18:07:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cfg, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-09 00:31:52 +00:00
|
|
|
// Validate checks required fields and consistency.
|
|
|
|
|
func (c *AppConfig) Validate() error {
|
|
|
|
|
if c.Temporal.HostPort == "" {
|
|
|
|
|
return fmt.Errorf("TEMPORAL_HOSTPORT is required")
|
|
|
|
|
}
|
|
|
|
|
if c.Temporal.Namespace == "" {
|
|
|
|
|
return fmt.Errorf("TEMPORAL_NAMESPACE is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TLS: both or neither
|
|
|
|
|
hasCert := c.Temporal.TLSCert != ""
|
|
|
|
|
hasKey := c.Temporal.TLSKey != ""
|
|
|
|
|
if hasCert != hasKey {
|
|
|
|
|
return fmt.Errorf("TEMPORAL_TLS_CERT and TEMPORAL_TLS_KEY must both be set or both empty")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate TLS files exist if specified
|
|
|
|
|
if hasCert {
|
|
|
|
|
if _, err := os.Stat(c.Temporal.TLSCert); err != nil {
|
|
|
|
|
return fmt.Errorf("TEMPORAL_TLS_CERT file not found: %s", c.Temporal.TLSCert)
|
|
|
|
|
}
|
|
|
|
|
if _, err := os.Stat(c.Temporal.TLSKey); err != nil {
|
|
|
|
|
return fmt.Errorf("TEMPORAL_TLS_KEY file not found: %s", c.Temporal.TLSKey)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prod requires LLM key
|
|
|
|
|
if c.Env == EnvProd {
|
|
|
|
|
if c.LLM.AnthropicKey == "" && c.LLM.AuthToken == "" {
|
|
|
|
|
return fmt.Errorf("prod requires ANTHROPIC_API_KEY or LLM_AUTH_TOKEN")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsProd returns true if running in production.
|
|
|
|
|
func (c *AppConfig) IsProd() bool { return c.Env == EnvProd }
|
|
|
|
|
|
|
|
|
|
// IsDevOrStaging returns true if running in dev or staging.
|
|
|
|
|
func (c *AppConfig) IsDevOrStaging() bool { return c.Env == EnvDev || c.Env == EnvStaging }
|
|
|
|
|
|
|
|
|
|
func defaultTemporalHost() string {
|
|
|
|
|
// In-cluster default vs local
|
|
|
|
|
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
|
|
|
|
|
return "temporal-frontend.temporal.svc.cluster.local:7233"
|
|
|
|
|
}
|
|
|
|
|
return "127.0.0.1:7233"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseEnv(s string) Environment {
|
|
|
|
|
switch strings.ToLower(s) {
|
|
|
|
|
case "prod", "production":
|
|
|
|
|
return EnvProd
|
|
|
|
|
case "staging", "stage":
|
|
|
|
|
return EnvStaging
|
|
|
|
|
default:
|
|
|
|
|
return EnvDev
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-21 18:07:12 -07:00
|
|
|
func getEnvOrDefault(key, defaultVal string) string {
|
|
|
|
|
if val := os.Getenv(key); val != "" {
|
|
|
|
|
return val
|
|
|
|
|
}
|
|
|
|
|
return defaultVal
|
|
|
|
|
}
|
2026-08-21 21:51:41 -07:00
|
|
|
|
2026-09-09 00:31:52 +00:00
|
|
|
func getEnvIntOrDefault(key string, defaultVal int) int {
|
|
|
|
|
if val := os.Getenv(key); val != "" {
|
|
|
|
|
if i, err := strconv.Atoi(val); err == nil {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return defaultVal
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-21 21:51:41 -07:00
|
|
|
func addDefaultPort(hostPort string) string {
|
|
|
|
|
if !strings.Contains(hostPort, ":") {
|
|
|
|
|
return hostPort + ":7233"
|
|
|
|
|
}
|
|
|
|
|
return hostPort
|
|
|
|
|
}
|