package main import ( "context" "encoding/json" "fmt" "os" "regexp" "strconv" "strings" "time" ) type Config struct { Port int Debug bool InitializeSlack bool SlackToken string SlackChannels []string PostgresConn string BasicAuthUser string BasicAuthPassword string FillWithTestdata bool OllamaURL string OllamaModel string LocalCheckpoint string LocalTokenizer string AssetPattern string DatacenterPattern string EventNamePattern string storage Storage queue Queue driver Driver ai AI } var ( renderAssetPattern = `(dpg|svc|red)-[a-z0-9-]*[a-z0-9]` renderDatacenterPattern = `[a-z]{4}[a-z]*-[0-9]` renderEventNamePattern = `(\[[^\]]*\] *)?(?P.*)` ) func newConfig(ctx context.Context) (Config, error) { return newConfigFromEnv(ctx, os.Getenv) } func newConfigFromEnv(ctx context.Context, getEnv func(string) string) (Config, error) { def := Config{ Port: 38080, OllamaModel: "gemma:2b", AssetPattern: renderAssetPattern, DatacenterPattern: renderDatacenterPattern, EventNamePattern: renderEventNamePattern, } var m map[string]any if b, err := json.Marshal(def); err != nil { return Config{}, err } else if err := json.Unmarshal(b, &m); err != nil { return Config{}, err } re := regexp.MustCompile(`[A-Z]`) for k, v := range m { envK := k idxes := re.FindAllIndex([]byte(envK), -1) for i := len(idxes) - 1; i >= 0; i-- { idx := idxes[i] if idx[0] > 0 { envK = fmt.Sprintf("%s_%s", envK[:idx[0]], envK[idx[0]:]) } } envK = strings.ToUpper(envK) s := getEnv(envK) if s == "" { continue } switch v.(type) { case string: m[k] = s case int64, float64: n, err := strconv.ParseFloat(s, 32) if err != nil { return Config{}, err } m[k] = n case bool: got, err := strconv.ParseBool(s) if err != nil { return Config{}, err } m[k] = got case nil, []interface{}: m[k] = strings.Split(s, ",") default: return Config{}, fmt.Errorf("not impl: parse %s as %T", envK, v) } } var result Config if b, err := json.Marshal(m); err != nil { return Config{}, err } else if err := json.Unmarshal(b, &result); err != nil { return Config{}, err } result.driver = NewRAM() if result.PostgresConn != "" { ctx, can := context.WithTimeout(ctx, time.Second*10) defer can() pg, err := NewPostgres(ctx, result.PostgresConn) if err != nil { return Config{}, err } result.driver = pg } if result.FillWithTestdata { if err := FillWithTestdata(ctx, result.driver, result.AssetPattern, result.DatacenterPattern, result.EventNamePattern); err != nil { return Config{}, err } } result.storage = NewStorage(result.driver) result.queue = NewQueue(result.driver) if result.OllamaURL != "" { result.ai = NewAIOllama(result.OllamaURL, result.OllamaModel) } else if result.LocalCheckpoint != "" && result.LocalTokenizer != "" { result.ai = NewAILocal(result.LocalCheckpoint, result.LocalTokenizer, 0.9, 128, 0.9) } else { result.ai = NewAINoop() } return result, nil }