Update app.py
Browse files
app.py
CHANGED
|
@@ -32,7 +32,7 @@ from gradio_client import Client
|
|
| 32 |
# ============================================================
|
| 33 |
|
| 34 |
APP_NAME = "X-RUDRA"
|
| 35 |
-
VERSION = "3.
|
| 36 |
|
| 37 |
M1_REPO = os.getenv("M1_REPO", "Shrijanagain/M1")
|
| 38 |
M2_REPO = os.getenv("M2_REPO", "Shrijanagain/M2")
|
|
@@ -74,29 +74,74 @@ def get_m2_client():
|
|
| 74 |
|
| 75 |
|
| 76 |
# ============================================================
|
| 77 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
# ============================================================
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
|
| 102 |
# ============================================================
|
|
@@ -182,7 +227,7 @@ Answer:"""
|
|
| 182 |
else:
|
| 183 |
return "I couldn't find specific information on that topic. Could you rephrase?", ""
|
| 184 |
|
| 185 |
-
# If only one draft exists, use that as final
|
| 186 |
if not draft_m1:
|
| 187 |
thinking = ""
|
| 188 |
clean = draft_m2
|
|
@@ -214,17 +259,14 @@ Combine these two drafts into a single, comprehensive, accurate, and natural ans
|
|
| 214 |
|
| 215 |
Final answer:"""
|
| 216 |
|
| 217 |
-
|
| 218 |
-
merge_max_tokens = max(1024, max_tokens * 2) # at least 1024, double the original
|
| 219 |
merged = call_model(m2_client, merge_prompt, merge_max_tokens, temperature)
|
| 220 |
|
| 221 |
-
# If merged is too short (incomplete), retry with even more tokens
|
| 222 |
if merged and len(merged) < 100:
|
| 223 |
print(f"Merged answer too short ({len(merged)} chars), retrying with 2048 tokens...")
|
| 224 |
merged = call_model(m2_client, merge_prompt, 2048, temperature)
|
| 225 |
|
| 226 |
if not merged:
|
| 227 |
-
# Fallback to draft_m1 if merge fails completely
|
| 228 |
print("Merge failed, falling back to draft_m1")
|
| 229 |
merged = draft_m1
|
| 230 |
|
|
@@ -249,7 +291,7 @@ def get_casual_model_response(query: str) -> str:
|
|
| 249 |
try:
|
| 250 |
result = client.predict(
|
| 251 |
prompt=f"User: {query}\nAssistant:",
|
| 252 |
-
max_tokens=
|
| 253 |
temperature=0.7,
|
| 254 |
api_name="/generate"
|
| 255 |
)
|
|
@@ -264,7 +306,7 @@ def get_casual_model_response(query: str) -> str:
|
|
| 264 |
try:
|
| 265 |
result = client.predict(
|
| 266 |
prompt=f"User: {query}\nAssistant:",
|
| 267 |
-
max_tokens=
|
| 268 |
temperature=0.7,
|
| 269 |
api_name="/generate"
|
| 270 |
)
|
|
@@ -409,7 +451,7 @@ def safe_dict(value):
|
|
| 409 |
|
| 410 |
|
| 411 |
# ============================================================
|
| 412 |
-
# MAIN RESEARCH FUNCTION
|
| 413 |
# ============================================================
|
| 414 |
|
| 415 |
async def do_research(question, max_results, max_rounds, use_models, freshness):
|
|
@@ -425,8 +467,12 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
|
|
| 425 |
|
| 426 |
question = str(question).strip()
|
| 427 |
|
| 428 |
-
# ----
|
| 429 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
answer = get_casual_model_response(question)
|
| 431 |
history = [
|
| 432 |
{"role": "user", "content": question},
|
|
@@ -434,7 +480,7 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
|
|
| 434 |
]
|
| 435 |
return history, "β‘ Casual chat (model reply, no search).", "", "", "", ""
|
| 436 |
|
| 437 |
-
# ----
|
| 438 |
started = time.perf_counter()
|
| 439 |
try:
|
| 440 |
engine = get_engine()
|
|
@@ -464,7 +510,7 @@ async def do_research(question, max_results, max_rounds, use_models, freshness):
|
|
| 464 |
})
|
| 465 |
data["sources"] = sources
|
| 466 |
|
| 467 |
-
# Generate final answer and thinking
|
| 468 |
final_answer, thinking_content = get_combined_model_answer(question, sources)
|
| 469 |
|
| 470 |
sources_md = format_sources(sources)
|
|
@@ -518,7 +564,7 @@ def health_check():
|
|
| 518 |
|
| 519 |
|
| 520 |
# ============================================================
|
| 521 |
-
# CSS
|
| 522 |
# ============================================================
|
| 523 |
|
| 524 |
CSS = """
|
|
|
|
| 32 |
# ============================================================
|
| 33 |
|
| 34 |
APP_NAME = "X-RUDRA"
|
| 35 |
+
VERSION = "3.8.1"
|
| 36 |
|
| 37 |
M1_REPO = os.getenv("M1_REPO", "Shrijanagain/M1")
|
| 38 |
M2_REPO = os.getenv("M2_REPO", "Shrijanagain/M2")
|
|
|
|
| 74 |
|
| 75 |
|
| 76 |
# ============================================================
|
| 77 |
+
# MODEL-BASED INTENT CLASSIFIER
|
| 78 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 79 |
+
# Uses M1 (or M2) to decide ACTION or CASUAL.
|
| 80 |
+
# No hardcoded keywords β the model decides.
|
| 81 |
+
# If both fail, defaults to ACTION.
|
| 82 |
# ============================================================
|
| 83 |
|
| 84 |
+
CLASSIFIER_SYSTEM_PROMPT = """
|
| 85 |
+
You are an intelligent assistant that classifies user messages into two categories:
|
| 86 |
+
- ACTION: The user asks for information that requires research, factβchecking, retrieval of current data, or external knowledge. This includes questions about news, comparisons, statistics, history, technology, science, politics, etc.
|
| 87 |
+
- CASUAL: The user is just chatting, greeting, making small talk, joking, or asking a simple question that can be answered from general knowledge without a search.
|
| 88 |
+
|
| 89 |
+
Respond with ONLY ONE WORD: ACTION or CASUAL.
|
| 90 |
+
Do NOT add any extra text, punctuation, or explanation.
|
| 91 |
+
"""
|
| 92 |
+
|
| 93 |
+
def classify_intent(question: str) -> str:
|
| 94 |
+
prompt = f"{CLASSIFIER_SYSTEM_PROMPT}\n\nUser message: \"{question}\"\n\nClassification:"
|
| 95 |
+
|
| 96 |
+
# Try M1 first
|
| 97 |
+
m1_client = get_m1_client()
|
| 98 |
+
if m1_client is not None:
|
| 99 |
+
try:
|
| 100 |
+
result = m1_client.predict(
|
| 101 |
+
prompt=prompt,
|
| 102 |
+
max_tokens=10,
|
| 103 |
+
temperature=0.0,
|
| 104 |
+
api_name="/generate"
|
| 105 |
+
)
|
| 106 |
+
if result:
|
| 107 |
+
result = result.strip().upper()
|
| 108 |
+
if "ACTION" in result:
|
| 109 |
+
print(f"[Classifier] M1 β ACTION")
|
| 110 |
+
return "ACTION"
|
| 111 |
+
elif "CASUAL" in result:
|
| 112 |
+
print(f"[Classifier] M1 β CASUAL")
|
| 113 |
+
return "CASUAL"
|
| 114 |
+
else:
|
| 115 |
+
print(f"[Classifier] M1 ambiguous: {result}")
|
| 116 |
+
except Exception as e:
|
| 117 |
+
print(f"[Classifier] M1 call failed: {e}")
|
| 118 |
+
|
| 119 |
+
# Try M2 as fallback
|
| 120 |
+
m2_client = get_m2_client()
|
| 121 |
+
if m2_client is not None:
|
| 122 |
+
try:
|
| 123 |
+
result = m2_client.predict(
|
| 124 |
+
prompt=prompt,
|
| 125 |
+
max_tokens=10,
|
| 126 |
+
temperature=0.0,
|
| 127 |
+
api_name="/generate"
|
| 128 |
+
)
|
| 129 |
+
if result:
|
| 130 |
+
result = result.strip().upper()
|
| 131 |
+
if "ACTION" in result:
|
| 132 |
+
print(f"[Classifier] M2 β ACTION")
|
| 133 |
+
return "ACTION"
|
| 134 |
+
elif "CASUAL" in result:
|
| 135 |
+
print(f"[Classifier] M2 β CASUAL")
|
| 136 |
+
return "CASUAL"
|
| 137 |
+
else:
|
| 138 |
+
print(f"[Classifier] M2 ambiguous: {result}")
|
| 139 |
+
except Exception as e:
|
| 140 |
+
print(f"[Classifier] M2 call failed: {e}")
|
| 141 |
+
|
| 142 |
+
# Default to ACTION if classification fails
|
| 143 |
+
print("[Classifier] Defaulting to ACTION")
|
| 144 |
+
return "ACTION"
|
| 145 |
|
| 146 |
|
| 147 |
# ============================================================
|
|
|
|
| 227 |
else:
|
| 228 |
return "I couldn't find specific information on that topic. Could you rephrase?", ""
|
| 229 |
|
| 230 |
+
# If only one draft exists, use that as final
|
| 231 |
if not draft_m1:
|
| 232 |
thinking = ""
|
| 233 |
clean = draft_m2
|
|
|
|
| 259 |
|
| 260 |
Final answer:"""
|
| 261 |
|
| 262 |
+
merge_max_tokens = max(1024, max_tokens * 2)
|
|
|
|
| 263 |
merged = call_model(m2_client, merge_prompt, merge_max_tokens, temperature)
|
| 264 |
|
|
|
|
| 265 |
if merged and len(merged) < 100:
|
| 266 |
print(f"Merged answer too short ({len(merged)} chars), retrying with 2048 tokens...")
|
| 267 |
merged = call_model(m2_client, merge_prompt, 2048, temperature)
|
| 268 |
|
| 269 |
if not merged:
|
|
|
|
| 270 |
print("Merge failed, falling back to draft_m1")
|
| 271 |
merged = draft_m1
|
| 272 |
|
|
|
|
| 291 |
try:
|
| 292 |
result = client.predict(
|
| 293 |
prompt=f"User: {query}\nAssistant:",
|
| 294 |
+
max_tokens=128,
|
| 295 |
temperature=0.7,
|
| 296 |
api_name="/generate"
|
| 297 |
)
|
|
|
|
| 306 |
try:
|
| 307 |
result = client.predict(
|
| 308 |
prompt=f"User: {query}\nAssistant:",
|
| 309 |
+
max_tokens=128,
|
| 310 |
temperature=0.7,
|
| 311 |
api_name="/generate"
|
| 312 |
)
|
|
|
|
| 451 |
|
| 452 |
|
| 453 |
# ============================================================
|
| 454 |
+
# MAIN RESEARCH FUNCTION
|
| 455 |
# ============================================================
|
| 456 |
|
| 457 |
async def do_research(question, max_results, max_rounds, use_models, freshness):
|
|
|
|
| 467 |
|
| 468 |
question = str(question).strip()
|
| 469 |
|
| 470 |
+
# ---- Step 1: Classify intent using M1 (or M2) ----
|
| 471 |
+
intent = classify_intent(question)
|
| 472 |
+
print(f"[Intent] {intent} for: {question}")
|
| 473 |
+
|
| 474 |
+
# ---- Step 2: If casual, reply directly ----
|
| 475 |
+
if intent == "CASUAL":
|
| 476 |
answer = get_casual_model_response(question)
|
| 477 |
history = [
|
| 478 |
{"role": "user", "content": question},
|
|
|
|
| 480 |
]
|
| 481 |
return history, "β‘ Casual chat (model reply, no search).", "", "", "", ""
|
| 482 |
|
| 483 |
+
# ---- Step 3: ACTION β run research pipeline ----
|
| 484 |
started = time.perf_counter()
|
| 485 |
try:
|
| 486 |
engine = get_engine()
|
|
|
|
| 510 |
})
|
| 511 |
data["sources"] = sources
|
| 512 |
|
| 513 |
+
# Generate final answer and thinking
|
| 514 |
final_answer, thinking_content = get_combined_model_answer(question, sources)
|
| 515 |
|
| 516 |
sources_md = format_sources(sources)
|
|
|
|
| 564 |
|
| 565 |
|
| 566 |
# ============================================================
|
| 567 |
+
# CSS
|
| 568 |
# ============================================================
|
| 569 |
|
| 570 |
CSS = """
|