Spaces:
Running
Running
feat: fix LLM model names, increase RAG retrieval and memory limits
Browse files- rag/constants.py +8 -4
- rag/rag.py +31 -12
rag/constants.py
CHANGED
|
@@ -5,12 +5,16 @@ BATCH_WINDOW_S = 0.05
|
|
| 5 |
WARMUP_INTERVAL_S = 300
|
| 6 |
|
| 7 |
# Web search configuration — Wiki + DDG for topics, DDG only for PDF/URL materials.
|
| 8 |
-
WIKI_TOP_K_RESULTS =
|
| 9 |
-
WIKI_DOC_CONTENT_CHARS_MAX =
|
| 10 |
|
| 11 |
# DuckDuckGO Search
|
| 12 |
-
DUCKDUCKGO_NUM_RESULTS =
|
| 13 |
-
DUCKDUCKGO_DOC_CONTENT_CHARS_MAX =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
RAG_PROMPT_TEMPLATE_BASE = """\
|
| 16 |
<role>
|
|
|
|
| 5 |
WARMUP_INTERVAL_S = 300
|
| 6 |
|
| 7 |
# Web search configuration — Wiki + DDG for topics, DDG only for PDF/URL materials.
|
| 8 |
+
WIKI_TOP_K_RESULTS = 2 # Number of top Wikipedia articles retrieved
|
| 9 |
+
WIKI_DOC_CONTENT_CHARS_MAX = 3500 # Max chars from Wikipedia results
|
| 10 |
|
| 11 |
# DuckDuckGO Search
|
| 12 |
+
DUCKDUCKGO_NUM_RESULTS = 8 # Number of DDG snippet results returned per search
|
| 13 |
+
DUCKDUCKGO_DOC_CONTENT_CHARS_MAX = 6000 # Max chars kept from combined DDG result block
|
| 14 |
+
|
| 15 |
+
# RAG & Memory Configuration
|
| 16 |
+
MEMORY_WINDOW_SIZE = 20 # Number of previous conversation turns (40 messages) preserved in memory window
|
| 17 |
+
TOP_K_CHUNKS = 8 # Number of top relevant material chunks retrieved for context
|
| 18 |
|
| 19 |
RAG_PROMPT_TEMPLATE_BASE = """\
|
| 20 |
<role>
|
rag/rag.py
CHANGED
|
@@ -25,6 +25,8 @@ from .constants import (
|
|
| 25 |
WIKI_DOC_CONTENT_CHARS_MAX,
|
| 26 |
DUCKDUCKGO_NUM_RESULTS,
|
| 27 |
DUCKDUCKGO_DOC_CONTENT_CHARS_MAX,
|
|
|
|
|
|
|
| 28 |
)
|
| 29 |
from .schemas import EmbeddingJob
|
| 30 |
|
|
@@ -163,12 +165,29 @@ def get_quiz_llm():
|
|
| 163 |
)
|
| 164 |
|
| 165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
def get_gemma_31b_llm():
|
| 167 |
if not os.environ.get("GEMINI_API_KEY"):
|
| 168 |
raise ValueError("GEMINI_API_KEY not found. Please set it in config.env.")
|
| 169 |
-
logger.info("Initializing primary LLM with model:
|
| 170 |
return ChatGoogleGenerativeAI(
|
| 171 |
-
model="
|
| 172 |
api_key=settings.gemini_api_key,
|
| 173 |
temperature=0.3,
|
| 174 |
max_output_tokens=2500,
|
|
@@ -179,9 +198,9 @@ def get_gemma_31b_llm():
|
|
| 179 |
def get_gemma_26b_llm():
|
| 180 |
if not os.environ.get("GEMINI_API_KEY"):
|
| 181 |
raise ValueError("GEMINI_API_KEY not found. Please set it in config.env.")
|
| 182 |
-
logger.info("Initializing fallback LLM with model:
|
| 183 |
return ChatGoogleGenerativeAI(
|
| 184 |
-
model="
|
| 185 |
api_key=settings.gemini_api_key,
|
| 186 |
temperature=0.3,
|
| 187 |
max_output_tokens=2500,
|
|
@@ -228,7 +247,7 @@ def direct_wiki_search(query: str) -> str:
|
|
| 228 |
|
| 229 |
class SupabaseRetriever(BaseRetriever):
|
| 230 |
material_id: str
|
| 231 |
-
k: int =
|
| 232 |
|
| 233 |
def _get_relevant_documents(self, query: str) -> list[Document]:
|
| 234 |
results = similarity_search(query, self.material_id, self.k)
|
|
@@ -289,7 +308,7 @@ def rag_answer(
|
|
| 289 |
):
|
| 290 |
if memory is None:
|
| 291 |
memory = ConversationBufferWindowMemory(
|
| 292 |
-
input_key="input", memory_key="chat_history", return_messages=True, k=
|
| 293 |
)
|
| 294 |
|
| 295 |
# Fetch material info if material_id is provided
|
|
@@ -308,7 +327,7 @@ def rag_answer(
|
|
| 308 |
|
| 309 |
if not is_topic:
|
| 310 |
# --- Material-based query (PDF/URL): vector similarity search ---
|
| 311 |
-
results = similarity_search(query, material_id, k=
|
| 312 |
if results:
|
| 313 |
has_chunks = True
|
| 314 |
chunks = [r["content"] for r in results]
|
|
@@ -380,13 +399,13 @@ def rag_answer(
|
|
| 380 |
"agent_scratchpad": "",
|
| 381 |
})
|
| 382 |
|
| 383 |
-
answer = response.content
|
| 384 |
# Only persist non-refusal answers to memory
|
| 385 |
if not _is_refusal(answer):
|
| 386 |
memory.save_context({"input": query}, {"output": answer})
|
| 387 |
return answer, memory
|
| 388 |
except Exception as e:
|
| 389 |
-
logger.warning(f"Gemma 4 31B API call failed or rate-limited: {e}. Falling back to
|
| 390 |
try:
|
| 391 |
fallback_llm = get_gemma_26b_llm()
|
| 392 |
chain = prompt | fallback_llm
|
|
@@ -399,7 +418,7 @@ def rag_answer(
|
|
| 399 |
"chat_history": chat_history,
|
| 400 |
"agent_scratchpad": "",
|
| 401 |
})
|
| 402 |
-
answer = response.content
|
| 403 |
# Only persist non-refusal answers to memory
|
| 404 |
if not _is_refusal(answer):
|
| 405 |
memory.save_context({"input": query}, {"output": answer})
|
|
@@ -425,7 +444,7 @@ def extract_chat_title(query: str, material_title: Optional[str] = None) -> str:
|
|
| 425 |
chain = prompt | primary_llm
|
| 426 |
response = chain.invoke({"query": query})
|
| 427 |
except Exception as e:
|
| 428 |
-
logger.warning(f"Gemma 4 31B API call failed or rate-limited in extract_chat_title: {e}. Falling back to
|
| 429 |
try:
|
| 430 |
fallback_llm = get_gemma_26b_llm()
|
| 431 |
chain = prompt | fallback_llm
|
|
@@ -434,7 +453,7 @@ def extract_chat_title(query: str, material_title: Optional[str] = None) -> str:
|
|
| 434 |
logger.error(f"Fallback Gemma 4 26B LLM call also failed in extract_chat_title: {fallback_err}")
|
| 435 |
raise fallback_err
|
| 436 |
|
| 437 |
-
title = response.content.strip().strip('"').strip("'")
|
| 438 |
if len(title) > 50:
|
| 439 |
title = title[:50].rsplit(' ', 1)[0] + '...'
|
| 440 |
return title
|
|
|
|
| 25 |
WIKI_DOC_CONTENT_CHARS_MAX,
|
| 26 |
DUCKDUCKGO_NUM_RESULTS,
|
| 27 |
DUCKDUCKGO_DOC_CONTENT_CHARS_MAX,
|
| 28 |
+
MEMORY_WINDOW_SIZE,
|
| 29 |
+
TOP_K_CHUNKS,
|
| 30 |
)
|
| 31 |
from .schemas import EmbeddingJob
|
| 32 |
|
|
|
|
| 165 |
)
|
| 166 |
|
| 167 |
|
| 168 |
+
def _clean_llm_response(content) -> str:
|
| 169 |
+
if isinstance(content, str):
|
| 170 |
+
return content
|
| 171 |
+
if isinstance(content, list):
|
| 172 |
+
texts = []
|
| 173 |
+
for part in content:
|
| 174 |
+
if isinstance(part, str):
|
| 175 |
+
texts.append(part)
|
| 176 |
+
elif isinstance(part, dict):
|
| 177 |
+
if part.get("type") == "text":
|
| 178 |
+
texts.append(part.get("text", ""))
|
| 179 |
+
elif "text" in part and part.get("type") != "thinking":
|
| 180 |
+
texts.append(part.get("text", ""))
|
| 181 |
+
return "\n".join(t for t in texts if t)
|
| 182 |
+
return str(content)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
def get_gemma_31b_llm():
|
| 186 |
if not os.environ.get("GEMINI_API_KEY"):
|
| 187 |
raise ValueError("GEMINI_API_KEY not found. Please set it in config.env.")
|
| 188 |
+
logger.info("Initializing primary LLM with model: gemma-4-31b-it")
|
| 189 |
return ChatGoogleGenerativeAI(
|
| 190 |
+
model="gemma-4-31b-it",
|
| 191 |
api_key=settings.gemini_api_key,
|
| 192 |
temperature=0.3,
|
| 193 |
max_output_tokens=2500,
|
|
|
|
| 198 |
def get_gemma_26b_llm():
|
| 199 |
if not os.environ.get("GEMINI_API_KEY"):
|
| 200 |
raise ValueError("GEMINI_API_KEY not found. Please set it in config.env.")
|
| 201 |
+
logger.info("Initializing fallback LLM with model: gemma-4-26b-a4b-it")
|
| 202 |
return ChatGoogleGenerativeAI(
|
| 203 |
+
model="gemma-4-26b-a4b-it",
|
| 204 |
api_key=settings.gemini_api_key,
|
| 205 |
temperature=0.3,
|
| 206 |
max_output_tokens=2500,
|
|
|
|
| 247 |
|
| 248 |
class SupabaseRetriever(BaseRetriever):
|
| 249 |
material_id: str
|
| 250 |
+
k: int = TOP_K_CHUNKS
|
| 251 |
|
| 252 |
def _get_relevant_documents(self, query: str) -> list[Document]:
|
| 253 |
results = similarity_search(query, self.material_id, self.k)
|
|
|
|
| 308 |
):
|
| 309 |
if memory is None:
|
| 310 |
memory = ConversationBufferWindowMemory(
|
| 311 |
+
input_key="input", memory_key="chat_history", return_messages=True, k=MEMORY_WINDOW_SIZE
|
| 312 |
)
|
| 313 |
|
| 314 |
# Fetch material info if material_id is provided
|
|
|
|
| 327 |
|
| 328 |
if not is_topic:
|
| 329 |
# --- Material-based query (PDF/URL): vector similarity search ---
|
| 330 |
+
results = similarity_search(query, material_id, k=TOP_K_CHUNKS)
|
| 331 |
if results:
|
| 332 |
has_chunks = True
|
| 333 |
chunks = [r["content"] for r in results]
|
|
|
|
| 399 |
"agent_scratchpad": "",
|
| 400 |
})
|
| 401 |
|
| 402 |
+
answer = _clean_llm_response(response.content)
|
| 403 |
# Only persist non-refusal answers to memory
|
| 404 |
if not _is_refusal(answer):
|
| 405 |
memory.save_context({"input": query}, {"output": answer})
|
| 406 |
return answer, memory
|
| 407 |
except Exception as e:
|
| 408 |
+
logger.warning(f"Gemma 4 31B API call failed or rate-limited: {e}. Falling back to gemma-4-26b-a4b-it immediately.")
|
| 409 |
try:
|
| 410 |
fallback_llm = get_gemma_26b_llm()
|
| 411 |
chain = prompt | fallback_llm
|
|
|
|
| 418 |
"chat_history": chat_history,
|
| 419 |
"agent_scratchpad": "",
|
| 420 |
})
|
| 421 |
+
answer = _clean_llm_response(response.content)
|
| 422 |
# Only persist non-refusal answers to memory
|
| 423 |
if not _is_refusal(answer):
|
| 424 |
memory.save_context({"input": query}, {"output": answer})
|
|
|
|
| 444 |
chain = prompt | primary_llm
|
| 445 |
response = chain.invoke({"query": query})
|
| 446 |
except Exception as e:
|
| 447 |
+
logger.warning(f"Gemma 4 31B API call failed or rate-limited in extract_chat_title: {e}. Falling back to gemma-4-26b-a4b-it immediately.")
|
| 448 |
try:
|
| 449 |
fallback_llm = get_gemma_26b_llm()
|
| 450 |
chain = prompt | fallback_llm
|
|
|
|
| 453 |
logger.error(f"Fallback Gemma 4 26B LLM call also failed in extract_chat_title: {fallback_err}")
|
| 454 |
raise fallback_err
|
| 455 |
|
| 456 |
+
title = _clean_llm_response(response.content).strip().strip('"').strip("'")
|
| 457 |
if len(title) > 50:
|
| 458 |
title = title[:50].rsplit(' ', 1)[0] + '...'
|
| 459 |
return title
|