rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
844ed03
Β·
1 Parent(s): 9a977de

perf: KI-034 retrieval cache + KI-035 reorder FAST_BRAIN_CHAIN

Browse files

KI-034 β€” Retrieval cache. In-process LRU keyed by
(query_text_normalized, top_k, sorted policy_ids, sorted insurer_slugs).
Cap 256 entries; FIFO/LRU eviction so memory stays bounded across long
sessions. Skips both the Voyage embed call AND the Chroma query +
regulatory/review boost passes on cache hit.

Typical chat session has lots of near-identical follow-ups ("waiting
period?" β†’ "PED waiting period?" β†’ "diabetes waiting period?"). Cache key
is lowercased + stripped so trivial reformatting hits the cache too.
Cache invalidates implicitly on process restart (HF Space rebuild).

Smoke: _cache_key("what is the waiting period?", 5, None, None) ==
_cache_key("WHAT IS the Waiting Period? ", 5, None, None) β†’ True
_cache_get returns ['mock_chunk'] for either form.

KI-035 β€” FAST_BRAIN_CHAIN reorder. Fast brain serves the latency-sensitive
roles: fact-find, QA, paraphrase, normalize, extract. Throughput-wise
they're all sub-second by content size, so TTFT dominates perceived
latency, not raw capability. Nemotron Nano 30B benches at ~1.6s TTFT vs
Qwen 80B's ~2-3s, so it now sits at position 0; Qwen 80B drops to
position 1 (still serves on Nemotron pool degradation).

Before: Qwen 80B β†’ Nemotron 30B β†’ GPT-OSS β†’ Qwen 122B β†’ DeepSeek β†’ Groq
After: Nemotron 30B β†’ Qwen 80B β†’ GPT-OSS β†’ Qwen 122B β†’ DeepSeek β†’ Groq

Combined effect on fact-find turns:
β€’ Each turn typically makes 2 fast-brain calls
(normalize_answer + paraphrase_question) + 1 brain call (the actual
reply). With Nemotron primary on those 2 fast calls, ~1s saved per
turn β€” meaningful when persona audits average 9-10s p50 today.

KI-025's NIM↔Groq 50/50 rotation still applies on top: half of all
fast-brain calls now start at Groq Llama-3.3-70B (sub-1s TTFT on LPU)
regardless of the chain reorder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

backend/providers/nvidia_nim_llm.py CHANGED
@@ -220,8 +220,14 @@ BRAIN_CHAIN = [
220
  # is the lowest-TTFT free-tier option, so a fast-brain fall-through to it is
221
  # still acceptable from a latency-budget standpoint.
222
  FAST_BRAIN_CHAIN = [
223
- "qwen/qwen3-next-80b-a3b-instruct",
224
- "nvidia/nemotron-3-nano-30b-a3b", # 1.6s response per Reddit benchmark
 
 
 
 
 
 
225
  "openai/gpt-oss-120b",
226
  "qwen/qwen3.5-122b-a10b",
227
  "deepseek-ai/deepseek-v4-flash",
 
220
  # is the lowest-TTFT free-tier option, so a fast-brain fall-through to it is
221
  # still acceptable from a latency-budget standpoint.
222
  FAST_BRAIN_CHAIN = [
223
+ # KI-035 (2026-05-14) β€” reordered for latency. Fast brain serves
224
+ # fact-find + QA + paraphrase + normalize + extract: every single one
225
+ # of these is a sub-second job by content size, so the bottleneck IS
226
+ # TTFT, not capability. Nemotron Nano 30B hits ~1.6s; Qwen 80B is
227
+ # ~2-3s. Moved Nemotron to primary; Qwen 80B stays as next fallback so
228
+ # if Nemotron's NIM pool degrades we still get quality.
229
+ "nvidia/nemotron-3-nano-30b-a3b", # ~1.6s TTFT (Reddit bench), NIM
230
+ "qwen/qwen3-next-80b-a3b-instruct", # ~2-3s, NIM
231
  "openai/gpt-oss-120b",
232
  "qwen/qwen3.5-122b-a10b",
233
  "deepseek-ai/deepseek-v4-flash",
rag/retrieve.py CHANGED
@@ -102,6 +102,49 @@ def _build_chunk(cid: str, doc: str, meta: dict, score: float) -> RetrievedChunk
102
  )
103
 
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  async def retrieve(
106
  query: str,
107
  top_k: int = settings.RAG_TOP_K,
@@ -121,6 +164,12 @@ async def retrieve(
121
  This ensures the brain sees regulatory ceilings even when policy
122
  chunks dominate raw cosine.
123
  """
 
 
 
 
 
 
124
  embedder = embedder or VoyageEmbeddings()
125
  [query_vec] = await embedder.embed([query], input_type="query")
126
 
@@ -269,6 +318,9 @@ async def retrieve(
269
  # Review boost is additive; failure shouldn't kill the main result
270
  pass
271
 
 
 
 
272
  return out
273
 
274
 
 
102
  )
103
 
104
 
105
+ # KI-034 (2026-05-14) β€” in-process retrieval cache. Keyed by
106
+ # (query_text, top_k, sorted policy_ids, sorted insurer_slugs). Caps at 256
107
+ # entries with FIFO eviction so memory stays bounded across long sessions.
108
+ # Within a single chat session, users frequently rephrase or follow up on the
109
+ # same topic ("what's the waiting period?" β†’ "and the pre-existing diseases
110
+ # waiting period?" β†’ "and the diabetes-specific one?"). When the cache key
111
+ # matches, we skip the Voyage embed call AND the Chroma collection.query() β€”
112
+ # saving the round-trip + the Voyage 3 RPM tax. Cache invalidates implicitly
113
+ # at process restart (HF Space deploy / reload).
114
+ from collections import OrderedDict as _OrderedDict
115
+ _RETRIEVAL_CACHE: "_OrderedDict[tuple, list]" = _OrderedDict()
116
+ _RETRIEVAL_CACHE_MAX = 256
117
+
118
+
119
+ def _cache_key(
120
+ query: str,
121
+ top_k: int,
122
+ policy_ids: Optional[list[str]],
123
+ insurer_slugs: Optional[list[str]],
124
+ ) -> tuple:
125
+ return (
126
+ (query or "").strip().lower(),
127
+ int(top_k),
128
+ tuple(sorted(policy_ids)) if policy_ids else None,
129
+ tuple(sorted(insurer_slugs)) if insurer_slugs else None,
130
+ )
131
+
132
+
133
+ def _cache_get(key: tuple) -> Optional[list]:
134
+ if key not in _RETRIEVAL_CACHE:
135
+ return None
136
+ # LRU bump
137
+ val = _RETRIEVAL_CACHE.pop(key)
138
+ _RETRIEVAL_CACHE[key] = val
139
+ return val
140
+
141
+
142
+ def _cache_set(key: tuple, value: list) -> None:
143
+ _RETRIEVAL_CACHE[key] = value
144
+ while len(_RETRIEVAL_CACHE) > _RETRIEVAL_CACHE_MAX:
145
+ _RETRIEVAL_CACHE.popitem(last=False) # evict oldest
146
+
147
+
148
  async def retrieve(
149
  query: str,
150
  top_k: int = settings.RAG_TOP_K,
 
164
  This ensures the brain sees regulatory ceilings even when policy
165
  chunks dominate raw cosine.
166
  """
167
+ # KI-034 β€” short-circuit identical-query re-asks via the LRU cache.
168
+ cache_key = _cache_key(query, top_k, policy_ids, insurer_slugs)
169
+ cached = _cache_get(cache_key)
170
+ if cached is not None:
171
+ return cached
172
+
173
  embedder = embedder or VoyageEmbeddings()
174
  [query_vec] = await embedder.embed([query], input_type="query")
175
 
 
318
  # Review boost is additive; failure shouldn't kill the main result
319
  pass
320
 
321
+ # KI-034 β€” populate cache with the FINAL merged result so subsequent
322
+ # identical queries skip Voyage embed + Chroma query + boost passes.
323
+ _cache_set(cache_key, out)
324
  return out
325
 
326