Prakyath01 commited on
Commit
3ee432b
·
verified ·
1 Parent(s): c1d9c39

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -22
app.py CHANGED
@@ -3,12 +3,12 @@ import json
3
  import requests
4
  import gradio as gr
5
  from bs4 import BeautifulSoup
6
- from textwrap import shorten
7
 
8
  from langchain_core.documents import Document
9
  from langchain_text_splitters import RecursiveCharacterTextSplitter
10
  from langchain_huggingface import HuggingFaceEmbeddings
11
  from langchain_community.vectorstores import Chroma
 
12
 
13
  # ------------------ SCRAPE KUBERNETES DOCS ------------------ #
14
 
@@ -33,10 +33,7 @@ def scrape_page(name, url):
33
  if not content:
34
  return None
35
  text = content.get_text(separator="\n").strip()
36
- return Document(
37
- page_content=text,
38
- metadata={"doc_id": name, "url": url}
39
- )
40
  except:
41
  return None
42
 
@@ -59,7 +56,34 @@ retriever = vectordb.as_retriever(
59
  search_kwargs={"k": 5, "score_threshold": 0.4}
60
  )
61
 
62
- # ------------------ LLM CALL (OPENROUTER) ------------------ #
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  def call_llm(prompt):
65
  url = "https://openrouter.ai/api/v1/chat/completions"
@@ -72,19 +96,19 @@ def call_llm(prompt):
72
  "model": "meta-llama/llama-3.1-8b-instruct",
73
  "messages": [{"role": "user", "content": prompt}],
74
  "max_tokens": 400,
75
- "temperature": 0.0,
76
  }
77
  r = requests.post(url, headers=headers, json=data)
78
  res = r.json()
79
  if "choices" in res:
80
  return res["choices"][0]["message"]["content"]
81
- print("🚨 LLM ERROR:", res)
82
- return "⚠️ Error: No response from model"
83
 
84
- # ------------------ BUILD ANSWER WITH CITATIONS ------------------ #
85
 
86
  def build_context_with_citations(query):
87
- docs = retriever.invoke(query)
88
  context = ""
89
  sources = []
90
  for i, d in enumerate(docs, start=1):
@@ -96,9 +120,9 @@ def build_context_with_citations(query):
96
  def answer_question(query, history):
97
  context, sources = build_context_with_citations(query)
98
  prompt = f"""
99
- Answer the question strictly using the context below.
100
- Every sentence must include citation like [1], [2].
101
- If missing inforeply: "Not in docs."
102
 
103
  Question: {query}
104
 
@@ -106,8 +130,8 @@ Context:
106
  {context}
107
  """
108
  answer = call_llm(prompt)
109
- src = "\n".join(sources) if sources else "No sources available."
110
- history.append((query, answer + "\n\n---\nSources:\n" + src))
111
  return history, ""
112
 
113
  # ------------------ GRADIO UI ------------------ #
@@ -115,22 +139,21 @@ Context:
115
  custom_css = """
116
  .source-box {
117
  background: #1e293b;
118
- padding: 10px;
119
- border-radius: 8px;
120
  color: #dbeafe;
 
 
121
  border: 1px solid #3b82f6;
122
  }
123
  """
124
 
125
  with gr.Blocks(theme="soft") as app:
126
  gr.HTML(f"<style>{custom_css}</style>")
127
-
128
  gr.HTML("<h1 style='text-align:center;color:#3b82f6'>☸ Kubernetes RAG Assistant</h1>"
129
- "<p style='text-align:center;color:#cbd5e1'>Ask Kubernetes questions answers include official docs citations 📌</p>")
130
 
131
  chat = gr.Chatbot(label="Conversation", height=450)
132
- msg = gr.Textbox(label="Ask a question...", placeholder="What is a pod?")
133
- clear = gr.Button("Clear Chat")
134
 
135
  msg.submit(answer_question, [msg, chat], [chat, msg])
136
  clear.click(lambda: ([], ""), None, [chat, msg])
 
3
  import requests
4
  import gradio as gr
5
  from bs4 import BeautifulSoup
 
6
 
7
  from langchain_core.documents import Document
8
  from langchain_text_splitters import RecursiveCharacterTextSplitter
9
  from langchain_huggingface import HuggingFaceEmbeddings
10
  from langchain_community.vectorstores import Chroma
11
+ from rank_bm25 import BM25Okapi # <-- NEW Hybrid Search Import
12
 
13
  # ------------------ SCRAPE KUBERNETES DOCS ------------------ #
14
 
 
33
  if not content:
34
  return None
35
  text = content.get_text(separator="\n").strip()
36
+ return Document(page_content=text, metadata={"doc_id": name, "url": url})
 
 
 
37
  except:
38
  return None
39
 
 
56
  search_kwargs={"k": 5, "score_threshold": 0.4}
57
  )
58
 
59
+ # ------------------ HYBRID SEARCH ------------------ #
60
+
61
+ bm25_corpus = [doc.page_content.split() for doc in chunks]
62
+ bm25 = BM25Okapi(bm25_corpus)
63
+
64
+ def hybrid_search(query, top_k=5):
65
+ # Vector Search
66
+ vector_results = retriever.invoke(query)
67
+
68
+ # BM25 Keyword Search
69
+ tokenized_query = query.lower().split()
70
+ bm25_scores = bm25.get_scores(tokenized_query)
71
+ bm25_ranked = sorted(zip(bm25_scores, chunks), key=lambda x: x[0], reverse=True)
72
+ bm25_results = [d for _, d in bm25_ranked[:top_k]]
73
+
74
+ # Combine + Remove duplicates
75
+ combined = vector_results + bm25_results
76
+ unique = []
77
+ seen = set()
78
+ for d in combined:
79
+ key = (d.metadata["doc_id"], d.page_content[:50])
80
+ if key not in seen:
81
+ seen.add(key)
82
+ unique.append(d)
83
+
84
+ return unique[:top_k]
85
+
86
+ # ------------------ LLM CALL (OpenRouter) ------------------ #
87
 
88
  def call_llm(prompt):
89
  url = "https://openrouter.ai/api/v1/chat/completions"
 
96
  "model": "meta-llama/llama-3.1-8b-instruct",
97
  "messages": [{"role": "user", "content": prompt}],
98
  "max_tokens": 400,
99
+ "temperature": 0.0
100
  }
101
  r = requests.post(url, headers=headers, json=data)
102
  res = r.json()
103
  if "choices" in res:
104
  return res["choices"][0]["message"]["content"]
105
+ print("LLM ERROR:", res)
106
+ return "⚠️ Model failed. Please retry."
107
 
108
+ # ------------------ RAG + CITATIONS ------------------ #
109
 
110
  def build_context_with_citations(query):
111
+ docs = hybrid_search(query)
112
  context = ""
113
  sources = []
114
  for i, d in enumerate(docs, start=1):
 
120
  def answer_question(query, history):
121
  context, sources = build_context_with_citations(query)
122
  prompt = f"""
123
+ Answer using ONLY the context below.
124
+ Every sentence MUST include citations like [1], [2].
125
+ If the answer is not in docs respond "Not in docs."
126
 
127
  Question: {query}
128
 
 
130
  {context}
131
  """
132
  answer = call_llm(prompt)
133
+ final = answer + "\n\n---\nSources:\n" + "\n".join(sources)
134
+ history.append((query, final))
135
  return history, ""
136
 
137
  # ------------------ GRADIO UI ------------------ #
 
139
  custom_css = """
140
  .source-box {
141
  background: #1e293b;
 
 
142
  color: #dbeafe;
143
+ padding: 10px;
144
+ border-radius: 7px;
145
  border: 1px solid #3b82f6;
146
  }
147
  """
148
 
149
  with gr.Blocks(theme="soft") as app:
150
  gr.HTML(f"<style>{custom_css}</style>")
 
151
  gr.HTML("<h1 style='text-align:center;color:#3b82f6'>☸ Kubernetes RAG Assistant</h1>"
152
+ "<p style='text-align:center;color:#cbd5e1'>Semantic + Hybrid Search Official K8s Docs Cited 📌</p>")
153
 
154
  chat = gr.Chatbot(label="Conversation", height=450)
155
+ msg = gr.Textbox(label="Ask anything about Kubernetes…", placeholder="e.g., What is RBAC?")
156
+ clear = gr.Button("Clear Conversation")
157
 
158
  msg.submit(answer_question, [msg, chat], [chat, msg])
159
  clear.click(lambda: ([], ""), None, [chat, msg])