feat(ai): pluggable provider interface, Ollama + Mistral impls, migrate Pass2 sites
Replaces the Mistral-only ai.Client with an ai.Provider interface backed by Ollama and Mistral implementations. Migrates enrichment + similarity callers to ai.Provider.Chat. Research endpoint returns 501 until commit 2 reinstates it on the new orchestrator.
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"marktvogt.de/backend/internal/config"
|
||||
"marktvogt.de/backend/internal/domain/discovery/enrich"
|
||||
"marktvogt.de/backend/internal/pkg/ai"
|
||||
"marktvogt.de/backend/internal/pkg/scrape"
|
||||
@@ -65,8 +66,14 @@ func realMain() int {
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
apiKey := os.Getenv("AI_API_KEY")
|
||||
model := os.Getenv("AI_MODEL_COMPLEX")
|
||||
apiKey := os.Getenv("AI_MISTRAL_API_KEY")
|
||||
if apiKey == "" {
|
||||
apiKey = os.Getenv("AI_API_KEY") // legacy fallback
|
||||
}
|
||||
model := os.Getenv("AI_MISTRAL_MODEL")
|
||||
if model == "" {
|
||||
model = os.Getenv("AI_MODEL_COMPLEX") // legacy fallback
|
||||
}
|
||||
if model == "" {
|
||||
model = "mistral-large-latest"
|
||||
}
|
||||
@@ -74,9 +81,14 @@ func realMain() int {
|
||||
if userAgent == "" {
|
||||
userAgent = "marktvogt-eval/1.0 (+https://marktvogt.de)"
|
||||
}
|
||||
client := ai.New(apiKey, "", model, 1.0)
|
||||
if !client.Enabled() {
|
||||
slog.Error("AI client not configured (set AI_API_KEY)")
|
||||
client, err := ai.NewFromConfig(config.AIConfig{
|
||||
Provider: "mistral",
|
||||
MistralAPIKey: apiKey,
|
||||
MistralModel: model,
|
||||
RateLimitRPS: 1.0,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Error("AI client not configured", "error", err)
|
||||
return 2
|
||||
}
|
||||
|
||||
@@ -95,7 +107,7 @@ func realMain() int {
|
||||
return runSimilarityMode(ctx, client, cfg)
|
||||
case modeCategory:
|
||||
scraper := scrape.New(userAgent)
|
||||
enricher := enrich.NewMistralLLMEnricher(client, scraper)
|
||||
enricher := enrich.NewLLMEnricher(client, scraper)
|
||||
cfg := evalConfig{
|
||||
model: model,
|
||||
fixturePath: pathWithDefault(*fixturePath, "backend/cmd/discovery-eval/fixtures/category.json"),
|
||||
@@ -117,7 +129,7 @@ func pathWithDefault(p, dflt string) string {
|
||||
return p
|
||||
}
|
||||
|
||||
func runSimilarityMode(ctx context.Context, client *ai.Client, cfg evalConfig) int {
|
||||
func runSimilarityMode(ctx context.Context, client ai.Provider, cfg evalConfig) int {
|
||||
fixture, err := loadFixture(cfg.fixturePath)
|
||||
if err != nil {
|
||||
slog.Error("load fixture", "path", cfg.fixturePath, "error", err)
|
||||
@@ -125,7 +137,7 @@ func runSimilarityMode(ctx context.Context, client *ai.Client, cfg evalConfig) i
|
||||
}
|
||||
slog.Info("loaded fixture", "mode", modeSimilarity, "pairs", len(fixture.Pairs), "path", cfg.fixturePath)
|
||||
|
||||
classifier := enrich.NewMistralSimilarityClassifier(client)
|
||||
classifier := enrich.NewSimilarityClassifier(client)
|
||||
|
||||
cache, err := loadCache(cfg.cachePath)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user