ST-x-Tony commited on
Commit
33871a2
·
verified ·
1 Parent(s): cc9f818

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -42
app.py CHANGED
@@ -3,6 +3,68 @@
3
  # Dual‑Model + Web Research · Gradio Space
4
  # ============================================================
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  from __future__ import annotations
7
 
8
  import os
@@ -22,7 +84,7 @@ from gradio_client import Client
22
  # ============================================================
23
 
24
  APP_NAME = "X-RUDRA"
25
- VERSION = "3.6.0"
26
 
27
  M1_REPO = os.getenv("M1_REPO", "Shrijanagain/M1")
28
  M2_REPO = os.getenv("M2_REPO", "Shrijanagain/M2")
@@ -125,10 +187,11 @@ def call_model(client, prompt, max_tokens=512, temperature=0.7):
125
 
126
 
127
  # ============================================================
128
- # SYNTHESIS: M1 (draft) M2 (refine) + extract thinking
129
  # ============================================================
130
 
131
  def get_combined_model_answer(question, sources, max_tokens=512, temperature=0.7):
 
132
  top_sources = sources[:5] if sources else []
133
  sources_text = ""
134
  if top_sources:
@@ -139,8 +202,7 @@ def get_combined_model_answer(question, sources, max_tokens=512, temperature=0.7
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}
@@ -152,11 +214,12 @@ Answer:"""
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
- draft = call_model(m2_client, prompt_m1, max_tokens, temperature)
158
 
159
- if not draft:
 
160
  if sources:
161
  parts = ["Based on available information:"]
162
  for i, src in enumerate(sources[:5], 1):
@@ -167,27 +230,37 @@ Answer:"""
167
  else:
168
  return "I couldn't find specific information on that topic. Could you rephrase?", ""
169
 
170
- # ----- 2. M2 refines the draft -----
171
- prompt_m2 = f"""Question: {question}
 
 
 
172
 
173
- Draft answer:
174
- {draft}
175
 
176
- 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.
 
177
 
178
- Improved answer:"""
 
179
 
180
- refined = call_model(m2_client, prompt_m2, max_tokens, temperature)
181
- if not refined:
182
- refined = draft
 
 
 
 
 
183
 
184
  # Extract thinking from <think> tags (if any)
185
  thinking_content = ""
186
- clean_answer = refined
187
- think_match = re.search(r"<think>(.*?)</think>", refined, re.DOTALL)
188
  if think_match:
189
  thinking_content = think_match.group(1).strip()
190
- clean_answer = re.sub(r"<think>.*?</think>", "", refined, flags=re.DOTALL).strip()
191
 
192
  return clean_answer, thinking_content
193
 
@@ -312,8 +385,8 @@ def build_activity(data, elapsed_ms):
312
  | Stage | Status |
313
  |---|---|
314
  | Task analysis | ✅ Complete |
315
- | M1 research | ✅ Generated draft |
316
- | M2 research | ✅ Refined answer |
317
  | Web discovery | ✅ Complete |
318
  | Evidence extraction | {"✅" if claims else "⚙️"} |
319
  | Source verification | ✅ Complete |
@@ -360,25 +433,32 @@ def safe_dict(value):
360
 
361
 
362
  # ============================================================
363
- # MAIN RESEARCH FUNCTION (returns thinking as well)
364
  # ============================================================
365
 
366
  async def do_research(question, max_results, max_rounds, use_models, freshness):
 
 
 
 
 
 
 
367
  if not question or not str(question).strip():
368
- return [], "⚪ Enter a question to start.", "", "", ""
369
 
370
  question = str(question).strip()
371
 
372
- # Casual query – skip heavy research
373
  if is_casual_query(question):
374
  answer = get_casual_model_response(question)
375
  history = [
376
  {"role": "user", "content": question},
377
  {"role": "assistant", "content": answer}
378
  ]
379
- return history, "⚡ Casual chat (model reply, no search).", "", "", ""
380
 
381
- # Serious query – web search + two‑stage synthesis
382
  started = time.perf_counter()
383
  try:
384
  engine = get_engine()
@@ -408,20 +488,15 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
408
  })
409
  data["sources"] = sources
410
 
411
- # Generate final answer using M1 + M2, also get thinking
412
  final_answer, thinking_content = get_combined_model_answer(question, sources)
413
 
414
- # Build outputs for tabs
415
  sources_md = format_sources(sources)
416
  evidence_md = format_evidence(data.get("claims", []))
417
  verification_md = format_verification(data.get("contradictions", []))
418
  activity_md = build_activity(data, elapsed_ms)
419
 
420
- # If there's thinking content, format it nicely
421
- if thinking_content:
422
- thinking_md = f"### 🧠 Thinking\n\n{thinking_content}"
423
- else:
424
- thinking_md = ""
425
 
426
  history = [
427
  {"role": "user", "content": question},
@@ -467,7 +542,7 @@ def health_check():
467
 
468
 
469
  # ============================================================
470
- # CSS – includes spinner animation for thinking
471
  # ============================================================
472
 
473
  CSS = """
@@ -480,7 +555,6 @@ body { background: #f7f7f8; }
480
  #send { min-height: 52px; font-size: 18px; font-weight: 700; }
481
  footer { display: none !important; }
482
 
483
- /* Thinking spinner animation */
484
  @keyframes think-pulse {
485
  0% { opacity: 0.3; transform: scale(0.95); }
486
  50% { opacity: 1; transform: scale(1.05); }
@@ -509,10 +583,10 @@ footer { display: none !important; }
509
 
510
 
511
  # ============================================================
512
- # GRADIO UI – added thinking output
513
  # ============================================================
514
 
515
- with gr.Blocks(title=APP_NAME, css=CSS) as demo:
516
  gr.HTML("""
517
  <div id="header">
518
  <div id="logo">⚡ X-RUDRA</div>
@@ -530,8 +604,7 @@ with gr.Blocks(title=APP_NAME, css=CSS) as demo:
530
  with gr.Column(scale=4):
531
  gr.Markdown("## 🔬 Live Research")
532
  activity = gr.Markdown("⚪ Waiting for your question.")
533
- # Thinking output will appear here
534
- thinking = gr.Markdown("", elem_id="thinking", visible=False)
535
  gr.Markdown("---")
536
  gr.Markdown(f"""
537
  ### Model Spaces
@@ -572,7 +645,6 @@ with gr.Blocks(title=APP_NAME, css=CSS) as demo:
572
  inputs=question
573
  )
574
 
575
- # Update outputs: added thinking
576
  inputs = [question, max_results, max_rounds, use_models, freshness]
577
  outputs = [chatbot, activity, sources, evidence, verification, thinking]
578
 
@@ -594,4 +666,4 @@ if __name__ == "__main__":
594
  print("HF_TOKEN set – rate limits reduced.")
595
  else:
596
  print("HF_TOKEN not set – you may experience rate limits. Set it as a Secret in your Space.")
597
- demo.launch(server_name="0.0.0.0", server_port=PORT, show_error=True)
 
3
  # Dual‑Model + Web Research · Gradio Space
4
  # ============================================================
5
 
6
+ # ──────────────────────────────────────────────────────────────
7
+ # REASONING‑ENFORCED AGENT CONTRACT (AGENT.md)
8
+ # This file is the operating policy for the entire assistant.
9
+ # For every request, the agent MUST:
10
+ # - Understand the goal and requirements.
11
+ # - Check constraints and missing information.
12
+ # - Decompose complex tasks and track dependencies.
13
+ # - Plan, execute, verify, and adapt.
14
+ # Private chain‑of‑thought is NEVER exposed.
15
+ # Only concise reasoning summaries are shown.
16
+ # See the full policy in the multi‑line comment below.
17
+ # ──────────────────────────────────────────────────────────────
18
+
19
+ """
20
+ # REASONING-ENFORCED AGENT
21
+
22
+ ## 1. CORE DIRECTIVE
23
+ You are a reasoning-first agent.
24
+ For every meaningful request, you MUST determine:
25
+ 1. What the user wants.
26
+ 2. The actual goal.
27
+ 3. Mandatory requirements.
28
+ 4. Constraints.
29
+ 5. Missing information.
30
+ 6. Ambiguity.
31
+ 7. Task decomposition.
32
+ 8. Dependencies.
33
+ 9. Execution order.
34
+ 10. Priorities.
35
+ 11. Evidence supporting key decisions.
36
+ 12. How the result will be verified.
37
+
38
+ Do NOT jump from input to output without reasoning.
39
+
40
+ ## 2. MANDATORY REASONING GATE
41
+ INPUT → UNDERSTAND → GOAL → REQUIREMENTS → CONSTRAINTS
42
+ → MISSING INFO → AMBIGUITY → DECOMPOSITION → DEPENDENCIES
43
+ → PRIORITY → PLAN → EXECUTE → VERIFY → FINALIZE
44
+
45
+ ## 3. NEVER SKIP CRITICAL ANALYSIS
46
+ Goal identification, requirement extraction, constraint checking,
47
+ missing‑information detection, dependency checking, completion verification.
48
+
49
+ ## 4. RESPONSE MODE
50
+ DIRECT, EXPLANATORY, PLANNED, CLARIFICATION, DIAGNOSTIC, EXECUTION, REVISION.
51
+
52
+ ## 5. ANTI‑FABRICATION
53
+ Never invent missing requirements, tool results, files, test results,
54
+ execution, verification, sources, or completion.
55
+
56
+ ## 6. REASONING VISIBILITY
57
+ The agent MUST reason internally but NEVER expose private chain‑of‑thought.
58
+ Provide concise summaries (Approach, Key assumptions, Decision, Blocker, Verification, Result) only when useful.
59
+
60
+ ## 7. FINAL CONTRACT
61
+ UNDERSTAND → PLAN → ACT → VERIFY → ADAPT → DELIVER
62
+ Primary objective:
63
+ "Produce the most correct, goal‑aligned, constraint‑compliant,
64
+ evidence‑supported, verified result using the minimum effective
65
+ reasoning and execution required."
66
+ """
67
+
68
  from __future__ import annotations
69
 
70
  import os
 
84
  # ============================================================
85
 
86
  APP_NAME = "X-RUDRA"
87
+ VERSION = "3.7.0" # bumped version
88
 
89
  M1_REPO = os.getenv("M1_REPO", "Shrijanagain/M1")
90
  M2_REPO = os.getenv("M2_REPO", "Shrijanagain/M2")
 
187
 
188
 
189
  # ============================================================
190
+ # SYNTHESIS: M1 + M2 independent drafts M2 merges them
191
  # ============================================================
192
 
193
  def get_combined_model_answer(question, sources, max_tokens=512, temperature=0.7):
194
+ # Build a summary of top sources (max 5)
195
  top_sources = sources[:5] if sources else []
196
  sources_text = ""
197
  if top_sources:
 
202
  else:
203
  sources_text = "No specific information available."
204
 
205
+ base_prompt = f"""Question: {question}
 
206
 
207
  Information:
208
  {sources_text}
 
214
  m1_client = get_m1_client()
215
  m2_client = get_m2_client()
216
 
217
+ # ----- 1. Get independent drafts from M1 and M2 -----
218
+ draft_m1 = call_model(m1_client, base_prompt, max_tokens, temperature)
219
+ draft_m2 = call_model(m2_client, base_prompt, max_tokens, temperature)
220
 
221
+ # Fallback if one fails
222
+ if not draft_m1 and not draft_m2:
223
  if sources:
224
  parts = ["Based on available information:"]
225
  for i, src in enumerate(sources[:5], 1):
 
230
  else:
231
  return "I couldn't find specific information on that topic. Could you rephrase?", ""
232
 
233
+ # If only one draft exists, use that as final
234
+ if not draft_m1:
235
+ return draft_m2, ""
236
+ if not draft_m2:
237
+ return draft_m1, ""
238
 
239
+ # ----- 2. Merge both drafts using M2 -----
240
+ merge_prompt = f"""Question: {question}
241
 
242
+ Draft from Model A:
243
+ {draft_m1}
244
 
245
+ Draft from Model B:
246
+ {draft_m2}
247
 
248
+ Combine these two drafts into a single, comprehensive, accurate, and natural answer. Keep the best parts from each. Ensure the final answer directly addresses the question, is well‑structured, and reads as a single coherent response. Do NOT mention that you are combining drafts or that you used multiple models. Just provide the final answer.
249
+
250
+ Final answer:"""
251
+
252
+ merged = call_model(m2_client, merge_prompt, max_tokens, temperature)
253
+ if not merged:
254
+ # If merging fails, fallback to draft_m1 (or draft_m2)
255
+ merged = draft_m1
256
 
257
  # Extract thinking from <think> tags (if any)
258
  thinking_content = ""
259
+ clean_answer = merged
260
+ think_match = re.search(r"<think>(.*?)</think>", merged, re.DOTALL)
261
  if think_match:
262
  thinking_content = think_match.group(1).strip()
263
+ clean_answer = re.sub(r"<think>.*?</think>", "", merged, flags=re.DOTALL).strip()
264
 
265
  return clean_answer, thinking_content
266
 
 
385
  | Stage | Status |
386
  |---|---|
387
  | Task analysis | ✅ Complete |
388
+ | M1 research | ✅ Draft generated |
389
+ | M2 research | ✅ Draft generated + merged |
390
  | Web discovery | ✅ Complete |
391
  | Evidence extraction | {"✅" if claims else "⚙️"} |
392
  | Source verification | ✅ Complete |
 
433
 
434
 
435
  # ============================================================
436
+ # MAIN RESEARCH FUNCTION (always returns 6 outputs)
437
  # ============================================================
438
 
439
  async def do_research(question, max_results, max_rounds, use_models, freshness):
440
+ empty_history = []
441
+ empty_activity = "⚪ Enter a question to start."
442
+ empty_sources = ""
443
+ empty_evidence = ""
444
+ empty_verification = ""
445
+ empty_thinking = ""
446
+
447
  if not question or not str(question).strip():
448
+ return empty_history, empty_activity, empty_sources, empty_evidence, empty_verification, empty_thinking
449
 
450
  question = str(question).strip()
451
 
452
+ # ---- Casual query ----
453
  if is_casual_query(question):
454
  answer = get_casual_model_response(question)
455
  history = [
456
  {"role": "user", "content": question},
457
  {"role": "assistant", "content": answer}
458
  ]
459
+ return history, "⚡ Casual chat (model reply, no search).", "", "", "", ""
460
 
461
+ # ---- Serious query ----
462
  started = time.perf_counter()
463
  try:
464
  engine = get_engine()
 
488
  })
489
  data["sources"] = sources
490
 
491
+ # Generate final answer and thinking (using new dual‑draft + merge)
492
  final_answer, thinking_content = get_combined_model_answer(question, sources)
493
 
 
494
  sources_md = format_sources(sources)
495
  evidence_md = format_evidence(data.get("claims", []))
496
  verification_md = format_verification(data.get("contradictions", []))
497
  activity_md = build_activity(data, elapsed_ms)
498
 
499
+ thinking_md = f"### 🧠 Thinking\n\n{thinking_content}" if thinking_content else ""
 
 
 
 
500
 
501
  history = [
502
  {"role": "user", "content": question},
 
542
 
543
 
544
  # ============================================================
545
+ # CSS – includes spinner animation
546
  # ============================================================
547
 
548
  CSS = """
 
555
  #send { min-height: 52px; font-size: 18px; font-weight: 700; }
556
  footer { display: none !important; }
557
 
 
558
  @keyframes think-pulse {
559
  0% { opacity: 0.3; transform: scale(0.95); }
560
  50% { opacity: 1; transform: scale(1.05); }
 
583
 
584
 
585
  # ============================================================
586
+ # GRADIO UI – 6 outputs
587
  # ============================================================
588
 
589
+ with gr.Blocks(title=APP_NAME) as demo:
590
  gr.HTML("""
591
  <div id="header">
592
  <div id="logo">⚡ X-RUDRA</div>
 
604
  with gr.Column(scale=4):
605
  gr.Markdown("## 🔬 Live Research")
606
  activity = gr.Markdown("⚪ Waiting for your question.")
607
+ thinking = gr.Markdown("", visible=True)
 
608
  gr.Markdown("---")
609
  gr.Markdown(f"""
610
  ### Model Spaces
 
645
  inputs=question
646
  )
647
 
 
648
  inputs = [question, max_results, max_rounds, use_models, freshness]
649
  outputs = [chatbot, activity, sources, evidence, verification, thinking]
650
 
 
666
  print("HF_TOKEN set – rate limits reduced.")
667
  else:
668
  print("HF_TOKEN not set – you may experience rate limits. Set it as a Secret in your Space.")
669
+ demo.launch(server_name="0.0.0.0", server_port=PORT, css=CSS, show_error=True)