ST-x-Tony commited on
Commit
0fae2b4
·
verified ·
1 Parent(s): c5b18a7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +400 -0
app.py ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import asyncio
4
+ import gradio as gr
5
+
6
+ from web_search import XrudraWebSearch
7
+
8
+
9
+ # ============================================================
10
+ # X-RUDRA GRADIO APP
11
+ # ============================================================
12
+
13
+ VERSION = "3.0.0"
14
+
15
+ ENGINE = XrudraWebSearch()
16
+
17
+
18
+ # ============================================================
19
+ # ASYNC SEARCH
20
+ # ============================================================
21
+
22
+ async def run_search(
23
+ question: str,
24
+ max_results: int,
25
+ max_rounds: int,
26
+ use_models: bool,
27
+ freshness: str,
28
+ ):
29
+ if not question or not question.strip():
30
+ return (
31
+ " Please enter a question.",
32
+ "",
33
+ "",
34
+ "",
35
+ )
36
+
37
+ try:
38
+ report = await ENGINE.search(
39
+ question=question.strip(),
40
+ max_results=int(max_results),
41
+ max_rounds=int(max_rounds),
42
+ use_models=use_models,
43
+ freshness_mode=freshness,
44
+ )
45
+
46
+ data = (
47
+ report.model_dump()
48
+ if hasattr(report, "model_dump")
49
+ else report
50
+ )
51
+
52
+ # ----------------------------------------------------
53
+ # Sources
54
+ # ----------------------------------------------------
55
+
56
+ sources = data.get("sources", [])
57
+
58
+ source_lines = []
59
+
60
+ for i, source in enumerate(sources, 1):
61
+ title = source.get("title", "Untitled")
62
+ url = source.get("url", "")
63
+ method = source.get("fetch_method", "unknown")
64
+ score = source.get("source_score", 0)
65
+
66
+ source_lines.append(
67
+ f"### {i}. {title}\n"
68
+ f"**URL:** {url}\n"
69
+ f"**Fetch:** `{method}`\n"
70
+ f"**Score:** `{score}`\n"
71
+ )
72
+
73
+ source_text = "\n\n".join(source_lines)
74
+
75
+ # ----------------------------------------------------
76
+ # Claims
77
+ # ----------------------------------------------------
78
+
79
+ claims = data.get("claims", [])
80
+
81
+ claim_lines = []
82
+
83
+ for i, claim in enumerate(claims, 1):
84
+ claim_text = claim.get("claim", "")
85
+ url = claim.get("source_url", "")
86
+ score = claim.get("support_score", 0)
87
+
88
+ claim_lines.append(
89
+ f"### Claim {i}\n"
90
+ f"{claim_text}\n\n"
91
+ f"Source: `{url}`\n"
92
+ f"Support: `{score}`"
93
+ )
94
+
95
+ claim_text = "\n\n".join(claim_lines)
96
+
97
+ # ----------------------------------------------------
98
+ # Contradictions
99
+ # ----------------------------------------------------
100
+
101
+ contradictions = data.get(
102
+ "contradictions",
103
+ [],
104
+ )
105
+
106
+ if contradictions:
107
+ contradiction_text = "\n\n".join(
108
+ [
109
+ (
110
+ f"### Possible contradiction {i}\n\n"
111
+ f"**A:** {x.get('claim_a', '')}\n\n"
112
+ f"Source A: {x.get('source_a', '')}\n\n"
113
+ f"**B:** {x.get('claim_b', '')}\n\n"
114
+ f"Source B: {x.get('source_b', '')}"
115
+ )
116
+ for i, x in enumerate(
117
+ contradictions,
118
+ 1,
119
+ )
120
+ ]
121
+ )
122
+ else:
123
+ contradiction_text = (
124
+ "No major automatic contradictions detected."
125
+ )
126
+
127
+ # ----------------------------------------------------
128
+ # Full JSON
129
+ # ----------------------------------------------------
130
+
131
+ json_text = json.dumps(
132
+ data,
133
+ ensure_ascii=False,
134
+ indent=2,
135
+ )
136
+
137
+ # ----------------------------------------------------
138
+ # Summary
139
+ # ----------------------------------------------------
140
+
141
+ summary = (
142
+ f"## X-RUDRA Research Complete\n\n"
143
+ f"**Question:** {question}\n\n"
144
+ f"**Rounds:** {data.get('rounds', 0)}\n\n"
145
+ f"**Sources:** {len(sources)}\n\n"
146
+ f"**Claims:** {len(claims)}\n\n"
147
+ f"**Stopping reason:** "
148
+ f"{data.get('stopping_reason', 'unknown')}\n\n"
149
+ f"**Models:** "
150
+ f"`Shrijanagain/M1` + `Shrijanagain/M2`\n\n"
151
+ f"**Web:** DuckDuckGo + Scrapling + Playwright"
152
+ )
153
+
154
+ return (
155
+ summary,
156
+ source_text or "No sources returned.",
157
+ claim_text or "No claims extracted.",
158
+ contradiction_text + "\n\n---\n\n```json\n"
159
+ + json_text
160
+ + "\n```",
161
+ )
162
+
163
+ except Exception as exc:
164
+ return (
165
+ f"## Search failed\n\n"
166
+ f"`{type(exc).__name__}: {exc}`",
167
+ "",
168
+ "",
169
+ "",
170
+ )
171
+
172
+
173
+ # ============================================================
174
+ # SYNC WRAPPER FOR GRADIO
175
+ # ============================================================
176
+
177
+ def search_wrapper(
178
+ question,
179
+ max_results,
180
+ max_rounds,
181
+ use_models,
182
+ freshness,
183
+ ):
184
+ return asyncio.run(
185
+ run_search(
186
+ question,
187
+ max_results,
188
+ max_rounds,
189
+ use_models,
190
+ freshness,
191
+ )
192
+ )
193
+
194
+
195
+ # ============================================================
196
+ # HEALTH
197
+ # ============================================================
198
+
199
+ def health():
200
+ return (
201
+ "## X-RUDRA\n\n"
202
+ f"Version: `{VERSION}`\n\n"
203
+ "M1: `Shrijanagain/M1`\n\n"
204
+ "M2: `Shrijanagain/M2`\n\n"
205
+ "Web Search: `DuckDuckGo`\n\n"
206
+ "Fetcher: `Scrapling`\n\n"
207
+ "Browser: `Playwright`\n\n"
208
+ "Evidence Engine: `Enabled`"
209
+ )
210
+
211
+
212
+ # ============================================================
213
+ # GRADIO UI
214
+ # ============================================================
215
+
216
+ CSS = """
217
+ #title {
218
+ text-align: center;
219
+ font-size: 42px;
220
+ font-weight: 800;
221
+ }
222
+
223
+ #subtitle {
224
+ text-align: center;
225
+ opacity: 0.75;
226
+ }
227
+
228
+ .search-btn {
229
+ min-height: 55px;
230
+ font-size: 18px;
231
+ font-weight: 700;
232
+ }
233
+ """
234
+
235
+
236
+ with gr.Blocks(
237
+ title="X-RUDRA",
238
+ css=CSS,
239
+ ) as demo:
240
+
241
+ gr.Markdown(
242
+ "# ⚡ X-RUDRA",
243
+ elem_id="title",
244
+ )
245
+
246
+ gr.Markdown(
247
+ "Dual-model AI research + live web intelligence",
248
+ elem_id="subtitle",
249
+ )
250
+
251
+ gr.Markdown(
252
+ """
253
+ **M1 + M2 → Query Planning → DuckDuckGo → Scrapling
254
+ → Playwright → Evidence → Cross-check**
255
+ """
256
+ )
257
+
258
+ with gr.Row():
259
+
260
+ with gr.Column(scale=4):
261
+
262
+ question = gr.Textbox(
263
+ label="Research Question",
264
+ placeholder=(
265
+ "Ask anything that requires current web research..."
266
+ ),
267
+ lines=5,
268
+ )
269
+
270
+ with gr.Row():
271
+
272
+ max_results = gr.Slider(
273
+ minimum=1,
274
+ maximum=30,
275
+ value=10,
276
+ step=1,
277
+ label="Max Sources",
278
+ )
279
+
280
+ max_rounds = gr.Slider(
281
+ minimum=1,
282
+ maximum=5,
283
+ value=3,
284
+ step=1,
285
+ label="Research Rounds",
286
+ )
287
+
288
+ with gr.Row():
289
+
290
+ use_models = gr.Checkbox(
291
+ value=True,
292
+ label="Use M1 + M2",
293
+ )
294
+
295
+ freshness = gr.Dropdown(
296
+ choices=[
297
+ "auto",
298
+ "latest",
299
+ "recent",
300
+ "current",
301
+ ],
302
+ value="auto",
303
+ label="Freshness",
304
+ )
305
+
306
+ search_button = gr.Button(
307
+ "SEARCH WITH X-RUDRA",
308
+ variant="primary",
309
+ elem_classes=["search-btn"],
310
+ )
311
+
312
+ health_button = gr.Button(
313
+ "Check Engine Health",
314
+ )
315
+
316
+ with gr.Column(scale=6):
317
+
318
+ summary = gr.Markdown(
319
+ label="Research Summary",
320
+ )
321
+
322
+ with gr.Tab("Sources"):
323
+
324
+ sources = gr.Markdown(
325
+ "Sources will appear here."
326
+ )
327
+
328
+ with gr.Tab(" Evidence / Claims"):
329
+
330
+ claims = gr.Markdown(
331
+ "Evidence will appear here."
332
+ )
333
+
334
+ with gr.Tab("⚠️ Contradictions + Raw JSON"):
335
+
336
+ contradictions = gr.Markdown(
337
+ "Contradictions and raw response will appear here."
338
+ )
339
+
340
+ health_output = gr.Markdown()
341
+
342
+ search_button.click(
343
+ fn=search_wrapper,
344
+ inputs=[
345
+ question,
346
+ max_results,
347
+ max_rounds,
348
+ use_models,
349
+ freshness,
350
+ ],
351
+ outputs=[
352
+ summary,
353
+ sources,
354
+ claims,
355
+ contradictions,
356
+ ],
357
+ )
358
+
359
+ question.submit(
360
+ fn=search_wrapper,
361
+ inputs=[
362
+ question,
363
+ max_results,
364
+ max_rounds,
365
+ use_models,
366
+ freshness,
367
+ ],
368
+ outputs=[
369
+ summary,
370
+ sources,
371
+ claims,
372
+ contradictions,
373
+ ],
374
+ )
375
+
376
+ health_button.click(
377
+ fn=health,
378
+ inputs=[],
379
+ outputs=[health_output],
380
+ )
381
+
382
+
383
+ # ============================================================
384
+ # LAUNCH
385
+ # ============================================================
386
+
387
+ if __name__ == "__main__":
388
+
389
+ port = int(
390
+ os.environ.get(
391
+ "PORT",
392
+ "7860",
393
+ )
394
+ )
395
+
396
+ demo.launch(
397
+ server_name="0.0.0.0",
398
+ server_port=port,
399
+ show_error=True,
400
+ )