# `backend/providers/` — STT / TTS / LLM / embedding clients Every external model is fronted by a small typed client here. The orchestrator and helper modules **only** ever import provider symbols from this folder — single import surface so a provider swap is local. ## Files | File | Provider | Role | Notes | | --- | --- | --- | --- | | `base.py` | — | Abstract `LLM`, `STT`, `TTS`, `Embeddings` Protocols. Every concrete client conforms. | — | | `nvidia_nim_llm.py` | NVIDIA NIM | Core chain runner — `NimChainLLM(chain=[...])` uses probe-driven primary election (KI-080): calls the elected PRIMARY once per turn, falls to elected BACKUP on real-time failure. Exposes `get_brain_llm()` (the separate `get_fast_brain_llm()` / `get_judge_llm()` accessors were collapsed into it in the 2026-05-15 three-chain consolidation). Legacy `_balanced_brain_chain()` (50/50 NIM ↔ Groq rotator) retained as a bypassed feature-flag branch for one-release rollback. | [ADR-019](../../70-docs/60-decisions/ADR-019-nim-single-provider-consolidation.md), [ADR-031](../../70-docs/60-decisions/ADR-031-sticky-primary-election.md) (supersedes [ADR-026](../../70-docs/60-decisions/ADR-026-provider-load-balancing.md)) | | `groq_llm.py` | Groq | Single-call Llama-3.3-70B client. Used as cross-provider backup election candidate (KI-080) for both brain + fast-brain chains. | [ADR-031](../../70-docs/60-decisions/ADR-031-sticky-primary-election.md) | | `openrouter_llm.py` | OpenRouter | Multi-model fallback rung (DeepSeek-V3 etc.) for chains; rarely the primary in production. | — | | `sarvam_llm.py` | Sarvam-M | Indic-aware LLM. Used directly by `backend/single_brain.py` for Indic outputs — Sarvam-M routing is conditional on `_detect_language(user_text)` returning `'indic'`. | [ADR-006](../../70-docs/60-decisions/ADR-006-sarvam-first-stack.md) | | `sarvam_stt.py` | Sarvam Saarika v2.5 | Speech-to-text (10 Indic languages + English). | ADR-006 | | `sarvam_tts.py` | Sarvam Bulbul v2 | Text-to-speech; returns base64 WAV the frontend mounts in the in-DOM `