bash98 commited on
Commit
48de39d
·
verified ·
1 Parent(s): d634f0d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +429 -79
app.py CHANGED
@@ -1,128 +1,478 @@
1
- """Manual RAG: separate CPU retrieval, CPU fallback, and ZeroGPU generation."""
2
 
3
- # MUST come before torch, Gradio, sentence-transformers, or transformers.
4
- import spaces
 
 
 
5
 
6
  import os
7
  import traceback
8
- import torch
 
 
 
 
9
  import gradio as gr
10
 
11
  from rag_core import (
12
- load_generator,
13
  load_retriever,
14
  answer_question,
15
  display_evidence,
16
  )
17
 
18
 
19
- def render_result(result):
20
- return result["answer"], display_evidence(result), result["status"]
 
21
 
 
 
 
 
 
 
22
 
23
- def respond_retrieval(question):
24
- """Retrieval-only processing on CPU; it never asks for a GPU."""
25
- try:
26
- return render_result(answer_question(question, retrieval_only=True))
27
- except Exception as error:
28
- traceback.print_exc()
29
- return f"Retrieval failed: {error}", "", "CPU retrieval error"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- def respond_cpu(question):
33
- """CPU answer-generation fallback when ZeroGPU is unavailable."""
34
  try:
35
- return render_result(answer_question(question, device="cpu"))
36
- except Exception as error:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  traceback.print_exc()
38
- return f"CPU generation failed: {error}", "", "CPU generation error"
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- @spaces.GPU(duration=60)
42
- def respond_gpu(question):
43
- """Answer with the model that ZeroGPU has packed for CUDA."""
44
  try:
45
- return render_result(answer_question(question, device="cuda"))
46
- except Exception as error:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  traceback.print_exc()
48
- return f"GPU generation failed: {error}", "", "GPU generation error"
49
 
 
 
 
 
 
 
50
 
51
- @spaces.GPU(duration=10)
52
- def check_zerogpu():
53
- """Test GPU allocation without loading or invoking any RAG model."""
54
- # If ZeroGPU worker_init fails, THIS LINE IS NEVER REACHED.
55
- value = torch.ones(1, device="cuda").sum().item()
56
- return f"ZeroGPU allocated successfully: {torch.cuda.get_device_name(0)}; test value = {value:.0f}"
57
 
 
 
 
58
 
59
  def make_demo():
60
- with gr.Blocks(title="Document Q&A with Manual RAG") as demo:
61
- gr.Markdown(
62
- "# Document Q&A with Manual RAG\n"
63
- "Ask questions about 30 SQuAD/Wikipedia passages. "
64
- "Evidence is retrieved on CPU; you may generate an answer on "
65
- "ZeroGPU or use the CPU fallback. Verify answers against sources."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  )
 
 
67
  question = gr.Textbox(
68
- label="Question",
69
  placeholder="Which company produces the iPod?",
70
  lines=2,
 
 
 
71
  )
72
- with gr.Row():
73
- retrieve_button = gr.Button("Retrieve evidence (CPU)")
74
- gpu_button = gr.Button("Answer with ZeroGPU", variant="primary")
75
- cpu_button = gr.Button("Answer with CPU fallback")
76
- answer = gr.Textbox(label="Answer - verify against evidence", lines=5)
77
- evidence = gr.Textbox(label="Source passages and similarity scores", lines=14)
78
- status = gr.Textbox(label="Run status")
79
- with gr.Accordion("ZeroGPU allocation diagnostic", open=False):
80
- gr.Markdown(
81
- "This runs one tiny CUDA operation. If it fails with "
82
- "`No CUDA GPUs are available` in `worker_init`, the error "
83
- "occurs before your RAG function runs."
 
 
84
  )
85
- test_button = gr.Button("Test ZeroGPU allocation")
86
- gpu_test_result = gr.Textbox(label="GPU test result")
87
-
88
- outputs = [answer, evidence, status]
89
- retrieve_button.click(fn=respond_retrieval, inputs=question, outputs=outputs)
90
- cpu_button.click(fn=respond_cpu, inputs=question, outputs=outputs)
91
- gpu_button.click(fn=respond_gpu, inputs=question, outputs=outputs)
92
- test_button.click(fn=check_zerogpu, inputs=[], outputs=gpu_test_result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  gr.Examples(
94
  examples=[
95
- ["Which company produces the iPod?"],
96
- ["At what age did Frédéric move to Paris?"],
97
- ["What is Beyoncé's private mobile phone number?"],
 
 
 
 
 
 
 
98
  ],
99
  inputs=[question],
 
 
100
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  return demo
102
 
103
 
104
- # Run initialization on the MAIN THREAD at module import; no model loads in
105
- # Gradio callbacks. In ZeroGPU's parent process, CUDA must remain uninitialized.
106
- print("Starting Manual RAG (ZeroGPU diagnostics)", flush=True)
107
- print(
108
- f"ZeroGPU env: {os.getenv('SPACES_ZERO_GPU')} | "
109
- f"torch={torch.__version__} | "
110
- f"parent CUDA initialized={torch.cuda.is_initialized()}",
111
- flush=True,
112
- )
113
- print("Preloading FLAN-T5 onto ZeroGPU-managed CUDA...", flush=True)
114
- print("Generation model:", load_generator(), flush=True)
115
- print("Loading CPU retriever:", load_retriever(), flush=True)
116
- print("Parent CUDA initialized after load:", torch.cuda.is_initialized(), flush=True)
117
 
118
- demo = make_demo()
119
- demo.queue(default_concurrency_limit=1, max_size=20)
120
 
121
- if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  demo.launch(
123
  server_name="0.0.0.0",
124
- server_port=int(os.getenv("PORT", "7860")),
125
- show_error=True,
 
126
  share=False,
 
127
  ssr_mode=False,
 
 
 
 
 
128
  )
 
 
 
 
 
 
1
 
2
+ """
3
+ Document Q&A with Manual RAG
4
+ CPU-Only Gradio Application
5
+ Hugging Face Spaces
6
+ """
7
 
8
  import os
9
  import traceback
10
+
11
+ # Force CPU execution
12
+ os.environ["CUDA_VISIBLE_DEVICES"] = ""
13
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
14
+
15
  import gradio as gr
16
 
17
  from rag_core import (
 
18
  load_retriever,
19
  answer_question,
20
  display_evidence,
21
  )
22
 
23
 
24
+ # ==========================================
25
+ # CUSTOM UI STYLING
26
+ # ==========================================
27
 
28
+ CUSTOM_CSS = """
29
+ .gradio-container {
30
+ max-width: 1080px !important;
31
+ margin: 0 auto !important;
32
+ padding: 22px 16px 38px !important;
33
+ }
34
 
35
+ #rag-hero {
36
+ padding: 26px 30px;
37
+ border: 1px solid var(--border-color-primary);
38
+ border-radius: 20px;
39
+ background: linear-gradient(
40
+ 125deg,
41
+ color-mix(
42
+ in srgb,
43
+ var(--primary-500) 13%,
44
+ var(--block-background-fill)
45
+ ) 0%,
46
+ var(--block-background-fill) 75%
47
+ );
48
+ margin-bottom: 18px;
49
+ }
50
+
51
+ #rag-hero .eyebrow {
52
+ font-size: 0.76rem;
53
+ text-transform: uppercase;
54
+ font-weight: 750;
55
+ letter-spacing: 0.11em;
56
+ color: var(--primary-500);
57
+ margin-bottom: 9px;
58
+ }
59
+
60
+ #rag-hero h1 {
61
+ font-size: clamp(1.65rem, 3vw, 2.35rem);
62
+ font-weight: 760;
63
+ line-height: 1.17;
64
+ letter-spacing: -0.035em;
65
+ margin: 0 0 10px;
66
+ }
67
+
68
+ #rag-hero p {
69
+ color: var(--body-text-color-subdued);
70
+ font-size: 0.99rem;
71
+ max-width: 750px;
72
+ line-height: 1.6;
73
+ margin: 0;
74
+ }
75
+
76
+ #rag-hero .meta {
77
+ font-size: 0.82rem;
78
+ color: var(--body-text-color-subdued);
79
+ margin-top: 15px;
80
+ }
81
+
82
+ #rag-input textarea {
83
+ font-size: 1.03rem !important;
84
+ }
85
+
86
+ #rag-actions button {
87
+ min-height: 48px !important;
88
+ font-weight: 650;
89
+ }
90
+
91
+ #rag-status {
92
+ margin: 4px 0 8px;
93
+ }
94
+
95
+ #rag-status p {
96
+ font-size: 0.9rem;
97
+ color: var(--body-text-color-subdued);
98
+ }
99
+
100
+ #rag-answer textarea {
101
+ font-size: 1.04rem !important;
102
+ line-height: 1.6 !important;
103
+ }
104
+
105
+ #rag-evidence textarea {
106
+ font-size: 0.94rem !important;
107
+ line-height: 1.55 !important;
108
+ }
109
+
110
+ #rag-footnote p {
111
+ font-size: 0.82rem;
112
+ color: var(--body-text-color-subdued);
113
+ }
114
 
115
+ @media (max-width: 680px) {
116
+ #rag-hero {
117
+ padding: 20px 18px;
118
+ border-radius: 16px;
119
+ }
120
+
121
+ .gradio-container {
122
+ padding: 12px 10px 22px !important;
123
+ }
124
+ }
125
+ """
126
+
127
+
128
+ # ==========================================
129
+ # FORMAT RAG RESULTS
130
+ # ==========================================
131
+
132
+ def format_result(result):
133
+ """Format answer, evidence, and run status."""
134
+
135
+ answer = result.get("answer", "")
136
+ evidence = display_evidence(result)
137
+
138
+ hits = result.get("retrieved") or []
139
+ count = len(hits)
140
+
141
+ top_score = (
142
+ hits[0]["score"]
143
+ if hits
144
+ else None
145
+ )
146
+
147
+ score_note = (
148
+ f" | Top cosine similarity: {top_score:.3f}"
149
+ if top_score is not None
150
+ else ""
151
+ )
152
+
153
+ status = result.get("status", "")
154
+
155
+ if status == "retrieval_only":
156
+
157
+ answer = (
158
+ "Evidence retrieved successfully. "
159
+ "Select Generate Answer to create a response."
160
+ )
161
+
162
+ message = (
163
+ f"**Evidence retrieved** | "
164
+ f"{count} source passages{score_note}"
165
+ )
166
+
167
+ elif status == "generated":
168
+
169
+ message = (
170
+ f"**Answer generated on CPU** | "
171
+ f"{count} source passages{score_note}"
172
+ )
173
+
174
+ elif status == "low_similarity_refusal":
175
+
176
+ message = (
177
+ "**No answer generated** | "
178
+ "The retrieved evidence is below "
179
+ "the similarity threshold."
180
+ f"{score_note}"
181
+ )
182
+
183
+ else:
184
+
185
+ message = (
186
+ f"**Completed on CPU** | "
187
+ f"{count} source passages{score_note}"
188
+ )
189
+
190
+ return answer, evidence, message
191
+
192
+
193
+ # ==========================================
194
+ # RETRIEVE EVIDENCE
195
+ # ==========================================
196
+
197
+ def retrieve_evidence(question):
198
+ """Retrieve relevant documents without generation."""
199
 
 
 
200
  try:
201
+
202
+ result = answer_question(
203
+ question,
204
+ retrieval_only=True
205
+ )
206
+
207
+ return format_result(result)
208
+
209
+ except ValueError as error:
210
+
211
+ return (
212
+ "Please check your question.",
213
+ "",
214
+ f"**Input error:** {str(error)}"
215
+ )
216
+
217
+ except Exception:
218
+
219
  traceback.print_exc()
 
220
 
221
+ return (
222
+ "Unable to retrieve evidence.",
223
+ "",
224
+ "**Retrieval error.** Check the application logs."
225
+ )
226
+
227
+
228
+ # ==========================================
229
+ # GENERATE ANSWER
230
+ # ==========================================
231
+
232
+ def generate_answer(question):
233
+ """Generate an answer using FLAN-T5 on CPU."""
234
 
 
 
 
235
  try:
236
+
237
+ result = answer_question(
238
+ question,
239
+ retrieval_only=False
240
+ )
241
+
242
+ return format_result(result)
243
+
244
+ except ValueError as error:
245
+
246
+ return (
247
+ "Please check your question.",
248
+ "",
249
+ f"**Input error:** {str(error)}"
250
+ )
251
+
252
+ except Exception:
253
+
254
  traceback.print_exc()
 
255
 
256
+ return (
257
+ "Unable to generate an answer. "
258
+ "You can still retrieve evidence.",
259
+ "",
260
+ "**Generation error.** Check the application logs."
261
+ )
262
 
 
 
 
 
 
 
263
 
264
+ # ==========================================
265
+ # BUILD GRADIO INTERFACE
266
+ # ==========================================
267
 
268
  def make_demo():
269
+
270
+ with gr.Blocks(
271
+ title="Document Q&A with Manual RAG"
272
+ ) as demo:
273
+
274
+ # Application header
275
+ gr.HTML(
276
+ """
277
+ <section id="rag-hero">
278
+
279
+ <div class="eyebrow">
280
+ Manual RAG | CPU Powered
281
+ </div>
282
+
283
+ <h1>
284
+ Document Q&amp;A with Manual RAG
285
+ </h1>
286
+
287
+ <p>
288
+ Ask a question, retrieve supporting
289
+ passages, and generate an answer
290
+ using only the available documents.
291
+ </p>
292
+
293
+ <div class="meta">
294
+ 30 SQuAD/Wikipedia passages
295
+ &nbsp; | &nbsp;
296
+ MiniLM Retrieval
297
+ &nbsp; | &nbsp;
298
+ FLAN-T5 Small Generation
299
+ </div>
300
+
301
+ </section>
302
+ """
303
  )
304
+
305
+ # Question input
306
  question = gr.Textbox(
307
+ label="Ask a Question",
308
  placeholder="Which company produces the iPod?",
309
  lines=2,
310
+ max_lines=3,
311
+ max_length=300,
312
+ elem_id="rag-input"
313
  )
314
+
315
+ # Action buttons
316
+ with gr.Row(elem_id="rag-actions"):
317
+
318
+ answer_button = gr.Button(
319
+ "Generate Answer",
320
+ variant="primary",
321
+ size="lg"
322
+ )
323
+
324
+ retrieve_button = gr.Button(
325
+ "Retrieve Evidence",
326
+ variant="secondary",
327
+ size="lg"
328
  )
329
+
330
+ # Application status
331
+ status = gr.Markdown(
332
+ "**Ready** | Running on CPU.",
333
+ elem_id="rag-status"
334
+ )
335
+
336
+ # Answer display
337
+ gr.Markdown("### Generated Answer")
338
+
339
+ answer = gr.Textbox(
340
+ label="Answer",
341
+ placeholder="Your answer will appear here.",
342
+ lines=4,
343
+ max_lines=8,
344
+ interactive=False,
345
+ buttons=["copy"],
346
+ elem_id="rag-answer"
347
+ )
348
+
349
+ # Retrieved documents
350
+ with gr.Accordion(
351
+ "Source Passages and Similarity Scores",
352
+ open=True
353
+ ):
354
+
355
+ evidence = gr.Textbox(
356
+ show_label=False,
357
+ placeholder=(
358
+ "Retrieved evidence will appear here."
359
+ ),
360
+ lines=11,
361
+ max_lines=14,
362
+ interactive=False,
363
+ buttons=["copy"],
364
+ elem_id="rag-evidence"
365
+ )
366
+
367
+ # Example questions
368
  gr.Examples(
369
  examples=[
370
+ [
371
+ "Which company produces the iPod?"
372
+ ],
373
+ [
374
+ "At what age did Frédéric move to Paris?"
375
+ ],
376
+ [
377
+ "What is the live price of Bitcoin "
378
+ "in Nigerian naira right now?"
379
+ ]
380
  ],
381
  inputs=[question],
382
+ label="Try an Example Question",
383
+ cache_examples=False
384
  )
385
+
386
+ # Footer
387
+ gr.Markdown(
388
+ """
389
+ **Important:** Answers are generated from
390
+ the 30 available SQuAD/Wikipedia passages,
391
+ not the live internet.
392
+
393
+ Similarity scores are not confidence scores.
394
+ Always verify answers against the retrieved
395
+ source passages.
396
+ """,
397
+ elem_id="rag-footnote"
398
+ )
399
+
400
+ # Connect buttons
401
+ outputs = [
402
+ answer,
403
+ evidence,
404
+ status
405
+ ]
406
+
407
+ answer_button.click(
408
+ fn=generate_answer,
409
+ inputs=question,
410
+ outputs=outputs
411
+ )
412
+
413
+ retrieve_button.click(
414
+ fn=retrieve_evidence,
415
+ inputs=question,
416
+ outputs=outputs
417
+ )
418
+
419
  return demo
420
 
421
 
422
+ # ==========================================
423
+ # START APPLICATION
424
+ # ==========================================
 
 
 
 
 
 
 
 
 
 
425
 
426
+ def main():
 
427
 
428
+ print("=" * 50)
429
+ print("Starting Document Q&A with Manual RAG")
430
+ print("Execution Mode: CPU Only")
431
+ print("=" * 50)
432
+
433
+ try:
434
+
435
+ print("Loading document retriever...")
436
+
437
+ info = load_retriever()
438
+
439
+ print(
440
+ f"Retriever initialized successfully: {info}"
441
+ )
442
+
443
+ except Exception:
444
+
445
+ print("Failed to initialize retriever.")
446
+ traceback.print_exc()
447
+ raise
448
+
449
+ # Build UI
450
+ demo = make_demo()
451
+
452
+ # Configure queue
453
+ demo.queue(
454
+ default_concurrency_limit=1,
455
+ max_size=20
456
+ )
457
+
458
+ print("Launching Gradio application...")
459
+
460
+ # Launch the app
461
  demo.launch(
462
  server_name="0.0.0.0",
463
+ server_port=int(
464
+ os.environ.get("PORT", "7860")
465
+ ),
466
  share=False,
467
+ show_error=True,
468
  ssr_mode=False,
469
+ theme=gr.themes.Soft(
470
+ primary_hue="indigo",
471
+ neutral_hue="slate"
472
+ ),
473
+ css=CUSTOM_CSS
474
  )
475
+
476
+
477
+ if __name__ == "__main__":
478
+ main()