Prakyath01 commited on
Commit
f7f504f
·
verified ·
1 Parent(s): 4a351f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -122
app.py CHANGED
@@ -1,20 +1,10 @@
1
  import os
2
  import requests
3
- import json
4
- from bs4 import BeautifulSoup
5
- from textwrap import shorten
6
-
7
  import gradio as gr
8
 
9
- from langchain_core.documents import Document
10
- from langchain_text_splitters import RecursiveCharacterTextSplitter
11
- from langchain_community.vectorstores import Chroma
12
- from langchain_community.embeddings import HuggingFaceEmbeddings
13
 
14
- # -----------------------
15
- # 1. SCRAPE K8S DOCS
16
- # -----------------------
17
- urls = {
18
  "pods": "https://kubernetes.io/docs/concepts/workloads/pods/",
19
  "deployments": "https://kubernetes.io/docs/concepts/workloads/controllers/deployment/",
20
  "services": "https://kubernetes.io/docs/concepts/services-networking/service/",
@@ -27,121 +17,113 @@ urls = {
27
  "autoscaling": "https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/"
28
  }
29
 
30
- def scrape_docs():
31
- docs = []
32
- for name, url in urls.items():
33
- try:
34
- r = requests.get(url, timeout=20)
35
- soup = BeautifulSoup(r.text, "html.parser")
36
- content = soup.find("div", class_="td-content")
37
- if not content:
38
- continue
39
- text = content.get_text(separator="\n").strip()
40
- docs.append(Document(page_content=text, metadata={"doc_id": name, "url": url}))
41
- except Exception:
42
- continue
43
- return docs
44
-
45
- docs = scrape_docs()
46
-
47
- # -----------------------
48
- # 2. CHUNK + EMBED + VECTOR DB
49
- # -----------------------
50
- splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100)
51
- chunks = splitter.split_documents(docs)
52
-
53
- embedding = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
54
- vectordb = Chroma.from_documents(chunks, embedding)
55
- retriever = vectordb.as_retriever(
56
- search_type="similarity_score_threshold",
57
- search_kwargs={"k": 5, "score_threshold": 0.4}
58
- )
59
-
60
- # -----------------------
61
- # 3. RAG HELPERS
62
- # -----------------------
63
- def build_context_with_citations(query: str):
64
- retrieved_docs = retriever.invoke(query)
65
- context = ""
66
- mapping = []
67
-
68
- for i, d in enumerate(retrieved_docs, start=1):
69
- label = f"[{i}]"
70
- context += f"{label} {d.page_content[:1000]}\n\nSource: {d.metadata['url']}\n\n"
71
- mapping.append({
72
- "label": label,
73
- "url": d.metadata["url"],
74
- "doc": d.metadata["doc_id"],
75
- "preview": shorten(d.page_content, width=200)
76
- })
77
- return context, mapping
78
-
79
- def build_prompt(query, context):
80
- return f"""
81
- You are a Kubernetes expert.
82
- Use ONLY the context below.
83
- Add citations like [1][2] after each fact.
84
- If not found, say: 'Not in docs'.
85
-
86
- QUESTION:
87
- {query}
88
-
89
- CONTEXT:
90
- {context}
91
- """.strip()
92
-
93
- # -----------------------
94
- # 4. OPENROUTER LLM
95
- # -----------------------
96
- import requests as req
97
-
98
- OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "")
99
-
100
- def call_llm(prompt: str) -> str:
101
- if not OPENROUTER_API_KEY:
102
- return "OpenRouter API key is not set. Please configure OPENROUTER_API_KEY in the Space settings."
103
-
104
  url = "https://openrouter.ai/api/v1/chat/completions"
105
  headers = {
106
- "Authorization": f"Bearer {OPENROUTER_API_KEY}",
107
- "Content-Type": "application/json"
 
108
  }
 
109
  data = {
110
  "model": "meta-llama/llama-3.1-8b-instruct",
111
- "messages": [
112
- {"role": "system", "content": "You are a Kubernetes expert. Only use provided context."},
113
- {"role": "user", "content": prompt}
114
- ],
115
- "temperature": 0.0
116
  }
117
- response = req.post(url, headers=headers, data=json.dumps(data))
118
- out = response.json()
119
- return out.get("choices", [{"message": {"content": "No response"}}])[0]["message"]["content"]
120
 
121
- def answer_question(query: str):
122
- context, sources = build_context_with_citations(query)
123
- prompt = build_prompt(query, context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  answer = call_llm(prompt)
125
- return answer, sources
126
-
127
- # -----------------------
128
- # 5. GRADIO CHAT APP
129
- # -----------------------
130
- def chat_fn(message, history):
131
- answer, sources = answer_question(message)
132
- src_lines = [f"{s['label']} – {s['url']}" for s in sources]
133
- sources_text = "\n".join(src_lines) if src_lines else "No sources found."
134
- full_answer = f"{answer}\n\n---\nSources:\n{sources_text}"
135
- return full_answer
136
-
137
- demo = gr.ChatInterface(
138
- fn=chat_fn,
139
- title="Kubernetes RAG Assistant",
140
- description="Ask Kubernetes questions. Answers are grounded in official docs and include citations."
141
- )
142
-
143
- def main():
144
- return demo
145
-
146
- if __name__ == "__main__":
147
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import requests
 
 
 
 
3
  import gradio as gr
4
 
5
+ # ---------------- RAG DOCUMENT SETUP ---------------- #
 
 
 
6
 
7
+ K8S_DOC_URLS = {
 
 
 
8
  "pods": "https://kubernetes.io/docs/concepts/workloads/pods/",
9
  "deployments": "https://kubernetes.io/docs/concepts/workloads/controllers/deployment/",
10
  "services": "https://kubernetes.io/docs/concepts/services-networking/service/",
 
17
  "autoscaling": "https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/"
18
  }
19
 
20
+ def fetch_doc(url):
21
+ try:
22
+ response = requests.get(url, timeout=10)
23
+ if response.status_code == 200:
24
+ return response.text
25
+ except:
26
+ return ""
27
+ return ""
28
+
29
+ DOCUMENTS = [
30
+ {"doc": name, "url": url, "text": fetch_doc(url)}
31
+ for name, url in K8S_DOC_URLS.items()
32
+ ]
33
+
34
+ def search_docs(query, top_k=3):
35
+ query = query.lower()
36
+ matches = []
37
+ for doc in DOCUMENTS:
38
+ text = doc["text"].lower()
39
+ if query in text:
40
+ snippet_start = text.index(query)
41
+ snippet_end = snippet_start + 350
42
+ snippet = doc["text"][snippet_start:snippet_end].replace("\n", " ")
43
+ matches.append((snippet, doc["url"], doc["doc"]))
44
+ return matches[:top_k]
45
+
46
+
47
+ # --------------- LLM CALL (OpenRouter) ---------------- #
48
+
49
+ def call_llm(prompt):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  url = "https://openrouter.ai/api/v1/chat/completions"
51
  headers = {
52
+ "Authorization": f"Bearer {os.getenv('OPENROUTER_API_KEY')}",
53
+ "HTTP-Referer": "https://huggingface.co/",
54
+ "X-Title": "Kubernetes RAG Assistant"
55
  }
56
+
57
  data = {
58
  "model": "meta-llama/llama-3.1-8b-instruct",
59
+ "messages": [{"role": "user", "content": prompt}],
60
+ "max_tokens": 350
 
 
 
61
  }
 
 
 
62
 
63
+ res = requests.post(url, json=data, headers=headers)
64
+ out = res.json()
65
+
66
+ if "choices" in out:
67
+ return out["choices"][0]["message"]["content"]
68
+ print("DEBUG LLM Error:", out)
69
+ return "⚠ Model error. Try again."
70
+
71
+
72
+ # ----------- RAG + Prompt Construction ---------------- #
73
+
74
+ def build_answer(query):
75
+ results = search_docs(query)
76
+ context = ""
77
+ citations = []
78
+
79
+ for i, (snippet, url, doc) in enumerate(results, start=1):
80
+ label = f"[{i}]"
81
+ context += f"{label}: {snippet}\n\n"
82
+ citations.append(f"{label} → {url}")
83
+
84
+ prompt = f"""
85
+ Use the context below to answer the question clearly.
86
+ Add citations like [1], [2] at the end of sentences.
87
+
88
+ Context:
89
+ {context}
90
+
91
+ Question: {query}
92
+ """
93
+
94
  answer = call_llm(prompt)
95
+ citations_text = "\n".join(citations) or "No sources found."
96
+
97
+ return answer, citations_text
98
+
99
+
100
+ # ---------------------- UI --------------------------- #
101
+
102
+ custom_css = """
103
+ .source-box {
104
+ font-size: 14px;
105
+ background: #1b2733;
106
+ padding: 10px;
107
+ border-radius: 8px;
108
+ color: #c9e2ff;
109
+ border: 1px solid #4a90e2;
110
+ }
111
+ """
112
+
113
+ with gr.Blocks(css=custom_css, theme="soft") as app:
114
+
115
+ gr.HTML("""
116
+ <h1 style='color:#326ce5; text-align:center;'>☸️ Kubernetes RAG Assistant</h1>
117
+ <p style='text-align:center; font-size:17px; color:#ddd;'>Ask any Kubernetes question and get answers with docs citations 📌</p>
118
+ """)
119
+
120
+ question = gr.Textbox(label="Ask a Kubernetes Question:", placeholder="e.g., What is RBAC in Kubernetes?")
121
+
122
+ answer = gr.Markdown(label="Answer")
123
+ sources = gr.Markdown(label="Sources", elem_classes=["source-box"])
124
+
125
+ submit = gr.Button("Ask ☸️")
126
+
127
+ submit.click(build_answer, inputs=question, outputs=[answer, sources])
128
+
129
+ app.launch()