Spaces:
Sleeping
Sleeping
Commit ·
d4f3644
1
Parent(s): a81f881
✨ Add security badge and live processing indicators
Browse files- Add AES-256 encryption badge below file upload
- Implement real-time progress indicators for document processing
- Show step-by-step feedback: loading → chunking → indexing → complete
- app/main.py +82 -12
app/main.py
CHANGED
|
@@ -14,6 +14,7 @@ class DocumentRagApp:
|
|
| 14 |
self.loaded_documents = []
|
| 15 |
|
| 16 |
def load_samples(self, vertical):
|
|
|
|
| 17 |
samples = {
|
| 18 |
"Legal": [
|
| 19 |
"data/samples/legal/service_agreement.txt",
|
|
@@ -33,19 +34,33 @@ class DocumentRagApp:
|
|
| 33 |
}
|
| 34 |
|
| 35 |
try:
|
| 36 |
-
|
|
|
|
| 37 |
if os.path.exists(path):
|
|
|
|
| 38 |
chunks = self.processor.process_txt(path)
|
|
|
|
|
|
|
| 39 |
self.rag_pipeline.add_documents(chunks, is_sample=True)
|
|
|
|
|
|
|
| 40 |
self.loaded_documents.append(os.path.basename(path))
|
| 41 |
-
|
|
|
|
|
|
|
| 42 |
except Exception as e:
|
| 43 |
-
|
| 44 |
|
| 45 |
def process_file(self, file):
|
|
|
|
| 46 |
if not file:
|
| 47 |
-
|
|
|
|
|
|
|
| 48 |
try:
|
|
|
|
|
|
|
|
|
|
| 49 |
ext = os.path.splitext(file.name)[1].lower()
|
| 50 |
if ext == ".pdf":
|
| 51 |
chunks = self.processor.process_pdf(file.name)
|
|
@@ -54,15 +69,18 @@ class DocumentRagApp:
|
|
| 54 |
elif ext == ".docx":
|
| 55 |
chunks = self.processor.process_docx(file.name)
|
| 56 |
else:
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
| 58 |
|
|
|
|
| 59 |
self.rag_pipeline.add_documents(chunks, is_sample=False)
|
| 60 |
-
self.loaded_documents.append(
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
)
|
| 64 |
except Exception as e:
|
| 65 |
-
|
| 66 |
|
| 67 |
def switch_model(self, model_choice):
|
| 68 |
"""Handle model switching from UI radio button"""
|
|
@@ -384,6 +402,47 @@ span, p, div { font-family: var(--font-body); }
|
|
| 384 |
padding: 0.25rem 0.5rem;
|
| 385 |
margin-top: 0.1rem;
|
| 386 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
"""
|
| 388 |
|
| 389 |
with gr.Blocks(css=css, theme=gr.themes.Base(), title="Enterprise RAG") as demo:
|
|
@@ -442,6 +501,17 @@ with gr.Blocks(css=css, theme=gr.themes.Base(), title="Enterprise RAG") as demo:
|
|
| 442 |
height=240, # Increased height
|
| 443 |
)
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
# Spacer before Process button
|
| 446 |
gr.HTML('<div style="height: 1.5rem"></div>')
|
| 447 |
|
|
@@ -468,7 +538,7 @@ with gr.Blocks(css=css, theme=gr.themes.Base(), title="Enterprise RAG") as demo:
|
|
| 468 |
show_label=False,
|
| 469 |
)
|
| 470 |
model_status = gr.Markdown(
|
| 471 |
-
"
|
| 472 |
elem_classes="model-status",
|
| 473 |
)
|
| 474 |
|
|
@@ -520,7 +590,7 @@ with gr.Blocks(css=css, theme=gr.themes.Base(), title="Enterprise RAG") as demo:
|
|
| 520 |
</div>
|
| 521 |
""")
|
| 522 |
|
| 523 |
-
# Event Wiring
|
| 524 |
load_legal.click(fn=lambda: app.load_samples("Legal"), outputs=load_status)
|
| 525 |
load_research.click(fn=lambda: app.load_samples("Research"), outputs=load_status)
|
| 526 |
load_finops.click(fn=lambda: app.load_samples("FinOps"), outputs=load_status)
|
|
|
|
| 14 |
self.loaded_documents = []
|
| 15 |
|
| 16 |
def load_samples(self, vertical):
|
| 17 |
+
"""Load sample documents with live progress updates"""
|
| 18 |
samples = {
|
| 19 |
"Legal": [
|
| 20 |
"data/samples/legal/service_agreement.txt",
|
|
|
|
| 34 |
}
|
| 35 |
|
| 36 |
try:
|
| 37 |
+
total_chunks = 0
|
| 38 |
+
for idx, path in enumerate(samples[vertical], 1):
|
| 39 |
if os.path.exists(path):
|
| 40 |
+
yield f"Loading document {idx}/{len(samples[vertical])}..."
|
| 41 |
chunks = self.processor.process_txt(path)
|
| 42 |
+
|
| 43 |
+
yield f"Creating smart chunks ({len(chunks)} chunks)..."
|
| 44 |
self.rag_pipeline.add_documents(chunks, is_sample=True)
|
| 45 |
+
|
| 46 |
+
yield f"Building search index..."
|
| 47 |
self.loaded_documents.append(os.path.basename(path))
|
| 48 |
+
total_chunks += len(chunks)
|
| 49 |
+
|
| 50 |
+
yield f"✓ Success! Loaded {len(samples[vertical])} documents ({total_chunks} searchable chunks)"
|
| 51 |
except Exception as e:
|
| 52 |
+
yield f"❌ Error: {str(e)}"
|
| 53 |
|
| 54 |
def process_file(self, file):
|
| 55 |
+
"""Process uploaded file with live progress updates"""
|
| 56 |
if not file:
|
| 57 |
+
yield "⚠️ Please upload a file"
|
| 58 |
+
return
|
| 59 |
+
|
| 60 |
try:
|
| 61 |
+
filename = os.path.basename(file.name)
|
| 62 |
+
yield f"Processing {filename}..."
|
| 63 |
+
|
| 64 |
ext = os.path.splitext(file.name)[1].lower()
|
| 65 |
if ext == ".pdf":
|
| 66 |
chunks = self.processor.process_pdf(file.name)
|
|
|
|
| 69 |
elif ext == ".docx":
|
| 70 |
chunks = self.processor.process_docx(file.name)
|
| 71 |
else:
|
| 72 |
+
yield "❌ Unsupported format. Please upload PDF, DOCX, or TXT files."
|
| 73 |
+
return
|
| 74 |
+
|
| 75 |
+
yield f"✂️ Created {len(chunks)} smart chunks..."
|
| 76 |
|
| 77 |
+
yield f"Building search index (securing with AES-256)..."
|
| 78 |
self.rag_pipeline.add_documents(chunks, is_sample=False)
|
| 79 |
+
self.loaded_documents.append(filename)
|
| 80 |
+
|
| 81 |
+
yield f"✓ Success! {filename} ready for questions ({len(chunks)} searchable chunks)"
|
|
|
|
| 82 |
except Exception as e:
|
| 83 |
+
yield f"❌ Error: {str(e)}. Please try again or contact support."
|
| 84 |
|
| 85 |
def switch_model(self, model_choice):
|
| 86 |
"""Handle model switching from UI radio button"""
|
|
|
|
| 402 |
padding: 0.25rem 0.5rem;
|
| 403 |
margin-top: 0.1rem;
|
| 404 |
}
|
| 405 |
+
|
| 406 |
+
/* --- SECURITY BADGE --- */
|
| 407 |
+
.security-badge {
|
| 408 |
+
display: flex;
|
| 409 |
+
align-items: center;
|
| 410 |
+
gap: 0.75rem;
|
| 411 |
+
background: rgba(16, 185, 129, 0.08) !important;
|
| 412 |
+
border: 1px solid rgba(16, 185, 129, 0.2) !important;
|
| 413 |
+
border-radius: 12px;
|
| 414 |
+
padding: 0.75rem 1rem;
|
| 415 |
+
margin-top: 0.75rem;
|
| 416 |
+
transition: all 0.3s ease;
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
.security-badge:hover {
|
| 420 |
+
background: rgba(16, 185, 129, 0.12) !important;
|
| 421 |
+
border-color: rgba(16, 185, 129, 0.3) !important;
|
| 422 |
+
box-shadow: 0 0 20px rgba(16, 185, 129, 0.15);
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
.badge-icon {
|
| 426 |
+
font-size: 1.5rem;
|
| 427 |
+
line-height: 1;
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
.badge-content {
|
| 431 |
+
flex: 1;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
.badge-title {
|
| 435 |
+
font-size: 0.9rem;
|
| 436 |
+
font-weight: 600;
|
| 437 |
+
color: var(--accent);
|
| 438 |
+
margin-bottom: 0.2rem;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
.badge-subtitle {
|
| 442 |
+
font-size: 0.75rem;
|
| 443 |
+
color: var(--text-secondary);
|
| 444 |
+
opacity: 0.8;
|
| 445 |
+
}
|
| 446 |
"""
|
| 447 |
|
| 448 |
with gr.Blocks(css=css, theme=gr.themes.Base(), title="Enterprise RAG") as demo:
|
|
|
|
| 501 |
height=240, # Increased height
|
| 502 |
)
|
| 503 |
|
| 504 |
+
# Security Badge
|
| 505 |
+
gr.HTML("""
|
| 506 |
+
<div class="security-badge">
|
| 507 |
+
<div class="badge-icon">🔒</div>
|
| 508 |
+
<div class="badge-content">
|
| 509 |
+
<div class="badge-title">AES-256 Encrypted</div>
|
| 510 |
+
<div class="badge-subtitle">Processed locally • Auto-deleted in 7 days</div>
|
| 511 |
+
</div>
|
| 512 |
+
</div>
|
| 513 |
+
""")
|
| 514 |
+
|
| 515 |
# Spacer before Process button
|
| 516 |
gr.HTML('<div style="height: 1.5rem"></div>')
|
| 517 |
|
|
|
|
| 538 |
show_label=False,
|
| 539 |
)
|
| 540 |
model_status = gr.Markdown(
|
| 541 |
+
"_GPT-OSS 120B active_",
|
| 542 |
elem_classes="model-status",
|
| 543 |
)
|
| 544 |
|
|
|
|
| 590 |
</div>
|
| 591 |
""")
|
| 592 |
|
| 593 |
+
# Event Wiring with live updates (generators)
|
| 594 |
load_legal.click(fn=lambda: app.load_samples("Legal"), outputs=load_status)
|
| 595 |
load_research.click(fn=lambda: app.load_samples("Research"), outputs=load_status)
|
| 596 |
load_finops.click(fn=lambda: app.load_samples("FinOps"), outputs=load_status)
|