feat(brain): add Gemini as brain_answer's third LLM tier
CI / Lint / Test / Vet (push) Successful in 36s
CI / Mirror to GitHub (push) Successful in 4s

llm.Router was primary-then-fallback (berget -> iguana ollama). Both
have failed together at least once in production (berget 5xx outage +
a cold fallback model exceeding the shared timeout budget), leaving
brain_answer/brain_classify fully down.

Router gains an optional Tertiary *Client, tried only if both Primary
and Fallback fail. Nil-safe — existing 2-tier deployments (no
BRAIN_LLM_TERTIARY_URL) behave exactly as before.

main.go wires it from BRAIN_LLM_TERTIARY_URL/MODEL + GEMINI_API_KEY,
pointed at Google's OpenAI-compatible Gemini endpoint
(generativelanguage.googleapis.com/v1beta/openai/) — llm.Client already
speaks OpenAI-shaped chat-completions, so no new client type needed.

Tests: TestRouter_FallsBackToTertiaryWhenPrimaryAndFallbackFail,
TestRouter_TertiaryNotCalledWhenFallbackSucceeds, TestRouter_AllThreeFail,
TestRouter_NilTertiary (2-tier backward-compat). Full suite green
(both modules, -race), golangci-lint clean.

Deployment env vars (infra repo, separate commit — GitOps, TBD there
too): BRAIN_LLM_TERTIARY_URL, BRAIN_LLM_TERTIARY_MODEL, GEMINI_API_KEY
secretRef.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 14:43:25 +02:00
co-authored by Claude Sonnet 5
parent 692244d213
commit 05330ad54a
3 changed files with 95 additions and 4 deletions
+5
View File
@@ -236,6 +236,11 @@ func main() {
fallbackModel := envOr("BRAIN_LLM_FALLBACK_MODEL", "gemma4:31b")
router.Fallback = llm.New(fallbackURL, "", fallbackModel, timeout)
}
if tertiaryURL := os.Getenv("BRAIN_LLM_TERTIARY_URL"); tertiaryURL != "" {
tertiaryModel := envOr("BRAIN_LLM_TERTIARY_MODEL", "gemini-2.5-flash")
tertiaryKey := os.Getenv("GEMINI_API_KEY")
router.Tertiary = llm.New(tertiaryURL, tertiaryKey, tertiaryModel, timeout)
}
answerComplete = router.Complete
logger.Info("brain answer LLM configured", "primary", primaryURL, "model", primaryModel)
}