rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
4e11540
·
1 Parent(s): 2c51f98

docs: expert README polish — rich Mermaid architecture diagrams, clearer data-split table, collapsible repo map

Browse files

Per 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>

Files changed (1) hide show
  1. README.md +220 -6
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 Request flow (a single turn)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- | Repo | What | Why separate |
222
- | --- | --- | --- |
223
- | **Code** — HF Space (`origin`) + GitHub mirror (`github`) | Application source only (no data blobs) | Keeps the deployable image small; the Space repo has a tight size cap |
224
- | **Data** — HF dataset `rohitsar567/insurance-bot-data` | Policy PDF corpus + prebuilt Chroma vectors + extracted JSON | Large binaries don't belong in the code repo; pulled at build |
225
- | **Curated facts** — `40-data/` (tracked in the code repo) | Small structured JSON the backend reads at request time | Decision-critical, human-reviewed, small enough to version with code |
 
 
 
 
 
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 &lt;audio&gt; 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 &lt;audio&gt;"]
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/&lt;name&gt;.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