rohitsar567 commited on
Commit
64d5deb
·
verified ·
1 Parent(s): 0c505c1

Deploy v1 — single-Docker FastAPI + Next.js + RAG + voice + faithfulness

Browse files
rag/ingest.py CHANGED
@@ -185,6 +185,10 @@ async def ingest_one(
185
  policy_name = manifest_entry.get("policy_name", pdf_path.stem)
186
  doc_type = manifest_entry.get("doc_type", "unknown")
187
  source_url = manifest_entry.get("url", "")
 
 
 
 
188
 
189
  # Skip if already ingested
190
  existing = collection.get(where={"policy_id": policy_id}, limit=1)
 
185
  policy_name = manifest_entry.get("policy_name", pdf_path.stem)
186
  doc_type = manifest_entry.get("doc_type", "unknown")
187
  source_url = manifest_entry.get("url", "")
188
+ # PDFs under rag/corpus/regulatory/ are IRDAI / Govt mandates. Tag them
189
+ # so retrieve.py can apply the regulatory-intent boost.
190
+ if pdf_path.parent.name == "regulatory" or insurer_slug == "regulatory":
191
+ doc_type = "regulatory"
192
 
193
  # Skip if already ingested
194
  existing = collection.get(where={"policy_id": policy_id}, limit=1)
rag/retrieve.py CHANGED
@@ -47,6 +47,43 @@ def get_collection():
47
  )
48
 
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  async def retrieve(
51
  query: str,
52
  top_k: int = settings.RAG_TOP_K,
@@ -58,6 +95,12 @@ async def retrieve(
58
 
59
  Optional filters narrow retrieval to specific policies/insurers (used
60
  by comparison and per-policy Q&A flows).
 
 
 
 
 
 
61
  """
62
  embedder = embedder or VoyageEmbeddings()
63
  [query_vec] = await embedder.embed([query], input_type="query")
@@ -69,6 +112,8 @@ async def retrieve(
69
  where["insurer_slug"] = {"$in": insurer_slugs}
70
 
71
  collection = get_collection()
 
 
72
  res = collection.query(
73
  query_embeddings=[query_vec],
74
  n_results=top_k,
@@ -76,32 +121,41 @@ async def retrieve(
76
  )
77
 
78
  out: list[RetrievedChunk] = []
79
- if not res["ids"] or not res["ids"][0]:
80
- return out
81
-
82
- for cid, doc, meta, dist in zip(
83
- res["ids"][0],
84
- res["documents"][0],
85
- res["metadatas"][0],
86
- res["distances"][0],
87
- ):
88
- # Chroma returns cosine *distance*; convert to similarity score
89
- score = 1.0 - dist
90
- out.append(
91
- RetrievedChunk(
92
- chunk_id=cid,
93
- text=doc,
94
- policy_id=meta.get("policy_id", ""),
95
- insurer_slug=meta.get("insurer_slug", ""),
96
- policy_name=meta.get("policy_name", ""),
97
- doc_type=meta.get("doc_type", ""),
98
- source_url=meta.get("source_url", ""),
99
- page_start=int(meta.get("page_start", 0)),
100
- page_end=int(meta.get("page_end", 0)),
101
- chunk_idx=int(meta.get("chunk_idx", 0)),
102
- score=score,
103
  )
104
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  return out
106
 
107
 
 
47
  )
48
 
49
 
50
+ import re as _re
51
+
52
+ # Queries containing these keywords trigger a parallel regulatory-only
53
+ # retrieval whose top results are boosted ×1.2 and merged into the final
54
+ # context. This ensures the brain sees IRDAI mandates whenever the user
55
+ # asks about compliance, legality, or what's "allowed" — even when the
56
+ # policy chunks would otherwise dominate raw cosine.
57
+ _REGULATORY_TRIGGERS = _re.compile(
58
+ r"\b(irdai|irda|regulation|regulator|regulatory|mandate|mandatory|"
59
+ r"allowed|prohibited|legal|illegal|unenforceable|cap|capped|ceiling|"
60
+ r"master circular|section\s+\d+|compliance|non[- ]?compliant|"
61
+ r"required|must|rule|statute|act|government)\b",
62
+ flags=_re.IGNORECASE,
63
+ )
64
+ REGULATORY_BOOST = 1.2 # multiplier applied to regulatory chunk scores
65
+
66
+
67
+ def _is_regulatory_intent(query: str) -> bool:
68
+ return bool(_REGULATORY_TRIGGERS.search(query or ""))
69
+
70
+
71
+ def _build_chunk(cid: str, doc: str, meta: dict, score: float) -> RetrievedChunk:
72
+ return RetrievedChunk(
73
+ chunk_id=cid,
74
+ text=doc,
75
+ policy_id=meta.get("policy_id", ""),
76
+ insurer_slug=meta.get("insurer_slug", ""),
77
+ policy_name=meta.get("policy_name", ""),
78
+ doc_type=meta.get("doc_type", ""),
79
+ source_url=meta.get("source_url", ""),
80
+ page_start=int(meta.get("page_start", 0)),
81
+ page_end=int(meta.get("page_end", 0)),
82
+ chunk_idx=int(meta.get("chunk_idx", 0)),
83
+ score=score,
84
+ )
85
+
86
+
87
  async def retrieve(
88
  query: str,
89
  top_k: int = settings.RAG_TOP_K,
 
95
 
96
  Optional filters narrow retrieval to specific policies/insurers (used
97
  by comparison and per-policy Q&A flows).
98
+
99
+ For queries with regulatory intent (IRDAI / mandate / allowed / etc.),
100
+ runs a SECOND retrieval restricted to doc_type='regulatory' chunks and
101
+ merges the top 3 of those (score-boosted ×1.2) into the result set.
102
+ This ensures the brain sees regulatory ceilings even when policy
103
+ chunks dominate raw cosine.
104
  """
105
  embedder = embedder or VoyageEmbeddings()
106
  [query_vec] = await embedder.embed([query], input_type="query")
 
112
  where["insurer_slug"] = {"$in": insurer_slugs}
113
 
114
  collection = get_collection()
115
+
116
+ # Standard retrieval
117
  res = collection.query(
118
  query_embeddings=[query_vec],
119
  n_results=top_k,
 
121
  )
122
 
123
  out: list[RetrievedChunk] = []
124
+ if res["ids"] and res["ids"][0]:
125
+ for cid, doc, meta, dist in zip(
126
+ res["ids"][0], res["documents"][0],
127
+ res["metadatas"][0], res["distances"][0],
128
+ ):
129
+ out.append(_build_chunk(cid, doc, meta, 1.0 - dist))
130
+
131
+ # Regulatory boost pass — only when the query is about IRDAI / regulations,
132
+ # and only when not already filtered to specific policies (otherwise the
133
+ # caller is asking about a specific policy, not regulations).
134
+ if _is_regulatory_intent(query) and not policy_ids and not insurer_slugs:
135
+ try:
136
+ reg_res = collection.query(
137
+ query_embeddings=[query_vec],
138
+ n_results=3,
139
+ where={"doc_type": "regulatory"},
 
 
 
 
 
 
 
 
140
  )
141
+ if reg_res["ids"] and reg_res["ids"][0]:
142
+ seen = {c.chunk_id for c in out}
143
+ reg_chunks: list[RetrievedChunk] = []
144
+ for cid, doc, meta, dist in zip(
145
+ reg_res["ids"][0], reg_res["documents"][0],
146
+ reg_res["metadatas"][0], reg_res["distances"][0],
147
+ ):
148
+ if cid in seen:
149
+ continue
150
+ boosted = (1.0 - dist) * REGULATORY_BOOST
151
+ reg_chunks.append(_build_chunk(cid, doc, meta, boosted))
152
+ # Merge and re-sort by score, then trim back to top_k
153
+ merged = sorted(out + reg_chunks, key=lambda c: c.score, reverse=True)
154
+ out = merged[:top_k]
155
+ except Exception:
156
+ # Regulatory boost is additive; failure shouldn't kill the main result
157
+ pass
158
+
159
  return out
160
 
161
 
tools/extract_policy_text_batch2.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Extract text from policy PDFs (batch 2) into the same text cache."""
2
+ import os, sys, json
3
+ import pdfplumber
4
+
5
+ BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
6
+ CACHE = "/tmp/claude/policy_extract/text_cache"
7
+ os.makedirs(CACHE, exist_ok=True)
8
+
9
+ # Batch 2: PDFs not yet covered, filtered by retail/standalone relevance
10
+ BATCH2 = [
11
+ ("aditya-birla", "rag/corpus/aditya-birla/activ-health-individual__wordings.pdf"),
12
+ ("bajaj-allianz", "rag/corpus/bajaj-allianz/comprehensive-care-plan__wordings.pdf"),
13
+ ("bajaj-allianz", "rag/corpus/bajaj-allianz/global-health-care__wordings.pdf"),
14
+ ("bajaj-allianz", "rag/corpus/bajaj-allianz/health-guard__wordings.pdf"),
15
+ ("bajaj-allianz", "rag/corpus/bajaj-allianz/silver-health__cis.pdf"),
16
+ ("bajaj-allianz", "rag/corpus/bajaj-allianz/tax-gain__cis.pdf"),
17
+ ("care-health", "rag/corpus/care-health/care-advantage__brochure.pdf"),
18
+ ("care-health", "rag/corpus/care-health/care-supreme-enhance__wordings.pdf"),
19
+ ("care-health", "rag/corpus/care-health/ultimate-care__wordings.pdf"),
20
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/energy-diabetes-hypertension__wordings.pdf"),
21
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/my-health-medisure-prime__wordings.pdf"),
22
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/my-health-sampoorna-suraksha__brochure.pdf"),
23
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/my-health-suraksha__brochure.pdf"),
24
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/my-health-women-suraksha__brochure.pdf"),
25
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/my-optima-secure-older-variant__wordings.pdf"),
26
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/optima-enhance__wordings.pdf"),
27
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/optima-plus__wordings.pdf"),
28
+ ("hdfc-ergo", "rag/corpus/hdfc-ergo/total-health-plan__wordings.pdf"),
29
+ ("icici-lombard", "rag/corpus/icici-lombard/arogya-sanjeevani__wordings.pdf"),
30
+ ("icici-lombard", "rag/corpus/icici-lombard/complete-health-insurance-umbrella__wordings.pdf"),
31
+ ("icici-lombard", "rag/corpus/icici-lombard/health-advantedge__wordings.pdf"),
32
+ ("icici-lombard", "rag/corpus/icici-lombard/health-booster-top-up__wordings.pdf"),
33
+ ("icici-lombard", "rag/corpus/icici-lombard/health-elite-plus__wordings.pdf"),
34
+ ("manipalcigna", "rag/corpus/manipalcigna/prohealth-select__wordings.pdf"),
35
+ ("manipalcigna", "rag/corpus/manipalcigna/sarvah-param__wordings.pdf"),
36
+ ("new-india", "rag/corpus/new-india/asha-kiran-policy__brochure.pdf"),
37
+ ("new-india", "rag/corpus/new-india/janata-mediclaim-policy__wordings.pdf"),
38
+ ("new-india", "rag/corpus/new-india/new-india-mediclaim-policy__wordings.pdf"),
39
+ ("new-india", "rag/corpus/new-india/universal-health-insurance__wordings.pdf"),
40
+ ("new-india", "rag/corpus/new-india/yuva-bharat-health-policy__wordings.pdf"),
41
+ ("niva-bupa", "rag/corpus/niva-bupa/aspire__wordings.pdf"),
42
+ ("niva-bupa", "rag/corpus/niva-bupa/health-plus-top-up__wordings.pdf"),
43
+ ("niva-bupa", "rag/corpus/niva-bupa/health-premia__wordings.pdf"),
44
+ ("niva-bupa", "rag/corpus/niva-bupa/reassure-3-0__wordings.pdf"),
45
+ ("niva-bupa", "rag/corpus/niva-bupa/rise__wordings.pdf"),
46
+ ("niva-bupa", "rag/corpus/niva-bupa/saral-suraksha-bima__wordings.pdf"),
47
+ ("star-health", "rag/corpus/star-health/health-premier__wordings.pdf"),
48
+ ("star-health", "rag/corpus/star-health/senior-citizens-red-carpet__brochure.pdf"),
49
+ ("star-health", "rag/corpus/star-health/star-assure__wordings.pdf"),
50
+ ("star-health", "rag/corpus/star-health/star-cardiac-care-platinum__wordings.pdf"),
51
+ ("star-health", "rag/corpus/star-health/star-cardiac-care__wordings.pdf"),
52
+ ("tata-aig", "rag/corpus/tata-aig/medicare-lite__cis.pdf"),
53
+ ("tata-aig", "rag/corpus/tata-aig/medicare-select__brochure.pdf"),
54
+ ]
55
+
56
+ for insurer, rel in BATCH2:
57
+ src = os.path.join(BASE, rel)
58
+ name = os.path.basename(rel).replace(".pdf", ".txt")
59
+ dst = os.path.join(CACHE, f"{insurer}__{name}")
60
+ if os.path.exists(dst):
61
+ print(f"skip {os.path.basename(dst)}")
62
+ continue
63
+ if not os.path.exists(src):
64
+ print(f"MISSING {src}")
65
+ continue
66
+ try:
67
+ with pdfplumber.open(src) as pdf:
68
+ # Extract up to 30 pages worth (most policy details in first ~20)
69
+ pages = pdf.pages[:30]
70
+ text = "\n".join((p.extract_text() or "") for p in pages)
71
+ with open(dst, "w", encoding="utf-8") as f:
72
+ f.write(text)
73
+ print(f"OK {os.path.basename(dst)} ({len(text)} chars, {len(pages)} pages)")
74
+ except Exception as e:
75
+ print(f"ERR {src}: {e}")
76
+ print("Done.")