- Implemented new gRPC service `AIService` in `proto/ai/v1/ai.proto` for handling natural language queries. - Generated Go code for the gRPC service and messages in `gen/ai/v1/`. - Created `services/ai-gateway/` directory structure with necessary files for the service. - Added configuration loading and structured logging. - Implemented domain logic for intent parsing and interaction with Home Assistant. - Established outbound adapters for Ollama and Home Assistant with mTLS support. - Updated `go.work` to include the new service and maintain existing dependencies. - Modified `discord-bot` to use the new `ai-gateway` for AI interactions. - Added deployment manifest for Kubernetes and CI/CD configuration for building and deploying the service.
90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// Config holds runtime configuration for the AI gRPC gateway.
|
|
type Config struct {
|
|
GRPCPort string
|
|
OllamaURL string
|
|
OllamaModel string
|
|
OllamaTimeout time.Duration
|
|
HAGatewayAddr string
|
|
HAGatewayServerName string
|
|
TLSDir string
|
|
OTELEndpoint string
|
|
LogLevel string
|
|
LogFormat string
|
|
LightCacheTTL time.Duration
|
|
}
|
|
|
|
// Load reads configuration from environment variables and applies defaults.
|
|
func Load() (*Config, error) {
|
|
ollamaTimeout, err := parseDurationEnv("OLLAMA_TIMEOUT", 30*time.Second)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cacheTTL, err := parseDurationEnv("LIGHT_CACHE_TTL", 60*time.Second)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg := &Config{
|
|
GRPCPort: getenvDefault("GRPC_PORT", "50052"),
|
|
OllamaURL: getenvDefault("OLLAMA_URL", "http://192.168.7.96:11434"),
|
|
OllamaModel: getenvDefault("OLLAMA_MODEL", "llama3"),
|
|
OllamaTimeout: ollamaTimeout,
|
|
HAGatewayAddr: getenvDefault("HA_GATEWAY_ADDR", "ha-gateway.home-services.svc.cluster.local:50051"),
|
|
HAGatewayServerName: getenvDefault("HA_GATEWAY_SERVER_NAME", "ha-gateway.home-services.svc.cluster.local"),
|
|
TLSDir: os.Getenv("TLS_DIR"),
|
|
OTELEndpoint: os.Getenv("OTEL_ENDPOINT"),
|
|
LogLevel: getenvDefault("LOG_LEVEL", "info"),
|
|
LogFormat: getenvDefault("LOG_FORMAT", "json"),
|
|
LightCacheTTL: cacheTTL,
|
|
}
|
|
if cfg.TLSDir != "" {
|
|
if err := validateTLSDir(cfg.TLSDir); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func getenvDefault(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func parseDurationEnv(key string, fallback time.Duration) (time.Duration, error) {
|
|
if v := os.Getenv(key); v != "" {
|
|
d, err := time.ParseDuration(v)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("parse %s: %w", key, err)
|
|
}
|
|
return d, nil
|
|
}
|
|
return fallback, nil
|
|
}
|
|
|
|
func validateTLSDir(dir string) error {
|
|
required := []string{"tls.crt", "tls.key", "ca.crt"}
|
|
for _, name := range required {
|
|
path := filepath.Join(dir, name)
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return fmt.Errorf("tls dir validation failed for %s: %w", path, err)
|
|
}
|
|
if info.IsDir() {
|
|
return fmt.Errorf("tls dir validation failed for %s: %w", path, errors.New("expected file"))
|
|
}
|
|
}
|
|
return nil
|
|
}
|