// config.go – Konfiguration, Clients und gemeinsame Verbindungen package config import ( "fmt" "log" "os" openai "github.com/sashabaranov/go-openai" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "gopkg.in/yaml.v3" ) type Config struct { Qdrant struct { Host string `yaml:"host"` Port string `yaml:"port"` APIKey string `yaml:"api_key"` Collection string `yaml:"collection"` } `yaml:"qdrant"` Embedding struct { URL string `yaml:"url"` Model string `yaml:"model"` Dimensions uint64 `yaml:"dimensions"` } `yaml:"embedding"` Chat struct { URL string `yaml:"url"` Model string `yaml:"model"` } `yaml:"chat"` Discord struct { Token string `yaml:"token"` GuildID string `yaml:"guild_id"` } `yaml:"discord"` Email struct { Host string `yaml:"host"` Port int `yaml:"port"` User string `yaml:"user"` Password string `yaml:"password"` TLS bool `yaml:"tls"` StartTLS bool `yaml:"starttls"` Folder string `yaml:"folder"` ProcessedFolder string `yaml:"processed_folder"` // Zielordner nach Zusammenfassung (leer = kein Verschieben) Model string `yaml:"model"` // Optional: überschreibt chat.model für Email-Zusammenfassungen } `yaml:"email"` Tasks struct { StorePath string `yaml:"store_path"` } `yaml:"tasks"` Daemon struct { ChannelID string `yaml:"channel_id"` EmailIntervalMin int `yaml:"email_interval_min"` TaskReminderHour int `yaml:"task_reminder_hour"` } `yaml:"daemon"` BrainRoot string `yaml:"brain_root"` TopK uint64 `yaml:"top_k"` ScoreThreshold float32 `yaml:"score_threshold"` } var Cfg Config // NewQdrantConn öffnet eine gRPC-Verbindung zur Qdrant-Instanz. // Der Aufrufer ist verantwortlich für conn.Close(). func NewQdrantConn() *grpc.ClientConn { conn, err := grpc.Dial( fmt.Sprintf("%s:%s", Cfg.Qdrant.Host, Cfg.Qdrant.Port), grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { log.Fatalf("❌ Qdrant Verbindung fehlgeschlagen: %v", err) } return conn } // NewEmbeddingClient erstellt einen Client für LocalAI (Embeddings). func NewEmbeddingClient() *openai.Client { c := openai.DefaultConfig("localai") c.BaseURL = Cfg.Embedding.URL return openai.NewClientWithConfig(c) } // NewChatClient erstellt einen Client für Chat-Completion (LocalAI). func NewChatClient() *openai.Client { c := openai.DefaultConfig("localai") c.BaseURL = Cfg.Chat.URL return openai.NewClientWithConfig(c) } // LoadConfig liest config.yml aus dem aktuellen Verzeichnis und validiert Pflichtfelder. func LoadConfig() { data, err := os.ReadFile("config.yml") if err != nil { log.Fatalf("❌ config.yml nicht gefunden: %v\n Lege config.yml im selben Verzeichnis an.", err) } if err := yaml.Unmarshal(data, &Cfg); err != nil { log.Fatalf("❌ config.yml ungültig: %v", err) } validateConfig() } // validateConfig prüft Pflichtfelder und gibt früh eine klare Fehlermeldung. func validateConfig() { var errs []string if Cfg.Qdrant.Host == "" { errs = append(errs, "qdrant.host fehlt") } if Cfg.Qdrant.Port == "" { errs = append(errs, "qdrant.port fehlt") } if Cfg.Embedding.URL == "" { errs = append(errs, "embedding.url fehlt") } if Cfg.Embedding.Model == "" { errs = append(errs, "embedding.model fehlt") } if Cfg.Chat.URL == "" { errs = append(errs, "chat.url fehlt") } if Cfg.Chat.Model == "" { errs = append(errs, "chat.model fehlt") } if len(errs) > 0 { for _, e := range errs { log.Printf("❌ config.yml: %s", e) } log.Fatal("❌ Konfiguration unvollständig – Bot wird nicht gestartet.") } }