refactor: rewrite handler-base as Go module
Replace Python handler-base library with Go module providing: - config: environment-based configuration - health: HTTP health/readiness server for k8s probes - natsutil: NATS/JetStream client with msgpack serialization - telemetry: OpenTelemetry tracing and metrics setup - clients: HTTP clients for LLM, embeddings, reranker, STT, TTS - handler: base Handler runner wiring NATS + health + telemetry Implements ADR-0061 Phase 1.
This commit is contained in:
145
config/config.go
Normal file
145
config/config.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// Package config provides environment-based configuration for handler services.
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Settings holds base configuration for all handler services.
|
||||
// Values are loaded from environment variables with sensible defaults.
|
||||
type Settings struct {
|
||||
// Service identification
|
||||
ServiceName string
|
||||
ServiceVersion string
|
||||
ServiceNamespace string
|
||||
DeploymentEnv string
|
||||
|
||||
// NATS configuration
|
||||
NATSURL string
|
||||
NATSUser string
|
||||
NATSPassword string
|
||||
NATSQueueGroup string
|
||||
|
||||
// Redis/Valkey configuration
|
||||
RedisURL string
|
||||
RedisPassword string
|
||||
|
||||
// Milvus configuration
|
||||
MilvusHost string
|
||||
MilvusPort int
|
||||
MilvusCollection string
|
||||
|
||||
// Service endpoints
|
||||
EmbeddingsURL string
|
||||
RerankerURL string
|
||||
LLMURL string
|
||||
TTSURL string
|
||||
STTURL string
|
||||
|
||||
// OpenTelemetry configuration
|
||||
OTELEnabled bool
|
||||
OTELEndpoint string
|
||||
OTELUseHTTP bool
|
||||
|
||||
// HyperDX configuration
|
||||
HyperDXEnabled bool
|
||||
HyperDXAPIKey string
|
||||
HyperDXEndpoint string
|
||||
|
||||
// MLflow configuration
|
||||
MLflowTrackingURI string
|
||||
MLflowExperimentName string
|
||||
MLflowEnabled bool
|
||||
|
||||
// Health check configuration
|
||||
HealthPort int
|
||||
HealthPath string
|
||||
ReadyPath string
|
||||
|
||||
// Timeouts
|
||||
HTTPTimeout time.Duration
|
||||
NATSTimeout time.Duration
|
||||
}
|
||||
|
||||
// Load creates a Settings populated from environment variables with defaults.
|
||||
func Load() *Settings {
|
||||
return &Settings{
|
||||
ServiceName: getEnv("SERVICE_NAME", "handler"),
|
||||
ServiceVersion: getEnv("SERVICE_VERSION", "1.0.0"),
|
||||
ServiceNamespace: getEnv("SERVICE_NAMESPACE", "ai-ml"),
|
||||
DeploymentEnv: getEnv("DEPLOYMENT_ENV", "production"),
|
||||
|
||||
NATSURL: getEnv("NATS_URL", "nats://nats.ai-ml.svc.cluster.local:4222"),
|
||||
NATSUser: getEnv("NATS_USER", ""),
|
||||
NATSPassword: getEnv("NATS_PASSWORD", ""),
|
||||
NATSQueueGroup: getEnv("NATS_QUEUE_GROUP", ""),
|
||||
|
||||
RedisURL: getEnv("REDIS_URL", "redis://valkey.ai-ml.svc.cluster.local:6379"),
|
||||
RedisPassword: getEnv("REDIS_PASSWORD", ""),
|
||||
|
||||
MilvusHost: getEnv("MILVUS_HOST", "milvus.ai-ml.svc.cluster.local"),
|
||||
MilvusPort: getEnvInt("MILVUS_PORT", 19530),
|
||||
MilvusCollection: getEnv("MILVUS_COLLECTION", "documents"),
|
||||
|
||||
EmbeddingsURL: getEnv("EMBEDDINGS_URL", "http://embeddings-predictor.ai-ml.svc.cluster.local"),
|
||||
RerankerURL: getEnv("RERANKER_URL", "http://reranker-predictor.ai-ml.svc.cluster.local"),
|
||||
LLMURL: getEnv("LLM_URL", "http://vllm-predictor.ai-ml.svc.cluster.local"),
|
||||
TTSURL: getEnv("TTS_URL", "http://tts-predictor.ai-ml.svc.cluster.local"),
|
||||
STTURL: getEnv("STT_URL", "http://whisper-predictor.ai-ml.svc.cluster.local"),
|
||||
|
||||
OTELEnabled: getEnvBool("OTEL_ENABLED", true),
|
||||
OTELEndpoint: getEnv("OTEL_ENDPOINT", "http://opentelemetry-collector.observability.svc.cluster.local:4317"),
|
||||
OTELUseHTTP: getEnvBool("OTEL_USE_HTTP", false),
|
||||
|
||||
HyperDXEnabled: getEnvBool("HYPERDX_ENABLED", false),
|
||||
HyperDXAPIKey: getEnv("HYPERDX_API_KEY", ""),
|
||||
HyperDXEndpoint: getEnv("HYPERDX_ENDPOINT", "https://in-otel.hyperdx.io"),
|
||||
|
||||
MLflowTrackingURI: getEnv("MLFLOW_TRACKING_URI", "http://mlflow.mlflow.svc.cluster.local:80"),
|
||||
MLflowExperimentName: getEnv("MLFLOW_EXPERIMENT_NAME", ""),
|
||||
MLflowEnabled: getEnvBool("MLFLOW_ENABLED", true),
|
||||
|
||||
HealthPort: getEnvInt("HEALTH_PORT", 8080),
|
||||
HealthPath: getEnv("HEALTH_PATH", "/health"),
|
||||
ReadyPath: getEnv("READY_PATH", "/ready"),
|
||||
|
||||
HTTPTimeout: getEnvDuration("HTTP_TIMEOUT", 60*time.Second),
|
||||
NATSTimeout: getEnvDuration("NATS_TIMEOUT", 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func getEnvInt(key string, fallback int) int {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func getEnvBool(key string, fallback bool) bool {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if b, err := strconv.ParseBool(v); err == nil {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func getEnvDuration(key string, fallback time.Duration) time.Duration {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
||||
return time.Duration(f * float64(time.Second))
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
42
config/config_test.go
Normal file
42
config/config_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadDefaults(t *testing.T) {
|
||||
s := Load()
|
||||
if s.ServiceName != "handler" {
|
||||
t.Errorf("expected default ServiceName 'handler', got %q", s.ServiceName)
|
||||
}
|
||||
if s.HealthPort != 8080 {
|
||||
t.Errorf("expected default HealthPort 8080, got %d", s.HealthPort)
|
||||
}
|
||||
if s.HTTPTimeout != 60*time.Second {
|
||||
t.Errorf("expected default HTTPTimeout 60s, got %v", s.HTTPTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromEnv(t *testing.T) {
|
||||
os.Setenv("SERVICE_NAME", "test-svc")
|
||||
os.Setenv("HEALTH_PORT", "9090")
|
||||
os.Setenv("OTEL_ENABLED", "false")
|
||||
defer func() {
|
||||
os.Unsetenv("SERVICE_NAME")
|
||||
os.Unsetenv("HEALTH_PORT")
|
||||
os.Unsetenv("OTEL_ENABLED")
|
||||
}()
|
||||
|
||||
s := Load()
|
||||
if s.ServiceName != "test-svc" {
|
||||
t.Errorf("expected ServiceName 'test-svc', got %q", s.ServiceName)
|
||||
}
|
||||
if s.HealthPort != 9090 {
|
||||
t.Errorf("expected HealthPort 9090, got %d", s.HealthPort)
|
||||
}
|
||||
if s.OTELEnabled {
|
||||
t.Error("expected OTELEnabled false")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user