Files
sift/internal/search/build.go
T
kyle 429f6b207e p3: tavily parse errors degrade; retry DNS on search
A 200 with a bad Tavily body is tavily: error, not an empty success
that would cache for the hour. Upstream clients stay in the source
list if DNS is down at boot and retry the pin on Search; /healthz
lists them as unresolved until a pin lands.
2026-09-23 12:22:31 -07:00

99 lines
2.6 KiB
Go

package search
import (
"log/slog"
"os"
"time"
"git.wntrmute.dev/kyle/sift/internal/config"
"git.wntrmute.dev/kyle/sift/internal/dcache"
"git.wntrmute.dev/kyle/sift/internal/search/dial"
)
const (
defaultSearx = "http://127.0.0.1:8888"
defaultBrave = "https://api.search.brave.com/res/v1/llm/context"
defaultTav = "https://api.tavily.com/search"
)
// Build constructs sources from config. Missing keys are listed on Engine.Degraded
// and logged once; those sources are not attempted. A source whose DNS is down
// at boot stays in the list and is retried on Search.
func Build(cfg config.Search, cache *dcache.DB, emb Embedder, log *slog.Logger) *Engine {
timeout := config.ParseDuration(cfg.Timeout, 8*time.Second)
ttl := config.ParseDuration(cfg.CacheTTL, time.Hour)
k := cfg.K
if k <= 0 {
k = DefaultK
}
e := &Engine{
Timeout: timeout,
DefaultK: k,
RerankOn: cfg.RerankOn(),
Cache: cache,
CacheTTL: ttl,
Embedder: emb,
Log: log,
TavilyCap: cfg.Tavily.DailyCreditCap,
}
if cfg.SearxNG.Enabled {
u := cfg.SearxNG.URL
if u == "" {
u = defaultSearx
}
addPinned(e, log, NameSearxNG, u, dial.Options{Loopback: true, Timeout: timeout}, func(c *dial.Client) Source {
return &SearxNG{Client: c}
})
}
if cfg.BraveLLM.Enabled {
key := os.Getenv("BRAVE_API_KEY")
if key == "" {
e.Degraded = append(e.Degraded, "search.brave_llm: missing key")
if log != nil {
log.Error("search source", "source", NameBraveLLM, "err", "missing key")
}
} else {
u := cfg.BraveLLM.URL
if u == "" {
u = defaultBrave
}
addPinned(e, log, NameBraveLLM, u, dial.Options{Timeout: timeout}, func(c *dial.Client) Source {
return &BraveLLM{Client: c, Key: key}
})
}
}
if cfg.Tavily.Enabled {
key := os.Getenv("TAVILY_API_KEY")
if key == "" {
e.Degraded = append(e.Degraded, "search.tavily: missing key")
if log != nil {
log.Error("search source", "source", NameTavily, "err", "missing key")
}
} else {
u := cfg.Tavily.URL
if u == "" {
u = defaultTav
}
addPinned(e, log, NameTavily, u, dial.Options{Timeout: timeout}, func(c *dial.Client) Source {
return &Tavily{Client: c, Key: key}
})
}
}
return e
}
func addPinned(e *Engine, log *slog.Logger, name, raw string, opt dial.Options, wrap func(*dial.Client) Source) {
c, err := dial.New(raw, opt)
if err != nil {
e.Degraded = append(e.Degraded, "search."+name+": unavailable")
if log != nil {
log.Error("search source", "source", name, "err", "unavailable")
}
return
}
e.Sources = append(e.Sources, wrap(c))
if !c.Ready() && log != nil {
log.Error("search source unresolved, will retry", "source", name)
}
}