vichudo commited on
Commit
5bc4687
·
1 Parent(s): d9feb53

chore: use imports to reduce app length

Browse files
Files changed (1) hide show
  1. app.py +29 -1186
app.py CHANGED
@@ -2,11 +2,6 @@ import os
2
  import sys
3
  import gradio as gr
4
  from dotenv import load_dotenv
5
- import subprocess
6
- import traceback
7
- import time
8
- import pickle
9
- import faiss
10
 
11
  # Add the current directory to the path so we can import from src
12
  sys.path.append(".")
@@ -22,1209 +17,57 @@ if not api_key:
22
  else:
23
  print("OpenAI API key loaded successfully.")
24
 
25
- # Global resource manager to persist data across requests
26
- class ResourceManager:
27
- _instance = None
28
-
29
- def __new__(cls):
30
- if cls._instance is None:
31
- cls._instance = super(ResourceManager, cls).__new__(cls)
32
- cls._instance.faiss_index = None
33
- cls._instance.doc_chunks = None
34
- cls._instance.embedding_vectors = None
35
- cls._instance.initialized = False
36
- return cls._instance
37
-
38
- def load_resources(self):
39
- """Load all required resources"""
40
- if self.initialized:
41
- print("Resources already loaded, skipping...")
42
- return True
43
-
44
- success = True
45
- # Load FAISS index
46
- try:
47
- print("Loading FAISS index...")
48
- self.faiss_index = faiss.read_index("embeddings/faiss_index.index")
49
- print("FAISS index loaded successfully")
50
- except Exception as e:
51
- print(f"Error loading FAISS index: {e}")
52
- success = False
53
-
54
- # Load document chunks
55
- try:
56
- print("Loading document chunks...")
57
- with open("data/doc_chunks.pkl", "rb") as f:
58
- self.doc_chunks = pickle.load(f)
59
- print(f"Loaded {len(self.doc_chunks)} document chunks")
60
- except Exception as e:
61
- print(f"Error loading document chunks: {e}")
62
- success = False
63
-
64
- # Load embeddings if available
65
- try:
66
- print("Loading embeddings...")
67
- with open("embeddings/embeddings.pkl", "rb") as f:
68
- self.embedding_vectors = pickle.load(f)
69
- print("Embeddings loaded successfully")
70
- except Exception as e:
71
- print(f"Error loading embeddings: {e}")
72
- # This is not critical, so don't set success to False
73
-
74
- if success:
75
- self.initialized = True
76
- print("All critical resources loaded successfully!")
77
-
78
- return success
79
-
80
- def get_faiss_index(self):
81
- """Get the FAISS index, loading if necessary"""
82
- if not self.initialized:
83
- self.load_resources()
84
- return self.faiss_index
85
-
86
- def get_doc_chunks(self):
87
- """Get the document chunks, loading if necessary"""
88
- if not self.initialized:
89
- self.load_resources()
90
- return self.doc_chunks
91
-
92
- def get_embedding_vectors(self):
93
- """Get the embedding vectors, loading if necessary"""
94
- if not self.initialized:
95
- self.load_resources()
96
- return self.embedding_vectors
97
 
98
  # Create the global resource manager
99
  resource_manager = ResourceManager()
100
 
101
- # Before initializing your model/agent, check and download data
102
- data_files = [
103
- "embeddings/faiss_index.index",
104
- "data/doc_chunks.pkl",
105
- "embeddings/embeddings.pkl"
106
- ]
107
-
108
- missing_files = [f for f in data_files if not os.path.exists(f)]
109
- data_ready = True
110
-
111
- if missing_files:
112
- print(f"Missing data files: {missing_files}")
113
- print("Downloading or creating required data files...")
114
- try:
115
- result = subprocess.run(
116
- [sys.executable, "download_from_hub.py"],
117
- check=False,
118
- capture_output=True,
119
- text=True
120
- )
121
- print(result.stdout)
122
- if result.returncode != 0:
123
- print(f"Warning: Data preparation finished with return code {result.returncode}")
124
- print(f"Error output: {result.stderr}")
125
- data_ready = False
126
- except Exception as e:
127
- print(f"Error preparing data: {e}")
128
- traceback.print_exc()
129
- data_ready = False
130
-
131
- # Verify files exist before importing modules that need them
132
- if not all(os.path.exists(f) for f in data_files):
133
- print("Warning: Some required data files are still missing.")
134
- print("The application may not function correctly.")
135
- data_ready = False
136
- else:
137
- # Load resources into the resource manager
138
- data_ready = resource_manager.load_resources()
139
-
140
- # Define a function to create fallback UI
141
- def create_fallback_ui():
142
- with gr.Blocks(
143
- theme=gr.themes.Base(),
144
- css=custom_css,
145
- title="Defensor Legal Assistant - Data Error"
146
- ) as demo:
147
- # Create a header with app-header class for better styling
148
- with gr.Column(elem_classes="app-header"):
149
- gr.HTML("""
150
- <div class="logo-container">
151
- <div class="logo">
152
- <span class="logo-icon">⚖️</span>
153
- <span class="logo-text">Defensor</span>
154
- </div>
155
- </div>
156
- """)
157
- gr.Markdown("# Agentic Defensor - Data Loading Error")
158
-
159
- with gr.Column(elem_classes="main-container"):
160
- gr.Markdown("""
161
- ### There was an error loading the required data files.
162
-
163
- This application requires specific dataset files to function properly.
164
- The system tried to download them from Hugging Face Hub but encountered errors.
165
-
166
- Please check the application logs for more details on what went wrong.
167
-
168
- Common issues:
169
- 1. Dataset repository might not be accessible or might not contain the expected files
170
- 2. Network connectivity issues when downloading datasets
171
- 3. Missing or invalid Hugging Face token for accessing private datasets
172
- """)
173
-
174
- with gr.Row():
175
- input_text = gr.Textbox(label="You can enter text here, but functionality is limited")
176
- output_text = gr.Textbox(label="System response")
177
-
178
- submit_btn = gr.Button("Submit (Limited functionality)")
179
-
180
- def dummy_response(text):
181
- return "The system is in fallback mode due to data loading errors. Please check application logs."
182
-
183
- submit_btn.click(dummy_response, inputs=[input_text], outputs=[output_text])
184
-
185
- return demo
186
-
187
  # Try importing modules
188
  director = None
189
  CHAT_MODEL = None
190
  try:
191
- # Use a workaround to avoid circular imports - don't import here
192
  # We'll import these in the specific functions where needed
193
  data_ready = data_ready and os.path.exists('src/agents/agent_director.py')
194
  except ImportError as e:
195
  print(f"Error importing modules: {e}")
196
  print("This may be caused by missing data files or module dependencies.")
197
  data_ready = False
198
- traceback.print_exc()
199
-
200
- # Define custom CSS for better UI
201
- custom_css = """
202
- body {
203
- background-color: #f8f9fa;
204
- color: #333;
205
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
206
- }
207
-
208
- /* App-wide typography improvements */
209
- h1, h2, h3, h4, h5, h6 {
210
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
211
- font-weight: 600;
212
- letter-spacing: -0.02em;
213
- }
214
-
215
- /* Dark mode theme - beautiful gradient background */
216
- .gradio-container {
217
- background: linear-gradient(135deg, #172a46 0%, #0c1220 100%);
218
- max-width: 1000px;
219
- margin: 0 auto;
220
- border-radius: 16px;
221
- overflow: hidden;
222
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
223
- border: 1px solid rgba(255, 255, 255, 0.05);
224
- }
225
-
226
- /* Logo styling */
227
- .logo-container {
228
- text-align: center;
229
- margin-bottom: 10px;
230
- }
231
-
232
- .logo {
233
- display: inline-flex;
234
- align-items: center;
235
- background: rgba(0, 0, 0, 0.2);
236
- padding: 10px 20px;
237
- border-radius: 50px;
238
- border: 1px solid rgba(255, 255, 255, 0.1);
239
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
240
- }
241
-
242
- .logo-icon {
243
- font-size: 2rem;
244
- margin-right: 10px;
245
- display: inline-block;
246
- }
247
-
248
- .logo-text {
249
- font-size: 1.6rem;
250
- font-weight: 700;
251
- color: white;
252
- letter-spacing: 0.5px;
253
- background: linear-gradient(90deg, #a5b4fc 0%, #818cf8 100%);
254
- -webkit-background-clip: text;
255
- -webkit-text-fill-color: transparent;
256
- text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
257
- }
258
-
259
- /* App header styling */
260
- .app-header {
261
- text-align: center;
262
- padding: 30px 20px 20px;
263
- background: rgba(0, 0, 0, 0.2);
264
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
265
- margin-bottom: 15px;
266
- }
267
-
268
- .app-header h1 {
269
- font-size: 2.4rem;
270
- color: #fff;
271
- margin-bottom: 10px;
272
- font-weight: 700;
273
- text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
274
- background: linear-gradient(90deg, #e0e7ff 0%, #c7d2fe 100%);
275
- -webkit-background-clip: text;
276
- -webkit-text-fill-color: transparent;
277
- }
278
-
279
- .app-header p {
280
- color: rgba(255, 255, 255, 0.8);
281
- font-size: 1.1rem;
282
- max-width: 700px;
283
- margin: 0 auto;
284
- line-height: 1.5;
285
- }
286
-
287
- /* Main container styling */
288
- .main-container {
289
- background-color: rgba(30, 41, 59, 0.7);
290
- backdrop-filter: blur(10px);
291
- border-radius: 12px;
292
- padding: 25px;
293
- margin: 15px;
294
- color: #fff;
295
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
296
- border: 1px solid rgba(255, 255, 255, 0.08);
297
- }
298
-
299
- /* Status container */
300
- .status-container {
301
- margin-bottom: 20px;
302
- text-align: center;
303
- width: 100%;
304
- }
305
-
306
- /* Response styling */
307
- .response-container, .response-wrapper {
308
- background: rgba(15, 23, 42, 0.6);
309
- backdrop-filter: blur(10px);
310
- border-radius: 12px;
311
- padding: 25px;
312
- margin-top: 20px;
313
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
314
- max-height: 700px;
315
- overflow-y: auto;
316
- width: 100%;
317
- border: 1px solid rgba(255, 255, 255, 0.05);
318
- transition: all 0.3s ease;
319
- }
320
-
321
- .answer-container {
322
- background: rgba(30, 41, 59, 0.4);
323
- border-radius: 10px;
324
- padding: 20px;
325
- margin-bottom: 15px;
326
- border: 1px solid rgba(255, 255, 255, 0.05);
327
- }
328
-
329
- .answer-container h2 {
330
- color: #a5b4fc;
331
- margin-top: 25px;
332
- margin-bottom: 15px;
333
- font-size: 1.5em;
334
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
335
- padding-bottom: 10px;
336
- }
337
-
338
- .answer-container h3 {
339
- color: #93c5fd;
340
- margin-top: 20px;
341
- margin-bottom: 12px;
342
- font-size: 1.3em;
343
- }
344
-
345
- .answer-container h4 {
346
- color: #bae6fd;
347
- margin-top: 18px;
348
- margin-bottom: 10px;
349
- font-size: 1.15em;
350
- }
351
-
352
- .answer-container p {
353
- margin-bottom: 15px;
354
- line-height: 1.6;
355
- color: rgba(255, 255, 255, 0.9);
356
- font-size: 1.05em;
357
- }
358
-
359
- /* Status indicators */
360
- .status-ready {
361
- background: linear-gradient(135deg, #134e5e 0%, #71b280 100%);
362
- color: white;
363
- padding: 12px 18px;
364
- border-radius: 30px;
365
- margin: 10px auto;
366
- font-weight: 600;
367
- box-shadow: 0 4px 12px rgba(19, 78, 94, 0.3);
368
- display: inline-block;
369
- min-width: 180px;
370
- }
371
-
372
- .status-processing {
373
- background: linear-gradient(135deg, #2980b9 0%, #2c3e50 100%);
374
- color: white;
375
- padding: 12px 18px;
376
- border-radius: 30px;
377
- margin: 10px auto;
378
- font-weight: 600;
379
- box-shadow: 0 4px 12px rgba(41, 128, 185, 0.3);
380
- animation: pulse 2s infinite;
381
- display: inline-block;
382
- min-width: 180px;
383
- }
384
-
385
- .status-error {
386
- background: linear-gradient(135deg, #cb2d3e 0%, #ef473a 100%);
387
- color: white;
388
- padding: 12px 18px;
389
- border-radius: 30px;
390
- margin: 15px auto;
391
- font-weight: 600;
392
- box-shadow: 0 4px 12px rgba(203, 45, 62, 0.3);
393
- display: inline-block;
394
- min-width: 180px;
395
- }
396
-
397
- .status-complete {
398
- background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
399
- color: white;
400
- padding: 12px 18px;
401
- border-radius: 30px;
402
- margin: 10px auto;
403
- font-weight: 600;
404
- box-shadow: 0 4px 12px rgba(17, 153, 142, 0.3);
405
- display: inline-block;
406
- min-width: 180px;
407
- }
408
-
409
- /* Input container */
410
- .input-container {
411
- background: rgba(30, 41, 59, 0.7);
412
- backdrop-filter: blur(10px);
413
- border-radius: 12px;
414
- padding: 25px;
415
- margin-bottom: 20px;
416
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
417
- border: 1px solid rgba(255, 255, 255, 0.08);
418
- transition: all 0.3s ease;
419
- }
420
-
421
- .input-container:focus-within {
422
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.25);
423
- transform: translateY(-2px);
424
- }
425
-
426
- .hint-text {
427
- color: rgba(255, 255, 255, 0.7);
428
- font-size: 0.95em;
429
- margin-top: 8px;
430
- font-style: italic;
431
- }
432
-
433
- /* Progress container */
434
- .progress-container {
435
- background: rgba(30, 41, 59, 0.7);
436
- backdrop-filter: blur(10px);
437
- border-radius: 12px;
438
- padding: 20px;
439
- margin: 20px 0;
440
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
441
- width: 100%;
442
- min-height: 90px;
443
- display: block;
444
- border: 1px solid rgba(255, 255, 255, 0.08);
445
- }
446
-
447
- /* Button container */
448
- .button-container {
449
- display: flex;
450
- gap: 15px;
451
- margin: 25px 0;
452
- width: 100%;
453
- justify-content: center;
454
- }
455
-
456
- .button-container button {
457
- min-height: 52px;
458
- font-size: 1.05em;
459
- font-weight: 600;
460
- padding: 0 35px;
461
- flex: 0 1 auto;
462
- min-width: 180px;
463
- max-width: 220px;
464
- letter-spacing: 0.3px;
465
- border-radius: 30px !important;
466
- }
467
-
468
- /* Progress label */
469
- .progress-label {
470
- margin-bottom: 12px;
471
- color: rgba(255, 255, 255, 0.85);
472
- font-weight: 600;
473
- font-size: 1.05em;
474
- text-align: center;
475
- }
476
-
477
- /* Progress stages styling */
478
- .progress-stages-container {
479
- display: flex;
480
- justify-content: space-between;
481
- flex-wrap: wrap;
482
- gap: 12px;
483
- margin-top: 20px;
484
- width: 100%;
485
- }
486
-
487
- .progress-stage {
488
- padding: 12px 15px;
489
- border-radius: 30px;
490
- background: rgba(15, 23, 42, 0.7);
491
- color: rgba(255, 255, 255, 0.6);
492
- font-size: 0.95em;
493
- transition: all 0.3s ease;
494
- flex: 1;
495
- text-align: center;
496
- min-width: 120px;
497
- max-width: 200px;
498
- display: flex;
499
- align-items: center;
500
- justify-content: center;
501
- border: 1px solid rgba(255, 255, 255, 0.05);
502
- }
503
-
504
- .progress-stage.active {
505
- background: linear-gradient(135deg, #2980b9 0%, #2c3e50 100%);
506
- color: white;
507
- font-weight: 600;
508
- animation: pulse 2s infinite;
509
- border: none;
510
- box-shadow: 0 4px 12px rgba(41, 128, 185, 0.25);
511
- }
512
-
513
- .progress-stage.complete {
514
- background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
515
- color: white;
516
- font-weight: 600;
517
- border: none;
518
- box-shadow: 0 4px 12px rgba(17, 153, 142, 0.25);
519
- }
520
-
521
- .progress-stage.error {
522
- background: linear-gradient(135deg, #cb2d3e 0%, #ef473a 100%);
523
- color: white;
524
- font-weight: 600;
525
- border: none;
526
- box-shadow: 0 4px 12px rgba(203, 45, 62, 0.25);
527
- }
528
-
529
- /* Reasoning steps styling */
530
- .reasoning-step {
531
- background: rgba(15, 23, 42, 0.5);
532
- border-radius: 10px;
533
- padding: 15px;
534
- margin-bottom: 15px;
535
- border-left: 3px solid rgba(255, 255, 255, 0.2);
536
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
537
- }
538
-
539
- .reasoning-stage {
540
- font-weight: 600;
541
- color: #93c5fd;
542
- margin-bottom: 10px;
543
- padding-bottom: 6px;
544
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
545
- }
546
-
547
- /* Sources section styling */
548
- .sources-section {
549
- background: rgba(15, 23, 42, 0.5);
550
- border-radius: 10px;
551
- padding: 15px;
552
- margin-top: 25px;
553
- border-left: 3px solid rgba(255, 255, 255, 0.2);
554
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
555
- }
556
 
557
- .sources-section h3 {
558
- color: #93c5fd;
559
- margin-top: 0;
560
- margin-bottom: 12px;
561
- }
562
 
563
- .source-item {
564
- margin-bottom: 8px;
565
- color: rgba(255, 255, 255, 0.85);
566
- }
567
-
568
- /* Enhanced visible styles for inputs */
569
- input, textarea {
570
- background: rgba(15, 23, 42, 0.6) !important;
571
- color: white !important;
572
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
573
- border-radius: 10px !important;
574
- padding: 15px !important;
575
- font-size: 1.05em !important;
576
- transition: all 0.3s ease !important;
577
- }
578
-
579
- input:focus, textarea:focus {
580
- border-color: rgba(147, 197, 253, 0.5) !important;
581
- outline: none !important;
582
- box-shadow: 0 0 0 3px rgba(147, 197, 253, 0.25) !important;
583
- background: rgba(15, 23, 42, 0.8) !important;
584
- }
585
-
586
- /* Make buttons more beautiful with gradients */
587
- button {
588
- border-radius: 30px !important;
589
- transition: all 0.3s ease !important;
590
- outline: none !important;
591
- font-weight: 600 !important;
592
- letter-spacing: 0.3px !important;
593
- }
594
-
595
- button:hover {
596
- transform: translateY(-3px) !important;
597
- box-shadow: 0 6px 15px rgba(0, 0, 0, 0.25) !important;
598
- }
599
-
600
- button:active {
601
- transform: translateY(-1px) !important;
602
- }
603
-
604
- /* Primary button styling */
605
- button[variant="primary"],
606
- .gr-button-primary {
607
- background: linear-gradient(135deg, #4f46e5 0%, #3b82f6 100%) !important;
608
- color: white !important;
609
- border: none !important;
610
- }
611
-
612
- button[variant="primary"]:hover,
613
- .gr-button-primary:hover {
614
- background: linear-gradient(135deg, #4338ca 0%, #2563eb 100%) !important;
615
- }
616
-
617
- /* Secondary button styling */
618
- button[variant="secondary"],
619
- .gr-button-secondary {
620
- background: rgba(30, 41, 59, 0.8) !important;
621
- color: white !important;
622
- border: 1px solid rgba(255, 255, 255, 0.15) !important;
623
- }
624
-
625
- button[variant="secondary"]:hover,
626
- .gr-button-secondary:hover {
627
- background: rgba(30, 41, 59, 0.95) !important;
628
- }
629
-
630
- /* Animation for processing effects */
631
- @keyframes pulse {
632
- 0% {
633
- opacity: 1;
634
- box-shadow: 0 4px 12px rgba(41, 128, 185, 0.3);
635
- }
636
- 50% {
637
- opacity: 0.85;
638
- box-shadow: 0 4px 20px rgba(41, 128, 185, 0.5);
639
- }
640
- 100% {
641
- opacity: 1;
642
- box-shadow: 0 4px 12px rgba(41, 128, 185, 0.3);
643
- }
644
- }
645
-
646
- /* Responsive improvements */
647
- @media (min-width: 768px) {
648
- .gradio-container {
649
- min-width: 750px !important;
650
- max-width: 1200px !important;
651
- }
652
-
653
- .input-container, .output-container {
654
- padding: 30px !important;
655
- }
656
-
657
- .button-container {
658
- justify-content: center !important;
659
- gap: 25px !important;
660
- }
661
- }
662
-
663
- @media (max-width: 767px) {
664
- .progress-stage {
665
- min-width: 90px;
666
- padding: 10px;
667
- font-size: 0.9em;
668
- }
669
-
670
- .app-header h1 {
671
- font-size: 1.8rem;
672
- }
673
-
674
- .app-header p {
675
- font-size: 1rem;
676
- }
677
-
678
- .logo-icon {
679
- font-size: 1.5rem;
680
- }
681
-
682
- .logo-text {
683
- font-size: 1.2rem;
684
- }
685
- }
686
-
687
- /* Override any built-in Gradio container colors */
688
- .gradio-container {
689
- color: white !important;
690
- }
691
-
692
- .gr-box, .gr-form, .gr-panel {
693
- background: rgba(30, 41, 59, 0.7) !important;
694
- backdrop-filter: blur(10px) !important;
695
- color: white !important;
696
- border-radius: 12px !important;
697
- border: 1px solid rgba(255, 255, 255, 0.08) !important;
698
- }
699
-
700
- .gr-input, .gr-textarea, .gr-checkbox, .gr-radio, .gr-slider, .gr-dropdown {
701
- background: rgba(15, 23, 42, 0.6) !important;
702
- color: white !important;
703
- border-color: rgba(255, 255, 255, 0.1) !important;
704
- }
705
-
706
- /* Override labels and markdown content */
707
- .gr-label, .gr-markdown {
708
- color: rgba(255, 255, 255, 0.9) !important;
709
- }
710
-
711
- /* Make sure accordions are styled properly */
712
- .gr-accordion {
713
- background: rgba(30, 41, 59, 0.7) !important;
714
- backdrop-filter: blur(10px) !important;
715
- color: white !important;
716
- border: 1px solid rgba(255, 255, 255, 0.08) !important;
717
- border-radius: 12px !important;
718
- margin: 15px 0 !important;
719
- }
720
-
721
- .gr-accordion-header {
722
- background: rgba(15, 23, 42, 0.5) !important;
723
- color: white !important;
724
- font-weight: 600 !important;
725
- padding: 15px !important;
726
- border-radius: 12px 12px 0 0 !important;
727
- }
728
-
729
- .gr-accordion-content {
730
- background: transparent !important;
731
- color: white !important;
732
- padding: 20px !important;
733
- }
734
-
735
- /* Ensure response text is visible */
736
- .response-wrapper,
737
- .response-wrapper *,
738
- .answer-container,
739
- .answer-container * {
740
- color: rgba(255, 255, 255, 0.9) !important;
741
- background-color: transparent !important;
742
- }
743
-
744
- .response-wrapper {
745
- background: rgba(15, 23, 42, 0.6) !important;
746
- backdrop-filter: blur(10px) !important;
747
- border-radius: 12px !important;
748
- padding: 25px !important;
749
- margin-top: 20px !important;
750
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15) !important;
751
- border: 1px solid rgba(255, 255, 255, 0.05) !important;
752
- }
753
-
754
- .answer-container {
755
- background: rgba(30, 41, 59, 0.4) !important;
756
- border-radius: 10px !important;
757
- padding: 20px !important;
758
- margin-bottom: 15px !important;
759
- border: 1px solid rgba(255, 255, 255, 0.05) !important;
760
- }
761
-
762
- /* Apply styles to specific elements within the answer container */
763
- .answer-container h2 {
764
- color: #a5b4fc !important;
765
- margin-top: 25px !important;
766
- margin-bottom: 15px !important;
767
- font-size: 1.5em !important;
768
- border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
769
- padding-bottom: 10px !important;
770
- }
771
-
772
- .answer-container h3 {
773
- color: #93c5fd !important;
774
- margin-top: 20px !important;
775
- margin-bottom: 12px !important;
776
- font-size: 1.3em !important;
777
- }
778
-
779
- .answer-container h4 {
780
- color: #bae6fd !important;
781
- margin-top: 18px !important;
782
- margin-bottom: 10px !important;
783
- font-size: 1.15em !important;
784
- }
785
-
786
- .answer-container p {
787
- margin-bottom: 15px !important;
788
- line-height: 1.6 !important;
789
- color: rgba(255, 255, 255, 0.9) !important;
790
- font-size: 1.05em !important;
791
- }
792
-
793
- .answer-container ul,
794
- .answer-container ol {
795
- margin-left: 20px !important;
796
- margin-bottom: 15px !important;
797
- color: rgba(255, 255, 255, 0.9) !important;
798
- }
799
-
800
- .answer-container li {
801
- margin-bottom: 8px !important;
802
- color: rgba(255, 255, 255, 0.9) !important;
803
- line-height: 1.5 !important;
804
- }
805
-
806
- /* Fix raw HTML rendering */
807
- .prose {
808
- color: rgba(255, 255, 255, 0.9) !important;
809
- background-color: transparent !important;
810
- }
811
-
812
- .prose * {
813
- color: inherit !important;
814
- }
815
-
816
- /* Slider styling */
817
- .gr-slider {
818
- background: rgba(15, 23, 42, 0.6) !important;
819
- }
820
-
821
- .gr-slider .thumb {
822
- background: #4f46e5 !important;
823
- border: 2px solid white !important;
824
- }
825
-
826
- .gr-slider .track {
827
- background: rgba(255, 255, 255, 0.2) !important;
828
- }
829
-
830
- .gr-slider .track-fill {
831
- background: linear-gradient(90deg, #4f46e5 0%, #3b82f6 100%) !important;
832
- }
833
-
834
- /* Checkbox styling */
835
- .gr-checkbox {
836
- border: 2px solid rgba(255, 255, 255, 0.3) !important;
837
- background: rgba(15, 23, 42, 0.6) !important;
838
- }
839
-
840
- .gr-checkbox:checked {
841
- background: #4f46e5 !important;
842
- border-color: white !important;
843
- }
844
-
845
- /* Better scrollbars for the response area */
846
- .response-wrapper::-webkit-scrollbar,
847
- .answer-container::-webkit-scrollbar {
848
- width: 8px;
849
- height: 8px;
850
- }
851
-
852
- .response-wrapper::-webkit-scrollbar-track,
853
- .answer-container::-webkit-scrollbar-track {
854
- background: rgba(255, 255, 255, 0.05);
855
- border-radius: 4px;
856
- }
857
-
858
- .response-wrapper::-webkit-scrollbar-thumb,
859
- .answer-container::-webkit-scrollbar-thumb {
860
- background: rgba(255, 255, 255, 0.2);
861
- border-radius: 4px;
862
- }
863
-
864
- .response-wrapper::-webkit-scrollbar-thumb:hover,
865
- .answer-container::-webkit-scrollbar-thumb:hover {
866
- background: rgba(255, 255, 255, 0.3);
867
- }
868
- """
869
-
870
- # Create the Gradio interface with full functionality
871
- def create_interface():
872
- """Create the Gradio interface."""
873
-
874
- with gr.Blocks(
875
- title="Defensor Legal Assistant",
876
- theme=gr.themes.Base(),
877
- css=custom_css
878
- ) as demo:
879
- # Create a header with app-header class for better styling
880
- with gr.Column(elem_classes="app-header"):
881
- gr.HTML("""
882
- <div class="logo-container">
883
- <div class="logo">
884
- <span class="logo-icon">⚖️</span>
885
- <span class="logo-text">Defensor</span>
886
- </div>
887
- </div>
888
- """)
889
- gr.Markdown("# Defensor Legal Assistant")
890
- gr.Markdown("Ask questions about legal defense strategies and get comprehensive answers based on legitimate sources.")
891
-
892
- # Main application container
893
- with gr.Column(elem_classes="main-container"):
894
- # Add status indicator with styles - in its own container
895
- with gr.Row(elem_classes="status-container"):
896
- status_indicator = gr.HTML(
897
- value="<div class='status-ready'>Ready to assist</div>",
898
- label=None
899
- )
900
-
901
- # Enhanced container for the input area
902
- with gr.Column(elem_classes="input-container"):
903
- query_input = gr.Textbox(
904
- label="Your Legal Question",
905
- placeholder="Enter your legal question here (e.g., 'What are the key elements of self-defense?')",
906
- lines=3
907
- )
908
- gr.Markdown("<div class='hint-text'>Ask detailed questions for better results. Be specific about the legal concepts you're interested in.</div>")
909
-
910
- # Create a dedicated container for the progress area
911
- with gr.Column(elem_classes="progress-container"):
912
- # Progress indicator with animation and better styling
913
- progress = gr.Progress()
914
-
915
- # Add a visual progress label
916
- progress_label = gr.HTML(
917
- "<div class='progress-label'>Waiting for your question...</div>",
918
- visible=True
919
- )
920
-
921
- # Better button container with centered buttons
922
- with gr.Row(elem_classes="button-container"):
923
- submit_btn = gr.Button(
924
- "Submit Question",
925
- variant="primary",
926
- interactive=True
927
- )
928
- clear_btn = gr.Button(
929
- "Clear",
930
- variant="secondary"
931
- )
932
-
933
- # Settings in collapsible section
934
- with gr.Accordion("Settings & Options", open=False):
935
- with gr.Row():
936
- top_k = gr.Slider(
937
- minimum=5,
938
- maximum=50,
939
- value=20,
940
- step=5,
941
- label="Number of Documents to Consider"
942
- )
943
- debug = gr.Checkbox(label="Show Agent Reasoning Process", value=True)
944
-
945
- # Display area for the response with enhanced formatting
946
- with gr.Column(elem_classes="output-container"):
947
- output = gr.HTML(label="Response", value="")
948
-
949
- # About section at the bottom
950
- with gr.Accordion("About Defensor Legal Assistant", open=False):
951
- gr.Markdown("""
952
- ## About Defensor Legal Assistant
953
-
954
- Defensor is a specialized legal assistant powered by advanced AI technology and a comprehensive legal knowledge base. It provides insights and information about legal defense strategies based on authoritative sources.
955
-
956
- ### How It Works
957
-
958
- 1. **Query Analysis**: The system analyzes your legal question to understand its context and intent
959
- 2. **Knowledge Retrieval**: It searches through a database of legal documents to find relevant information
960
- 3. **Context Organization**: The system organizes the retrieved information into a coherent context
961
- 4. **Response Generation**: A comprehensive answer is generated based on the collected information
962
-
963
- ### Tips for Best Results
964
-
965
- - Be specific in your questions
966
- - Include relevant legal terms or concepts
967
- - For complex topics, consider breaking down into multiple targeted questions
968
- - Use the "Show Agent Reasoning" option to see how the system arrived at its answer
969
-
970
- ### Limitations
971
-
972
- This assistant provides legal information, not legal advice. Always consult with a qualified attorney for advice on specific legal matters.
973
- """)
974
-
975
- def process_query_with_status(query, top_k=50, debug=False, progress=gr.Progress()):
976
- """Process a query using the agent director with status updates."""
977
- global director
978
-
979
- if not query.strip():
980
- return "<div class='status-error'>Please enter a question to proceed</div>", ""
981
-
982
- # Show progress - with fixed percentage formatting
983
- progress(0, desc="Initializing...")
984
-
985
- # Import here to avoid circular references
986
- try:
987
- from src.agents.agent_director import AgentDirector
988
- from src.utils.config import CHAT_MODEL
989
- except ImportError as e:
990
- return f"<div class='status-error'>ERROR: Failed to import required modules: {str(e)}</div>", ""
991
-
992
- # Initialize the agent if not already done
993
- if director is None:
994
- try:
995
- progress(0.1, desc="Setting up the agent...") # Use decimal for percentage
996
- # Initialize the agent director
997
- director = AgentDirector(model=CHAT_MODEL, top_k=top_k, debug=debug)
998
- progress(0.2, desc="Agent ready") # Use decimal for percentage
999
- except Exception as e:
1000
- error_details = traceback.format_exc()
1001
- print(f"Agent initialization error: {e}")
1002
- print(error_details)
1003
- progress(1.0, desc="Error") # Use decimal for percentage
1004
- return f"<div class='status-error'>ERROR: Failed to initialize agent: {str(e)}</div>", ""
1005
-
1006
- # Process the query
1007
- try:
1008
- # Update progress with meaningful steps and show visual stages
1009
- progress_stages = """<div class='progress-stages-container'>
1010
- <div class='progress-stage active'>► Analyzing query</div>
1011
- <div class='progress-stage'>Searching documents</div>
1012
- <div class='progress-stage'>Organizing information</div>
1013
- <div class='progress-stage'>Formulating response</div>
1014
- </div>"""
1015
-
1016
- # Update progress with meaningful steps
1017
- progress(0.25, desc="Analyzing your question...") # Use decimal for percentage
1018
- time.sleep(0.5) # Small delay for visual feedback
1019
-
1020
- progress(0.4, desc="Searching knowledge base...") # Use decimal for percentage
1021
- progress_stages = """<div class='progress-stages-container'>
1022
- <div class='progress-stage complete'>✓ Analyzing query</div>
1023
- <div class='progress-stage active'>► Searching documents</div>
1024
- <div class='progress-stage'>Organizing information</div>
1025
- <div class='progress-stage'>Formulating response</div>
1026
- </div>"""
1027
- time.sleep(0.5) # Small delay for visual feedback
1028
-
1029
- progress(0.6, desc="Organizing information...") # Use decimal for percentage
1030
- progress_stages = """<div class='progress-stages-container'>
1031
- <div class='progress-stage complete'>✓ Analyzing query</div>
1032
- <div class='progress-stage complete'>✓ Searching documents</div>
1033
- <div class='progress-stage active'>► Organizing information</div>
1034
- <div class='progress-stage'>Formulating response</div>
1035
- </div>"""
1036
- time.sleep(0.5) # Small delay for visual feedback
1037
-
1038
- progress(0.8, desc="Formulating legal response...") # Use decimal for percentage
1039
- progress_stages = """<div class='progress-stages-container'>
1040
- <div class='progress-stage complete'>✓ Analyzing query</div>
1041
- <div class='progress-stage complete'>✓ Searching documents</div>
1042
- <div class='progress-stage complete'>✓ Organizing information</div>
1043
- <div class='progress-stage active'>► Formulating response</div>
1044
- </div>"""
1045
-
1046
- # Process the query with timeout handling
1047
- try:
1048
- # Set a timeout for processing
1049
- import threading
1050
- import queue
1051
-
1052
- def process_with_timeout():
1053
- try:
1054
- result = director.process_query(query)
1055
- result_queue.put(result)
1056
- except Exception as e:
1057
- result_queue.put({"error": str(e)})
1058
-
1059
- result_queue = queue.Queue()
1060
- processing_thread = threading.Thread(target=process_with_timeout)
1061
- processing_thread.daemon = True
1062
- processing_thread.start()
1063
-
1064
- # Wait for the result with a timeout
1065
- timeout = 60 # 60 seconds timeout
1066
- processing_thread.join(timeout)
1067
-
1068
- if not result_queue.empty():
1069
- result = result_queue.get()
1070
- if "error" in result:
1071
- raise Exception(result["error"])
1072
- else:
1073
- # If the queue is empty after timeout, provide a fallback answer
1074
- print("Query processing timed out, providing fallback response")
1075
-
1076
- # Try to use the legal agent as fallback
1077
- try:
1078
- from src.agents.legal_agent import LegalAgent
1079
- fallback_agent = LegalAgent(model=CHAT_MODEL)
1080
- # Let the legal agent handle retrieval internally
1081
- result = fallback_agent.answer_query(query, top_k=5)
1082
- except Exception as fallback_error:
1083
- print(f"Fallback error: {fallback_error}")
1084
- # If fallback fails, use a generic response
1085
- result = {
1086
- "answer": "I apologize, but I was unable to generate a complete response. Please try rephrasing your question or asking something else.",
1087
- "sources": []
1088
- }
1089
- except Exception as process_error:
1090
- print(f"Error during query processing: {process_error}")
1091
- traceback.print_exc()
1092
-
1093
- # Provide a simplified fallback
1094
- result = {
1095
- "answer": f"I encountered an error while processing your query: {str(process_error)}. Please try again or rephrase your question.",
1096
- "sources": []
1097
- }
1098
-
1099
- # Format the response with enhanced HTML
1100
- answer = result.get("answer", "No answer available.")
1101
-
1102
- # Create a well-formatted, dark-theme friendly output
1103
- answer_html = ""
1104
-
1105
- # Convert answer text to HTML paragraphs
1106
- for paragraph in answer.split("\n\n"):
1107
- if paragraph.strip():
1108
- # Check if it's a heading
1109
- if paragraph.startswith("# "):
1110
- heading = paragraph[2:].strip()
1111
- answer_html += f"<h2>{heading}</h2>"
1112
- elif paragraph.startswith("## "):
1113
- heading = paragraph[3:].strip()
1114
- answer_html += f"<h3>{heading}</h3>"
1115
- elif paragraph.startswith("### "):
1116
- heading = paragraph[4:].strip()
1117
- answer_html += f"<h4>{heading}</h4>"
1118
- else:
1119
- answer_html += f"<p>{paragraph}</p>"
1120
-
1121
- # Add sources if available
1122
- if "sources" in result and result["sources"]:
1123
- sources_html = "<div class='sources-section'>"
1124
- sources_html += "<h3>Sources</h3><ul>"
1125
- for source in result["sources"]:
1126
- if source and source != "Source information unavailable":
1127
- sources_html += f"<li class='source-item'>{source}</li>"
1128
- sources_html += "</ul></div>"
1129
- answer_html += sources_html
1130
-
1131
- # Add debugging information if available with nice formatting
1132
- if debug and result.get("reasoning_steps") is not None:
1133
- reasoning_html = "<h3>Agent Reasoning Process</h3>"
1134
- for step in result["reasoning_steps"]:
1135
- step_stage = step['stage']
1136
- step_reasoning = step['reasoning'].replace('\n', '<br>')
1137
- reasoning_html += f"""
1138
- <div class='reasoning-step'>
1139
- <div class='reasoning-stage'>{step_stage}</div>
1140
- <div>{step_reasoning}</div>
1141
- </div>
1142
- """
1143
- answer_html += reasoning_html
1144
-
1145
- progress(1.0, desc="Completed!")
1146
-
1147
- # Update the progress stages to show completion
1148
- progress_stages = """<div class='progress-stages-container'>
1149
- <div class='progress-stage complete'>✓ Analyzing query</div>
1150
- <div class='progress-stage complete'>✓ Searching documents</div>
1151
- <div class='progress-stage complete'>✓ Organizing information</div>
1152
- <div class='progress-stage complete'>✓ Response ready</div>
1153
- </div>"""
1154
-
1155
- # Final formatted output with all styling embedded
1156
- return f"""
1157
- <div class="response-wrapper">
1158
- <div class="answer-container">
1159
- {answer_html}
1160
- </div>
1161
- </div>
1162
- """, progress_stages
1163
-
1164
- except Exception as e:
1165
- error_details = traceback.format_exc()
1166
- print(f"Query processing error: {e}")
1167
- print(error_details)
1168
- progress(1.0, desc="Error")
1169
-
1170
- # Create a complete progress stages display with error indicator
1171
- error_progress_stages = """<div class='progress-stages-container'>
1172
- <div class='progress-stage complete'>✓ Analyzing query</div>
1173
- <div class='progress-stage complete'>✓ Searching documents</div>
1174
- <div class='progress-stage error'>✗ Error</div>
1175
- <div class='progress-stage'>Formulating response</div>
1176
- </div>"""
1177
-
1178
- return f"""
1179
- <div class='status-error'>
1180
- <strong>ERROR:</strong> {str(e)}
1181
- <p>Please try again or rephrase your question.</p>
1182
- </div>
1183
- """, error_progress_stages
1184
-
1185
- def set_ready_status():
1186
- return "<div class='status-ready'>Ready to assist</div>", "<div class='progress-label'>Waiting for your question...</div>"
1187
-
1188
- def set_processing_status():
1189
- return "<div class='status-processing'>Processing your question...</div>", gr.update(interactive=False), "<div class='progress-label'>Processing legal query...</div>"
1190
-
1191
- def set_completed_status():
1192
- return "<div class='status-complete'>Response complete</div>", gr.update(interactive=True), "<div class='progress-label'>Response complete</div>"
1193
-
1194
- def clear_inputs():
1195
- return "", "<div class='status-ready'>Ready to assist</div>", gr.update(interactive=True), "", "<div class='progress-label'>Waiting for your question...</div>"
1196
-
1197
- # Set up event handlers with improved button state management
1198
- submit_btn.click(
1199
- fn=set_processing_status,
1200
- outputs=[status_indicator, submit_btn, progress_label],
1201
- queue=False
1202
- ).then(
1203
- fn=process_query_with_status,
1204
- inputs=[query_input, top_k, debug],
1205
- outputs=[output, progress_label],
1206
- api_name="query"
1207
- ).then(
1208
- fn=set_completed_status,
1209
- outputs=[status_indicator, submit_btn, progress_label],
1210
- queue=False
1211
- )
1212
-
1213
- clear_btn.click(
1214
- fn=clear_inputs,
1215
- outputs=[query_input, status_indicator, submit_btn, output, progress_label],
1216
- queue=False
1217
- )
1218
-
1219
- return demo
1220
 
1221
  # Launch the app
1222
  if __name__ == "__main__":
1223
  # Create and launch the appropriate interface based on data readiness
1224
  if data_ready:
1225
- demo = create_interface()
1226
  else:
1227
- demo = create_fallback_ui()
1228
 
1229
  demo.launch(
1230
  server_name="0.0.0.0",
 
2
  import sys
3
  import gradio as gr
4
  from dotenv import load_dotenv
 
 
 
 
 
5
 
6
  # Add the current directory to the path so we can import from src
7
  sys.path.append(".")
 
17
  else:
18
  print("OpenAI API key loaded successfully.")
19
 
20
+ # Import our custom modules
21
+ from src.utils.styles import get_custom_css
22
+ from src.utils.ui import create_interface, create_fallback_ui
23
+ from src.utils.resource_manager import ResourceManager, check_data_files
24
+ from src.utils.query_handler import process_query_with_status
25
+
26
+ # Check if data files exist and load them
27
+ data_ready = check_data_files()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  # Create the global resource manager
30
  resource_manager = ResourceManager()
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # Try importing modules
33
  director = None
34
  CHAT_MODEL = None
35
  try:
 
36
  # We'll import these in the specific functions where needed
37
  data_ready = data_ready and os.path.exists('src/agents/agent_director.py')
38
  except ImportError as e:
39
  print(f"Error importing modules: {e}")
40
  print("This may be caused by missing data files or module dependencies.")
41
  data_ready = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ # Get the custom CSS
44
+ custom_css = get_custom_css()
 
 
 
45
 
46
+ # Define a wrapper function for processing queries
47
+ def process_query_wrapper(query, top_k=50, debug=False, progress=None):
48
+ """Wrapper function to process queries with the resource manager."""
49
+ try:
50
+ # Get the director from the resource manager
51
+ director = resource_manager.get_director()
52
+ return process_query_with_status(query, top_k, debug, progress, director)
53
+ except Exception as e:
54
+ import traceback
55
+ print(f"Error in process_query_wrapper: {e}")
56
+ traceback.print_exc()
57
+ return (f"""
58
+ <div class='status-error'>
59
+ <strong>ERROR:</strong> An unexpected error occurred: {str(e)}
60
+ <p>Please try again or contact support if the problem persists.</p>
61
+ </div>
62
+ """, "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  # Launch the app
65
  if __name__ == "__main__":
66
  # Create and launch the appropriate interface based on data readiness
67
  if data_ready:
68
+ demo = create_interface(custom_css, process_query_wrapper)
69
  else:
70
+ demo = create_fallback_ui(custom_css)
71
 
72
  demo.launch(
73
  server_name="0.0.0.0",