ST-x-Tony commited on
Commit
355fee6
·
verified ·
1 Parent(s): 2bc9710

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -106
app.py CHANGED
@@ -1,6 +1,6 @@
1
  # ============================================================
2
  # X-RUDRA CHAT
3
- # Hugging Face Gradio Space Edition
4
  # ============================================================
5
 
6
  from __future__ import annotations
@@ -21,24 +21,19 @@ from gradio_client import Client
21
  # ============================================================
22
 
23
  APP_NAME = "X-RUDRA"
24
- VERSION = "3.3.1"
25
 
26
- # M1 and M2 Spaces – change these to your own if needed
27
  M1_REPO = os.getenv("M1_REPO", "Shrijanagain/M1")
28
  M2_REPO = os.getenv("M2_REPO", "Shrijanagain/M2")
29
  PORT = int(os.getenv("PORT", "7860"))
30
 
31
- # ------------------------------------------------------------
32
- # HF_TOKEN is recommended to avoid rate limits
33
- # Set it as a Secret in your Space settings.
34
- # ------------------------------------------------------------
35
  HF_TOKEN = os.getenv("HF_TOKEN")
36
  if HF_TOKEN:
37
- os.environ["HF_TOKEN"] = HF_TOKEN # ensures the transformers lib uses it
38
 
39
 
40
  # ============================================================
41
- # GRADIO CLIENTS FOR M1 / M2 (LAZY)
42
  # ============================================================
43
 
44
  _M1_CLIENT = None
@@ -48,7 +43,6 @@ def get_m1_client():
48
  global _M1_CLIENT
49
  if _M1_CLIENT is None:
50
  try:
51
- # Construct the public URL of the Space
52
  url = f"https://{M1_REPO.replace('/', '-')}.hf.space"
53
  _M1_CLIENT = Client(url)
54
  except Exception as e:
@@ -73,10 +67,14 @@ def get_m2_client():
73
  # ============================================================
74
 
75
  def is_casual_query(text: str) -> bool:
76
- """Return True if the query is casual/timepass."""
77
  text = text.lower().strip()
78
- if len(text.split()) <= 2:
 
79
  return True
 
 
 
 
80
  casual_patterns = [
81
  "hey", "hi", "hello", "yo", "what's up", "how are you",
82
  "good morning", "good evening", "good night",
@@ -85,13 +83,13 @@ def is_casual_query(text: str) -> bool:
85
  "who are you", "what can you do", "help", "thanks"
86
  ]
87
  for pattern in casual_patterns:
88
- if text.startswith(pattern) or pattern in text:
89
  return True
90
  return False
91
 
92
 
93
  # ============================================================
94
- # LAZY ENGINE (for serious queries)
95
  # ============================================================
96
 
97
  _ENGINE = None
@@ -104,6 +102,129 @@ def get_engine():
104
  return _ENGINE
105
 
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  # ============================================================
108
  # FORMATTERS (Sources, Evidence, Verification)
109
  # ============================================================
@@ -174,14 +295,6 @@ def format_verification(contradictions):
174
  return "\n\n".join(output)
175
 
176
 
177
- def extract_answer(data):
178
- for key in ("final_answer", "answer", "response", "final", "synthesis", "summary"):
179
- val = data.get(key)
180
- if isinstance(val, str) and val.strip():
181
- return val.strip()
182
- return None
183
-
184
-
185
  def build_activity(data, elapsed_ms):
186
  sources = data.get("sources", []) or data.get("results", [])
187
  claims = data.get("claims", [])
@@ -193,13 +306,13 @@ def build_activity(data, elapsed_ms):
193
  | Stage | Status |
194
  |---|---|
195
  | Task analysis | ✅ Complete |
196
- | M1 research | {"Enabled" if data.get("m1") is not None else "⚙️ Pipeline"} |
197
- | M2 research | {"Enabled" if data.get("m2") is not None else "⚙️ Pipeline"} |
198
  | Web discovery | ✅ Complete |
199
- | Evidence extraction | {"✅" if data.get("claims") else "⚙️"} |
200
  | Source verification | ✅ Complete |
201
  | Contradiction check | {"⚠️ Found" if contradictions else "✅ Clear"} |
202
- | Final synthesis | {"" if extract_answer(data) else "⚙️"} |
203
 
204
  **Sources:** `{len(sources)}`
205
  **Claims:** `{len(claims)}`
@@ -240,50 +353,6 @@ def safe_dict(value):
240
  return {"result": str(value)}
241
 
242
 
243
- # ============================================================
244
- # GET MODEL RESPONSE FOR CASUAL QUERIES
245
- # ============================================================
246
-
247
- def get_casual_model_response(query: str) -> str:
248
- """Call M1 (or M2) to generate a friendly reply for casual queries."""
249
- # Try M1 first
250
- client = get_m1_client()
251
- if client is not None:
252
- try:
253
- # Assuming the endpoint is /generate with inputs: prompt, max_tokens, temperature
254
- result = client.predict(
255
- prompt=f"User: {query}\nAssistant:",
256
- max_tokens=64,
257
- temperature=0.7,
258
- api_name="/generate"
259
- )
260
- if result and isinstance(result, str) and result.strip():
261
- return result.strip()
262
- except Exception as e:
263
- print(f"M1 casual call failed: {e}")
264
-
265
- # Try M2
266
- client = get_m2_client()
267
- if client is not None:
268
- try:
269
- result = client.predict(
270
- prompt=f"User: {query}\nAssistant:",
271
- max_tokens=64,
272
- temperature=0.7,
273
- api_name="/generate"
274
- )
275
- if result and isinstance(result, str) and result.strip():
276
- return result.strip()
277
- except Exception as e:
278
- print(f"M2 casual call failed: {e}")
279
-
280
- # Ultimate fallback
281
- return (
282
- f"👋 Hi there! I'm X‑RUDRA, your research assistant. "
283
- f"How can I help you today? (Your message `{query}` was casual, so I kept it light.)"
284
- )
285
-
286
-
287
  # ============================================================
288
  # MAIN RESEARCH FUNCTION
289
  # ============================================================
@@ -294,9 +363,7 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
294
 
295
  question = str(question).strip()
296
 
297
- # ------------------------------------------------------------
298
- # CASUAL QUERY – get a model‑generated reply (no web search)
299
- # ------------------------------------------------------------
300
  if is_casual_query(question):
301
  answer = get_casual_model_response(question)
302
  history = [
@@ -305,11 +372,8 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
305
  ]
306
  return history, "⚡ Casual chat (model reply, no search).", "", "", ""
307
 
308
- # ------------------------------------------------------------
309
- # SERIOUS QUERY – run the engine (web + models)
310
- # ------------------------------------------------------------
311
  started = time.perf_counter()
312
-
313
  try:
314
  engine = get_engine()
315
  report = await engine.search(
@@ -322,12 +386,6 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
322
  data = safe_dict(report)
323
  elapsed_ms = int((time.perf_counter() - started) * 1000)
324
 
325
- # Debug (optional)
326
- print("\n" + "="*60)
327
- print("RAW ENGINE DATA (first 3000 chars):")
328
- print(json.dumps(data, indent=2, default=str)[:3000])
329
- print("="*60 + "\n")
330
-
331
  # Convert 'results' to 'sources' if needed
332
  sources = data.get("sources", [])
333
  if not sources:
@@ -344,29 +402,18 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
344
  })
345
  data["sources"] = sources
346
 
347
- # Extract or synthesise answer
348
- answer = extract_answer(data)
349
- if answer is None:
350
- if sources:
351
- top = sources[:5]
352
- parts = [f"Based on the top results:"]
353
- for i, src in enumerate(top, 1):
354
- title = src.get("title", "Untitled")
355
- snippet = src.get("snippet", src.get("description", ""))
356
- parts.append(f"{i}. **{title}** – {snippet[:200]}..." if snippet else f"{i}. **{title}**")
357
- answer = "\n\n".join(parts)
358
- else:
359
- answer = "No information found. Try rephrasing your question."
360
-
361
- claims = data.get("claims", [])
362
  sources_md = format_sources(sources)
363
- evidence_md = format_evidence(claims)
364
  verification_md = format_verification(data.get("contradictions", []))
365
  activity_md = build_activity(data, elapsed_ms)
366
 
367
  history = [
368
  {"role": "user", "content": question},
369
- {"role": "assistant", "content": answer}
370
  ]
371
 
372
  return history, activity_md, sources_md, evidence_md, verification_md
@@ -408,7 +455,7 @@ def health_check():
408
 
409
 
410
  # ============================================================
411
- # CSS
412
  # ============================================================
413
 
414
  CSS = """
@@ -423,15 +470,11 @@ footer { display: none !important; }
423
  """
424
 
425
 
426
- # ============================================================
427
- # GRADIO UI
428
- # ============================================================
429
-
430
  with gr.Blocks(title=APP_NAME) as demo:
431
  gr.HTML("""
432
  <div id="header">
433
  <div id="logo">⚡ X-RUDRA</div>
434
- <div id="tagline">Dual-Model AI · Live Web Research · Evidence</div>
435
  </div>
436
  """)
437
 
 
1
  # ============================================================
2
  # X-RUDRA CHAT
3
+ # Dual‑Model + Web Research · Gradio Space
4
  # ============================================================
5
 
6
  from __future__ import annotations
 
21
  # ============================================================
22
 
23
  APP_NAME = "X-RUDRA"
24
+ VERSION = "3.5.0" # bumped version
25
 
 
26
  M1_REPO = os.getenv("M1_REPO", "Shrijanagain/M1")
27
  M2_REPO = os.getenv("M2_REPO", "Shrijanagain/M2")
28
  PORT = int(os.getenv("PORT", "7860"))
29
 
 
 
 
 
30
  HF_TOKEN = os.getenv("HF_TOKEN")
31
  if HF_TOKEN:
32
+ os.environ["HF_TOKEN"] = HF_TOKEN
33
 
34
 
35
  # ============================================================
36
+ # GRADIO CLIENTS FOR M1 / M2
37
  # ============================================================
38
 
39
  _M1_CLIENT = None
 
43
  global _M1_CLIENT
44
  if _M1_CLIENT is None:
45
  try:
 
46
  url = f"https://{M1_REPO.replace('/', '-')}.hf.space"
47
  _M1_CLIENT = Client(url)
48
  except Exception as e:
 
67
  # ============================================================
68
 
69
  def is_casual_query(text: str) -> bool:
 
70
  text = text.lower().strip()
71
+ words = text.split()
72
+ if len(words) <= 2:
73
  return True
74
+ question_words = {"what", "how", "why", "when", "where", "who", "which",
75
+ "can", "could", "would", "will", "is", "are", "do", "does"}
76
+ if words[0] in question_words and len(words) >= 3:
77
+ return False
78
  casual_patterns = [
79
  "hey", "hi", "hello", "yo", "what's up", "how are you",
80
  "good morning", "good evening", "good night",
 
83
  "who are you", "what can you do", "help", "thanks"
84
  ]
85
  for pattern in casual_patterns:
86
+ if text == pattern or text.startswith(pattern + " "):
87
  return True
88
  return False
89
 
90
 
91
  # ============================================================
92
+ # LAZY ENGINE (web search)
93
  # ============================================================
94
 
95
  _ENGINE = None
 
102
  return _ENGINE
103
 
104
 
105
+ # ============================================================
106
+ # HELPERS – CALL MODELS
107
+ # ============================================================
108
+
109
+ def call_model(client, prompt, max_tokens=512, temperature=0.7):
110
+ if client is None:
111
+ return None
112
+ try:
113
+ result = client.predict(
114
+ prompt=prompt,
115
+ max_tokens=max_tokens,
116
+ temperature=temperature,
117
+ api_name="/generate"
118
+ )
119
+ if result and isinstance(result, str) and result.strip():
120
+ return result.strip()
121
+ except Exception as e:
122
+ print(f"Model call failed: {e}")
123
+ return None
124
+
125
+
126
+ # ============================================================
127
+ # SYNTHESIS: M1 (draft) → M2 (refine)
128
+ # ============================================================
129
+
130
+ def get_combined_model_answer(question, sources, max_tokens=512, temperature=0.7):
131
+ # Build a concise summary of top sources (max 5)
132
+ top_sources = sources[:5] if sources else []
133
+ sources_text = ""
134
+ if top_sources:
135
+ for i, src in enumerate(top_sources, 1):
136
+ title = src.get("title", "Untitled")
137
+ snippet = src.get("snippet", src.get("description", ""))
138
+ sources_text += f"{i}. {title}: {snippet[:300]}\n"
139
+ else:
140
+ sources_text = "No specific information available."
141
+
142
+ # ----- 1. M1 generates draft answer -----
143
+ prompt_m1 = f"""Question: {question}
144
+
145
+ Information:
146
+ {sources_text}
147
+
148
+ Based on the information above and your knowledge, provide a comprehensive, accurate, and well‑structured answer to the question. Be direct and natural – write as if you are an expert answering a user.
149
+
150
+ Answer:"""
151
+
152
+ m1_client = get_m1_client()
153
+ m2_client = get_m2_client()
154
+
155
+ draft = call_model(m1_client, prompt_m1, max_tokens, temperature)
156
+ if not draft:
157
+ # Fallback: try M2 directly for draft
158
+ draft = call_model(m2_client, prompt_m1, max_tokens, temperature)
159
+
160
+ if not draft:
161
+ # Ultimate fallback – simple summarization
162
+ if sources:
163
+ parts = ["Based on available information:"]
164
+ for i, src in enumerate(sources[:5], 1):
165
+ title = src.get("title", "Untitled")
166
+ snippet = src.get("snippet", src.get("description", ""))
167
+ parts.append(f"{i}. {title}: {snippet[:200]}..." if snippet else f"{i}. {title}")
168
+ return "\n\n".join(parts)
169
+ else:
170
+ return "I couldn't find specific information on that topic. Could you rephrase?"
171
+
172
+ # ----- 2. M2 refines the draft -----
173
+ prompt_m2 = f"""Question: {question}
174
+
175
+ Draft answer:
176
+ {draft}
177
+
178
+ Please refine and improve this answer to make it more comprehensive, accurate, and natural. Ensure it directly addresses the question. Provide only the final improved answer, without any extra commentary or meta‑references.
179
+
180
+ Improved answer:"""
181
+
182
+ refined = call_model(m2_client, prompt_m2, max_tokens, temperature)
183
+ if refined:
184
+ return refined
185
+ else:
186
+ return draft
187
+
188
+
189
+ # ============================================================
190
+ # CASUAL REPLY (calls M1 or M2)
191
+ # ============================================================
192
+
193
+ def get_casual_model_response(query: str) -> str:
194
+ client = get_m1_client()
195
+ if client is not None:
196
+ try:
197
+ result = client.predict(
198
+ prompt=f"User: {query}\nAssistant:",
199
+ max_tokens=64,
200
+ temperature=0.7,
201
+ api_name="/generate"
202
+ )
203
+ if result and isinstance(result, str) and result.strip():
204
+ return result.strip()
205
+ except Exception as e:
206
+ print(f"M1 casual failed: {e}")
207
+
208
+ client = get_m2_client()
209
+ if client is not None:
210
+ try:
211
+ result = client.predict(
212
+ prompt=f"User: {query}\nAssistant:",
213
+ max_tokens=64,
214
+ temperature=0.7,
215
+ api_name="/generate"
216
+ )
217
+ if result and isinstance(result, str) and result.strip():
218
+ return result.strip()
219
+ except Exception as e:
220
+ print(f"M2 casual failed: {e}")
221
+
222
+ return (
223
+ f"👋 Hi there! I'm X‑RUDRA, your research assistant. "
224
+ f"How can I help you today? (Your message `{query}` was casual, so I kept it light.)"
225
+ )
226
+
227
+
228
  # ============================================================
229
  # FORMATTERS (Sources, Evidence, Verification)
230
  # ============================================================
 
295
  return "\n\n".join(output)
296
 
297
 
 
 
 
 
 
 
 
 
298
  def build_activity(data, elapsed_ms):
299
  sources = data.get("sources", []) or data.get("results", [])
300
  claims = data.get("claims", [])
 
306
  | Stage | Status |
307
  |---|---|
308
  | Task analysis | ✅ Complete |
309
+ | M1 research | ✅ Generated draft |
310
+ | M2 research | ✅ Refined answer |
311
  | Web discovery | ✅ Complete |
312
+ | Evidence extraction | {"✅" if claims else "⚙️"} |
313
  | Source verification | ✅ Complete |
314
  | Contradiction check | {"⚠️ Found" if contradictions else "✅ Clear"} |
315
+ | Final synthesis | ✅ Complete |
316
 
317
  **Sources:** `{len(sources)}`
318
  **Claims:** `{len(claims)}`
 
353
  return {"result": str(value)}
354
 
355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  # ============================================================
357
  # MAIN RESEARCH FUNCTION
358
  # ============================================================
 
363
 
364
  question = str(question).strip()
365
 
366
+ # Casual query – skip heavy research
 
 
367
  if is_casual_query(question):
368
  answer = get_casual_model_response(question)
369
  history = [
 
372
  ]
373
  return history, "⚡ Casual chat (model reply, no search).", "", "", ""
374
 
375
+ # Serious query – web search + two‑stage synthesis
 
 
376
  started = time.perf_counter()
 
377
  try:
378
  engine = get_engine()
379
  report = await engine.search(
 
386
  data = safe_dict(report)
387
  elapsed_ms = int((time.perf_counter() - started) * 1000)
388
 
 
 
 
 
 
 
389
  # Convert 'results' to 'sources' if needed
390
  sources = data.get("sources", [])
391
  if not sources:
 
402
  })
403
  data["sources"] = sources
404
 
405
+ # Generate final answer using M1 + M2
406
+ final_answer = get_combined_model_answer(question, sources)
407
+
408
+ # Build outputs for tabs
 
 
 
 
 
 
 
 
 
 
 
409
  sources_md = format_sources(sources)
410
+ evidence_md = format_evidence(data.get("claims", []))
411
  verification_md = format_verification(data.get("contradictions", []))
412
  activity_md = build_activity(data, elapsed_ms)
413
 
414
  history = [
415
  {"role": "user", "content": question},
416
+ {"role": "assistant", "content": final_answer}
417
  ]
418
 
419
  return history, activity_md, sources_md, evidence_md, verification_md
 
455
 
456
 
457
  # ============================================================
458
+ # CSS AND UI
459
  # ============================================================
460
 
461
  CSS = """
 
470
  """
471
 
472
 
 
 
 
 
473
  with gr.Blocks(title=APP_NAME) as demo:
474
  gr.HTML("""
475
  <div id="header">
476
  <div id="logo">⚡ X-RUDRA</div>
477
+ <div id="tagline">DualModel AI · Live Web Research · Evidence</div>
478
  </div>
479
  """)
480