Spaces:
Sleeping
Sleeping
Commit ·
4e11540
1
Parent(s): 2c51f98
docs: expert README polish — rich Mermaid architecture diagrams, clearer data-split table, collapsible repo map
Browse filesPer expert review (no file moves): (1) §4.2 now has a 7-diagram Mermaid
suite covering every layer + interlinkage (request flow, brain+fallback,
voice, profile/recall, data+ingest, 8 upload gates, deploy); ASCII kept
as plain-text fallback. (2) Rewrote the first table (data split) to be
scannable with a lazy-reader reassurance. (3) §8 repo map: concise
at-a-glance top-level + full tree behind a <details> so the landing
view isn't overwhelming.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README.md
CHANGED
|
@@ -125,7 +125,201 @@ falls back to an **NVIDIA NIM** open-model chain. Voice in/out is handled by
|
|
| 125 |
**Sarvam** (Indian-language STT/TTS). Heavy data (PDF corpus + prebuilt
|
| 126 |
vectors) lives in a separate Hugging Face **dataset**, not in the code repo.
|
| 127 |
|
| 128 |
-
### 4.2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
```
|
| 131 |
┌──────────────────────────────────────────────┐
|
|
@@ -218,11 +412,16 @@ profile drives both recommendation fit and the illustrative premium estimate
|
|
| 218 |
|
| 219 |
There are **three repositories**, deliberately separated:
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
What lives where:
|
| 228 |
|
|
@@ -342,6 +541,19 @@ the load-bearing ones.
|
|
| 342 |
|
| 343 |
## 8. Repository map
|
| 344 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
```
|
| 346 |
.
|
| 347 |
├── backend/ FastAPI app
|
|
@@ -380,6 +592,8 @@ the load-bearing ones.
|
|
| 380 |
└── requirements.txt
|
| 381 |
```
|
| 382 |
|
|
|
|
|
|
|
| 383 |
> ⚠️ **Note on `70-docs/` and ADRs:** these capture design history and
|
| 384 |
> rationale; some predate the single-brain rewrite and are being brought into
|
| 385 |
> line with the system as it actually runs today. **This README is the
|
|
|
|
| 125 |
**Sarvam** (Indian-language STT/TTS). Heavy data (PDF corpus + prebuilt
|
| 126 |
vectors) lives in a separate Hugging Face **dataset**, not in the code repo.
|
| 127 |
|
| 128 |
+
### 4.2 Architecture diagrams (every layer, every link)
|
| 129 |
+
|
| 130 |
+
> These render natively on GitHub. They are the authoritative visual map
|
| 131 |
+
> of the system; a compact plain-text version of the core path is kept
|
| 132 |
+
> after them as a fallback for non-Mermaid renderers.
|
| 133 |
+
|
| 134 |
+
**A · Single-turn request flow — end to end, all layers**
|
| 135 |
+
|
| 136 |
+
```mermaid
|
| 137 |
+
flowchart TB
|
| 138 |
+
subgraph BROWSER["🖥️ Browser — Next.js 16 / React 19 (frontend/src/app/page.tsx)"]
|
| 139 |
+
UI["Chat · Marketplace · Compare · Profile builder"]
|
| 140 |
+
VOICEIN["Voice capture<br/>Web Speech (interim) + MediaRecorder (authoritative)"]
|
| 141 |
+
AUD["In-DOM <audio> playback · barge-in"]
|
| 142 |
+
end
|
| 143 |
+
|
| 144 |
+
subgraph API["⚙️ FastAPI — backend/main.py"]
|
| 145 |
+
RC["/api/chat"]
|
| 146 |
+
RT["/api/transcribe"]
|
| 147 |
+
RU["/api/upload-policy"]
|
| 148 |
+
RO["/api/coverage · /profile · /policies · /scorecard · /session"]
|
| 149 |
+
end
|
| 150 |
+
|
| 151 |
+
subgraph BRAIN["🧠 THE BRAIN — backend/single_brain.py · Google Gemini (gemini-2.5-flash-lite)"]
|
| 152 |
+
HT["handle_turn() — one model call + tool loop"]
|
| 153 |
+
subgraph TOOLS["function-calling tools — backend/brain_tools.py"]
|
| 154 |
+
T1["retrieve_policies(query, filters, top_k)"]
|
| 155 |
+
T2["save_profile_field(field, value)"]
|
| 156 |
+
T3["get_policy_facts(policy_ids)"]
|
| 157 |
+
T4["mark_recommendation(policy_ids)"]
|
| 158 |
+
end
|
| 159 |
+
FB["on Gemini fail / cold-start 503 →<br/>backend/nim_fallback.py (NVIDIA NIM chain)"]
|
| 160 |
+
end
|
| 161 |
+
|
| 162 |
+
subgraph DATA["📚 Retrieval & curated data"]
|
| 163 |
+
CH["Chroma vector store · BGE-small-en-v1.5 (local, 384-d)<br/>rag/retrieve.py"]
|
| 164 |
+
SHARED["shared 'policies' collection<br/>~150 plans · ~7.3k chunks · 20 insurers"]
|
| 165 |
+
QUAR["per-session 'quarantine' collection<br/>uploaded PDFs · 24h TTL · session-isolated"]
|
| 166 |
+
FACTS["40-data/policy_facts/*.json<br/>curated facts + verbatim source_quote"]
|
| 167 |
+
end
|
| 168 |
+
|
| 169 |
+
subgraph SCORE["🎯 Fit, scoring & pricing"]
|
| 170 |
+
SC["scorecard.py + retrieval_filters.py<br/>profile-fit gate + grade"]
|
| 171 |
+
PR["premium_calculator.py + sum_insured.py<br/>illustrative premium range"]
|
| 172 |
+
end
|
| 173 |
+
|
| 174 |
+
subgraph PROF["👤 Profile & persistence"]
|
| 175 |
+
SS["session_state.py (live profile, recall staging)"]
|
| 176 |
+
PS["profile_store.py / profile_persistence.py<br/>named profile on disk (recall)"]
|
| 177 |
+
PRG["profile_rag.py — profile as session-scoped chunk"]
|
| 178 |
+
end
|
| 179 |
+
|
| 180 |
+
subgraph VOICEOUT["🔊 Voice out"]
|
| 181 |
+
VF["voice_format.py — money/Indic normalisation, sentence chunking"]
|
| 182 |
+
TTS["Sarvam Bulbul TTS"]
|
| 183 |
+
end
|
| 184 |
+
|
| 185 |
+
UI -->|"text · POST"| RC
|
| 186 |
+
VOICEIN -->|"audio"| RT
|
| 187 |
+
RT -->|"Sarvam Saarika STT (authoritative transcript)"| RC
|
| 188 |
+
UI -->|"upload"| RU
|
| 189 |
+
UI --> RO
|
| 190 |
+
RC --> HT
|
| 191 |
+
HT <-->|"tool calls / results"| TOOLS
|
| 192 |
+
T1 --> CH
|
| 193 |
+
T3 --> FACTS
|
| 194 |
+
CH --> SHARED
|
| 195 |
+
CH --> QUAR
|
| 196 |
+
T2 --> SS
|
| 197 |
+
SS --> PS
|
| 198 |
+
SS --> PRG
|
| 199 |
+
PRG -.->|"'given my situation' grounding"| CH
|
| 200 |
+
HT --> SC
|
| 201 |
+
SC --> PR
|
| 202 |
+
RO --> SC
|
| 203 |
+
RO --> PR
|
| 204 |
+
HT -. "Gemini down" .-> FB
|
| 205 |
+
FB --> HT
|
| 206 |
+
HT -->|"reply + citations"| RC
|
| 207 |
+
RC -->|"text reply"| UI
|
| 208 |
+
RC -->|"speak?"| VF
|
| 209 |
+
VF --> TTS
|
| 210 |
+
TTS --> AUD
|
| 211 |
+
AUD -. "barge-in aborts in-flight /api/chat" .-> RC
|
| 212 |
+
HT -.->|"1 JSON line / turn"| LOG["logs/turns.jsonl (replay/audit)"]
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
**B · LLM brain + fail-loud fallback chain**
|
| 216 |
+
|
| 217 |
+
```mermaid
|
| 218 |
+
flowchart LR
|
| 219 |
+
Q["chat turn"] --> G{"Gemini<br/>gemini-2.5-flash-lite"}
|
| 220 |
+
G -->|"OK"| ANS["grounded reply<br/>(only from tool results)"]
|
| 221 |
+
G -->|"real failure / cold-start 503"| H["backend/llm_health.py<br/>background probe + sticky-primary election"]
|
| 222 |
+
H --> NIM["NVIDIA NIM open-model chain<br/>backend/nim_fallback.py"]
|
| 223 |
+
NIM -->|"healthy model"| ANS
|
| 224 |
+
NIM -->|"whole chain down"| LOUD["explicit 'service degraded'<br/>(never a silently wrong answer)"]
|
| 225 |
+
ANS --> GUARD["prose-grounding guard:<br/>every policy/UIN named is verified<br/>against retrieve_policies + get_policy_facts"]
|
| 226 |
+
GUARD --> OUT["sent to user"]
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
**C · Voice pipeline (in / out, with barge-in)**
|
| 230 |
+
|
| 231 |
+
```mermaid
|
| 232 |
+
flowchart LR
|
| 233 |
+
MIC["mic — tap-to-talk (touch) / push-to-talk (desktop)"] --> MR["MediaRecorder (authoritative audio)"]
|
| 234 |
+
MIC -.->|"live interim text"| WS["Web Speech API (display only)"]
|
| 235 |
+
MR --> STT["/api/transcribe → Sarvam Saarika STT"]
|
| 236 |
+
STT --> BR["single_brain.handle_turn"]
|
| 237 |
+
BR --> RPL["reply text + citations"]
|
| 238 |
+
RPL --> VF["voice_format.py<br/>money/Indic normalise · chunk at sentence bounds"]
|
| 239 |
+
VF --> BUL["Sarvam Bulbul TTS"]
|
| 240 |
+
BUL --> PLAY["in-DOM <audio>"]
|
| 241 |
+
SPK["user speaks over bot"] -.->|"barge-in"| PLAY
|
| 242 |
+
SPK -.->|"abort in-flight"| BR
|
| 243 |
+
```
|
| 244 |
+
|
| 245 |
+
**D · Profile, personalisation & returning-user recall**
|
| 246 |
+
|
| 247 |
+
```mermaid
|
| 248 |
+
flowchart TB
|
| 249 |
+
A["user answers (chat or profile builder)"] --> SPF["save_profile_field → session_state.profile"]
|
| 250 |
+
SPF --> ENDT["end of handle_turn"]
|
| 251 |
+
ENDT --> AP["auto_persist_session()<br/>(no-op if no name; errors swallowed)"]
|
| 252 |
+
AP --> DISK["profile_store: 40-data/profiles/<name>.json"]
|
| 253 |
+
AP --> EMB["profile_rag: session-scoped profile chunk"]
|
| 254 |
+
RV["return visit — user states a name"] --> EX["extract name"]
|
| 255 |
+
EX --> TR["try_recall_by_name (first-name slug fallback)"]
|
| 256 |
+
TR --> DISK
|
| 257 |
+
TR --> STAGE["session.pending_profile_recall (STAGED, never auto-merged)"]
|
| 258 |
+
STAGE --> ASK["bot: 'Welcome back — are you the same X?'"]
|
| 259 |
+
ASK -->|"yes"| MERGE["apply_pending_recall → merge stored slots"]
|
| 260 |
+
ASK -->|"no"| FRESH["discard · continue as new user"]
|
| 261 |
+
SPF --> FIT["scorecard fit + grade"]
|
| 262 |
+
SPF --> PREM["illustrative premium"]
|
| 263 |
+
note1["evicted/blank session + carried chat_history →<br/>STATE-RECOVERY: rebuild profile from history,<br/>never re-ask the name"] -.-> ENDT
|
| 264 |
+
```
|
| 265 |
+
|
| 266 |
+
**E · Data architecture & offline ingest pipeline**
|
| 267 |
+
|
| 268 |
+
```mermaid
|
| 269 |
+
flowchart LR
|
| 270 |
+
subgraph OFFLINE["🛠️ Offline ingest (not on the request path)"]
|
| 271 |
+
PDF["rag/corpus/ — raw policy PDFs"] --> ING["rag/ingest.py → chunk"]
|
| 272 |
+
ING --> EXT["rag/extract.py + schema.py → structured facts"]
|
| 273 |
+
ING --> VEC["embed (BGE-small) → Chroma vectors"]
|
| 274 |
+
EXT --> PFJSON["40-data/policy_facts/*.json (+ source_quote)"]
|
| 275 |
+
EXT --> DUCK["policies.duckdb (rollup)"]
|
| 276 |
+
end
|
| 277 |
+
subgraph REPOS["Three deliberately-separated repos"]
|
| 278 |
+
CODE["Code — HF Space (origin) + GitHub mirror<br/>app source only, no blobs"]
|
| 279 |
+
DSET["Data — HF dataset rohitsar567/insurance-bot-data<br/>PDF corpus + prebuilt Chroma vectors"]
|
| 280 |
+
CUR["Curated — 40-data/ (versioned with code)<br/>small human-reviewed JSON"]
|
| 281 |
+
end
|
| 282 |
+
VEC --> DSET
|
| 283 |
+
PDF --> DSET
|
| 284 |
+
PFJSON --> CUR
|
| 285 |
+
DSET -->|"snapshot_download at Docker build"| RUNTIME["request-time: rag/retrieve.py reads vectors"]
|
| 286 |
+
CUR -->|"read per request"| RUNTIME
|
| 287 |
+
```
|
| 288 |
+
|
| 289 |
+
**F · Uploaded-PDF defence — 8 sequential gates**
|
| 290 |
+
|
| 291 |
+
```mermaid
|
| 292 |
+
flowchart LR
|
| 293 |
+
UP["/api/upload-policy (public web)"] --> G1["1 File mechanics<br/>%PDF · size band · %%EOF · no exe/JS"]
|
| 294 |
+
G1 --> G2["2 Content quality<br/>≥1500 chars · ≥3 pp · domain keyword"]
|
| 295 |
+
G2 --> G3["3 Prompt-injection sweep"]
|
| 296 |
+
G3 --> G4["4 Per-session rate limit"]
|
| 297 |
+
G4 --> G5["5 Per-IP rate limit"]
|
| 298 |
+
G5 --> G6["6 Encrypted/locked → reject"]
|
| 299 |
+
G6 --> G7["7 Page-count ceiling (>200)"]
|
| 300 |
+
G7 --> G8["8 Hash dedupe + reject-cache"]
|
| 301 |
+
G8 -->|"pass"| QC["per-session QUARANTINE Chroma collection<br/>session-scoped · 24h idle TTL · never the shared corpus"]
|
| 302 |
+
G1 & G2 & G3 & G4 & G5 & G6 & G7 & G8 -->|"fail"| REJ["clean rejection (reason surfaced)"]
|
| 303 |
+
```
|
| 304 |
+
|
| 305 |
+
**G · Deployment**
|
| 306 |
+
|
| 307 |
+
```mermaid
|
| 308 |
+
flowchart LR
|
| 309 |
+
DEV["git push"] --> ORI["HF Space remote (origin)"]
|
| 310 |
+
DEV --> GH["GitHub mirror (github)"]
|
| 311 |
+
ORI --> BUILD["HF Space — Docker build"]
|
| 312 |
+
BUILD --> SNAP["huggingface_hub.snapshot_download<br/>hydrate rag/ (corpus + vectors) from HF dataset"]
|
| 313 |
+
BUILD --> FE["build Next.js static export"]
|
| 314 |
+
SNAP --> RUN["entrypoint.sh → uvicorn backend.main:app :7860"]
|
| 315 |
+
FE --> RUN
|
| 316 |
+
RUN --> LIVE["live Space (FastAPI also serves the frontend)"]
|
| 317 |
+
LIVE --> CHK["verify reported build SHA advanced<br/>(LFS/quota push can fail silently)"]
|
| 318 |
+
```
|
| 319 |
+
|
| 320 |
+
---
|
| 321 |
+
|
| 322 |
+
### 4.2-text Request flow (a single turn) — plain-text fallback
|
| 323 |
|
| 324 |
```
|
| 325 |
┌──────────────────────────────────────────────┐
|
|
|
|
| 412 |
|
| 413 |
There are **three repositories**, deliberately separated:
|
| 414 |
|
| 415 |
+
*(You don't need any of this to use the app — just open the live link at
|
| 416 |
+
the top. This section is for someone running or reviewing the code.)*
|
| 417 |
+
|
| 418 |
+
The data is split into **three places**, each for a clear reason:
|
| 419 |
+
|
| 420 |
+
| # | Place | What it holds | Why it's separate |
|
| 421 |
+
| - | --- | --- | --- |
|
| 422 |
+
| 1 | **Code repo** (this repo — HF Space + GitHub mirror) | Application source only — no data blobs | Keeps the deployable image small (the Space has a tight size cap) |
|
| 423 |
+
| 2 | **Data dataset** (HF dataset `rohitsar567/insurance-bot-data`) | The big binaries: policy-PDF corpus + prebuilt Chroma vectors | Large files don't belong in code; pulled in automatically at build |
|
| 424 |
+
| 3 | **Curated facts** (`40-data/`, inside this code repo) | Small, human-reviewed JSON the backend reads on every request | Decision-critical and tiny — safe to version alongside the code |
|
| 425 |
|
| 426 |
What lives where:
|
| 427 |
|
|
|
|
| 541 |
|
| 542 |
## 8. Repository map
|
| 543 |
|
| 544 |
+
**At a glance** — the root is intentionally small; you only need to know
|
| 545 |
+
these:
|
| 546 |
+
|
| 547 |
+
- **`backend/`** — FastAPI app + the brain, tools, retrieval, scoring, security
|
| 548 |
+
- **`frontend/`** — the Next.js web app
|
| 549 |
+
- **`rag/`** — retrieval + offline ingest (corpus/vectors are git-ignored, pulled at build)
|
| 550 |
+
- **`40-data/`** — curated, human-reviewed policy facts (versioned with code)
|
| 551 |
+
- **`tests/`** — the pytest green gate
|
| 552 |
+
- root files: `Dockerfile`, `entrypoint.sh`, `requirements.txt`, `pytest.ini`, `README.md`
|
| 553 |
+
|
| 554 |
+
<details>
|
| 555 |
+
<summary><b>Full directory tree</b> — click to expand</summary>
|
| 556 |
+
|
| 557 |
```
|
| 558 |
.
|
| 559 |
├── backend/ FastAPI app
|
|
|
|
| 592 |
└── requirements.txt
|
| 593 |
```
|
| 594 |
|
| 595 |
+
</details>
|
| 596 |
+
|
| 597 |
> ⚠️ **Note on `70-docs/` and ADRs:** these capture design history and
|
| 598 |
> rationale; some predate the single-brain rewrite and are being brought into
|
| 599 |
> line with the system as it actually runs today. **This README is the
|