Vineetiitg commited on
Commit
ce52a54
·
1 Parent(s): 8358ef7

feat: add LLM-based query rewriting

Browse files
app/engine/query_transform.py CHANGED
@@ -1,5 +1,12 @@
 
1
  import re
2
 
 
 
 
 
 
 
3
 
4
  def normalize_query(query: str) -> str:
5
  return re.sub(r"\s+", " ", query).strip()
@@ -8,8 +15,34 @@ def normalize_query(query: str) -> str:
8
  def query_variants(query: str) -> list[str]:
9
  normalized = normalize_query(query)
10
  variants = [normalized]
11
- if "error" in normalized.lower() and "troubleshoot" not in normalized.lower():
12
- variants.append(f"troubleshoot {normalized}")
13
- if "how" in normalized.lower() and "steps" not in normalized.lower():
14
- variants.append(f"{normalized} steps")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  return list(dict.fromkeys(variants))
 
1
+ import json
2
  import re
3
 
4
+ from langchain_core.prompts import PromptTemplate
5
+ from langchain_ollama import ChatOllama
6
+
7
+ from app.core.config import settings
8
+ from app.core.logging import logger
9
+
10
 
11
  def normalize_query(query: str) -> str:
12
  return re.sub(r"\s+", " ", query).strip()
 
15
  def query_variants(query: str) -> list[str]:
16
  normalized = normalize_query(query)
17
  variants = [normalized]
18
+
19
+ try:
20
+ llm = ChatOllama(
21
+ model=settings.OLLAMA_MODEL,
22
+ temperature=0,
23
+ format="json",
24
+ base_url=settings.OLLAMA_BASE_URL
25
+ )
26
+ prompt = PromptTemplate(
27
+ template="""You are an expert technical support assistant.
28
+ Your goal is to generate 2 alternative phrasing variants for the user's question to improve retrieval accuracy.
29
+ Return ONLY a JSON object with a single key 'variants' containing a list of strings.
30
+
31
+ User Question: {question}""",
32
+ input_variables=["question"],
33
+ )
34
+ chain = prompt | llm
35
+ result = chain.invoke({"question": normalized})
36
+
37
+ parsed = json.loads(result.content)
38
+ new_variants = parsed.get("variants", [])
39
+
40
+ if isinstance(new_variants, list):
41
+ for variant in new_variants:
42
+ if isinstance(variant, str) and variant.strip():
43
+ variants.append(variant.strip())
44
+
45
+ except Exception as e:
46
+ logger.warning(f"Failed to generate query variants with LLM: {e}")
47
+
48
  return list(dict.fromkeys(variants))
tests/test_retriever.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from unittest.mock import patch
2
+
3
+ from app.engine.query_transform import normalize_query, query_variants
4
+
5
+
6
+ def test_query_normalization_collapses_whitespace():
7
+ assert normalize_query(" reset password \n now ") == "reset password now"
8
+
9
+
10
+ @patch("app.engine.query_transform.ChatOllama")
11
+ def test_query_variants_add_helpful_expansions(mock_chat):
12
+ mock_instance = mock_chat.return_value
13
+ class MockResult:
14
+ content = '{"variants": ["troubleshoot error 404 steps"]}'
15
+
16
+ mock_chain_invoke = mock_instance.invoke
17
+ mock_chain_invoke.return_value = MockResult()
18
+
19
+ # We also have to mock the prompt | llm chain, which returns a RunnableSequence
20
+ # A simpler way is to mock the chain.invoke, but it's built inline.
21
+ # Let's mock ChatOllama.invoke to return the expected json if it's called directly by prompt | llm? No, ChatOllama gets passed prompt string.
22
+ # We can patch ChatOllama.invoke
23
+ mock_instance.invoke.return_value = MockResult()
24
+
25
+ variants = query_variants("How to fix error 404?")
26
+
27
+ assert "How to fix error 404?" in variants
28
+ # The LLM mock adds "troubleshoot error 404 steps"
29
+ assert "troubleshoot error 404 steps" in variants