Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,14 +7,14 @@ from rank_bm25 import BM25Okapi
|
|
| 7 |
import gradio as gr
|
| 8 |
from huggingface_hub import InferenceClient
|
| 9 |
|
| 10 |
-
#
|
| 11 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 12 |
PRIMARY_MODEL = "google/gemma-4-31B-it"
|
| 13 |
FALLBACK_MODEL = "google/gemma-2-2b-it"
|
| 14 |
|
| 15 |
client = InferenceClient(token=HF_TOKEN)
|
| 16 |
|
| 17 |
-
#
|
| 18 |
@dataclass(frozen=True)
|
| 19 |
class Chunk:
|
| 20 |
chunk_id: str
|
|
@@ -60,7 +60,7 @@ def grounding_score(answer: str, chunks: List[Chunk]) -> float:
|
|
| 60 |
overlap = len(words & ref_words) / max(len(words), 1)
|
| 61 |
return (len(cited) / len(sentences) * 0.7) + (overlap * 0.3)
|
| 62 |
|
| 63 |
-
#
|
| 64 |
CURRENT_INDEX = None
|
| 65 |
CURRENT_CHUNKS: List[Chunk] = []
|
| 66 |
|
|
@@ -79,24 +79,24 @@ def process_uploads(files):
|
|
| 79 |
p = Path(fp)
|
| 80 |
print(f"[DEBUG] Processing: {p} (exists={p.exists()}, size={p.stat().st_size if p.exists() else 'N/A'})")
|
| 81 |
if p.suffix.lower() != ".txt":
|
| 82 |
-
return f"
|
| 83 |
|
| 84 |
all_chunks.extend(load_text(p))
|
| 85 |
|
| 86 |
if not all_chunks:
|
| 87 |
-
return f"
|
| 88 |
|
| 89 |
corpus = [_tokenize(c.text) for c in all_chunks]
|
| 90 |
CURRENT_INDEX = BM25Okapi(corpus)
|
| 91 |
CURRENT_CHUNKS = all_chunks
|
| 92 |
|
| 93 |
preview = "\n".join(f" β’ Chunk {i+1}: {c.text[:80]}β¦" for i, c in enumerate(all_chunks[:3]))
|
| 94 |
-
return f"
|
| 95 |
|
| 96 |
# ββ 4. Agentic Pipeline with Reasoning Trace βββββββββββββββββ
|
| 97 |
def agentic_answer(question: str):
|
| 98 |
if not HF_TOKEN:
|
| 99 |
-
return "
|
| 100 |
if CURRENT_INDEX is None:
|
| 101 |
return "Upload document(s) first!", "", "", "", ""
|
| 102 |
|
|
@@ -148,7 +148,7 @@ def agentic_answer(question: str):
|
|
| 148 |
continue
|
| 149 |
|
| 150 |
if not answer_text:
|
| 151 |
-
return "
|
| 152 |
|
| 153 |
# Step 3: Split Reasoning Trace from Final Answer
|
| 154 |
if "<reasoning_trace>" in answer_text:
|
|
@@ -156,7 +156,7 @@ def agentic_answer(question: str):
|
|
| 156 |
trace = parts[0].replace("<reasoning_trace>", "").strip()
|
| 157 |
final_ans = parts[1].strip() if len(parts) > 1 else "Analysis complete."
|
| 158 |
else:
|
| 159 |
-
trace = "
|
| 160 |
final_ans = answer_text
|
| 161 |
|
| 162 |
# Step 4: Compute Trust & Safety Badges
|
|
@@ -165,7 +165,7 @@ def agentic_answer(question: str):
|
|
| 165 |
|
| 166 |
trust_cls = "high-trust" if score > 0.5 else "low-trust"
|
| 167 |
safety_cls = "safety-refusal" if is_refusal else "safety-pass"
|
| 168 |
-
safety_label = "
|
| 169 |
|
| 170 |
trust_html = f'<div class="badge {trust_cls}">Trust: {score:.2f} ({model_used})</div>'
|
| 171 |
safety_html = f'<div class="badge {safety_cls}">{safety_label}</div>'
|
|
@@ -173,7 +173,7 @@ def agentic_answer(question: str):
|
|
| 173 |
|
| 174 |
return final_ans, trace, sources_md, trust_html, safety_html
|
| 175 |
|
| 176 |
-
#
|
| 177 |
CUSTOM_CSS = """
|
| 178 |
:root { --p: #2c5282; --bg: #0f1117; --accent: #805ad5; }
|
| 179 |
.gradio-container { background: var(--bg) !important; color: #e2e8f0 !important; font-family: system-ui, sans-serif; }
|
|
@@ -209,7 +209,7 @@ button.primary { background: linear-gradient(135deg, #2c5282, #805ad5) !importan
|
|
| 209 |
|
| 210 |
with gr.Blocks(css=CUSTOM_CSS, title="Medical RAG Studio", theme=gr.themes.Soft()) as demo:
|
| 211 |
with gr.Column(elem_id="header"):
|
| 212 |
-
gr.Markdown("#
|
| 213 |
|
| 214 |
with gr.Row():
|
| 215 |
with gr.Column(scale=2):
|
|
@@ -220,7 +220,7 @@ with gr.Blocks(css=CUSTOM_CSS, title="Medical RAG Studio", theme=gr.themes.Soft(
|
|
| 220 |
btn = gr.Button("Analyze with Gemma", variant="primary")
|
| 221 |
|
| 222 |
# Demo Questions for Judges
|
| 223 |
-
gr.Markdown("###
|
| 224 |
gr.Examples(
|
| 225 |
examples=[
|
| 226 |
["What are the symptoms of a common cold according to the documents?"],
|
|
@@ -241,7 +241,7 @@ with gr.Blocks(css=CUSTOM_CSS, title="Medical RAG Studio", theme=gr.themes.Soft(
|
|
| 241 |
ans = gr.Markdown(elem_id="ans")
|
| 242 |
|
| 243 |
# Reasoning Trace Accordion β Explainable AI for judges
|
| 244 |
-
with gr.Accordion("
|
| 245 |
trace_output = gr.Markdown(
|
| 246 |
value="*Reasoning trace will appear here after analysis...*"
|
| 247 |
)
|
|
|
|
| 7 |
import gradio as gr
|
| 8 |
from huggingface_hub import InferenceClient
|
| 9 |
|
| 10 |
+
# 1. Auth & Models
|
| 11 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 12 |
PRIMARY_MODEL = "google/gemma-4-31B-it"
|
| 13 |
FALLBACK_MODEL = "google/gemma-2-2b-it"
|
| 14 |
|
| 15 |
client = InferenceClient(token=HF_TOKEN)
|
| 16 |
|
| 17 |
+
# 2. RAG Utilities
|
| 18 |
@dataclass(frozen=True)
|
| 19 |
class Chunk:
|
| 20 |
chunk_id: str
|
|
|
|
| 60 |
overlap = len(words & ref_words) / max(len(words), 1)
|
| 61 |
return (len(cited) / len(sentences) * 0.7) + (overlap * 0.3)
|
| 62 |
|
| 63 |
+
# 3. Global State
|
| 64 |
CURRENT_INDEX = None
|
| 65 |
CURRENT_CHUNKS: List[Chunk] = []
|
| 66 |
|
|
|
|
| 79 |
p = Path(fp)
|
| 80 |
print(f"[DEBUG] Processing: {p} (exists={p.exists()}, size={p.stat().st_size if p.exists() else 'N/A'})")
|
| 81 |
if p.suffix.lower() != ".txt":
|
| 82 |
+
return f"Only .txt files are supported. Unsupported file: **{p.name}**"
|
| 83 |
|
| 84 |
all_chunks.extend(load_text(p))
|
| 85 |
|
| 86 |
if not all_chunks:
|
| 87 |
+
return f" No text extracted from {len(files)} file(s). Check file content."
|
| 88 |
|
| 89 |
corpus = [_tokenize(c.text) for c in all_chunks]
|
| 90 |
CURRENT_INDEX = BM25Okapi(corpus)
|
| 91 |
CURRENT_CHUNKS = all_chunks
|
| 92 |
|
| 93 |
preview = "\n".join(f" β’ Chunk {i+1}: {c.text[:80]}β¦" for i, c in enumerate(all_chunks[:3]))
|
| 94 |
+
return f" Indexed **{len(all_chunks)} chunks** from {len(files)} file(s).\n\n{preview}"
|
| 95 |
|
| 96 |
# ββ 4. Agentic Pipeline with Reasoning Trace βββββββββββββββββ
|
| 97 |
def agentic_answer(question: str):
|
| 98 |
if not HF_TOKEN:
|
| 99 |
+
return " Missing HF_TOKEN! Add it in Space Settings β Secrets.", "", "", "", ""
|
| 100 |
if CURRENT_INDEX is None:
|
| 101 |
return "Upload document(s) first!", "", "", "", ""
|
| 102 |
|
|
|
|
| 148 |
continue
|
| 149 |
|
| 150 |
if not answer_text:
|
| 151 |
+
return " Both models unavailable. Check your HF_TOKEN and model access.", "", "", "", ""
|
| 152 |
|
| 153 |
# Step 3: Split Reasoning Trace from Final Answer
|
| 154 |
if "<reasoning_trace>" in answer_text:
|
|
|
|
| 156 |
trace = parts[0].replace("<reasoning_trace>", "").strip()
|
| 157 |
final_ans = parts[1].strip() if len(parts) > 1 else "Analysis complete."
|
| 158 |
else:
|
| 159 |
+
trace = " *Standard inference performed β no explicit trace returned by model.*"
|
| 160 |
final_ans = answer_text
|
| 161 |
|
| 162 |
# Step 4: Compute Trust & Safety Badges
|
|
|
|
| 165 |
|
| 166 |
trust_cls = "high-trust" if score > 0.5 else "low-trust"
|
| 167 |
safety_cls = "safety-refusal" if is_refusal else "safety-pass"
|
| 168 |
+
safety_label = " Safety Advisory" if is_refusal else " Safety: PASSED"
|
| 169 |
|
| 170 |
trust_html = f'<div class="badge {trust_cls}">Trust: {score:.2f} ({model_used})</div>'
|
| 171 |
safety_html = f'<div class="badge {safety_cls}">{safety_label}</div>'
|
|
|
|
| 173 |
|
| 174 |
return final_ans, trace, sources_md, trust_html, safety_html
|
| 175 |
|
| 176 |
+
# 5. UI
|
| 177 |
CUSTOM_CSS = """
|
| 178 |
:root { --p: #2c5282; --bg: #0f1117; --accent: #805ad5; }
|
| 179 |
.gradio-container { background: var(--bg) !important; color: #e2e8f0 !important; font-family: system-ui, sans-serif; }
|
|
|
|
| 209 |
|
| 210 |
with gr.Blocks(css=CUSTOM_CSS, title="Medical RAG Studio", theme=gr.themes.Soft()) as demo:
|
| 211 |
with gr.Column(elem_id="header"):
|
| 212 |
+
gr.Markdown("# Medical RAG Studio\n### High-Trust Agentic Intelligence for Health Discovery")
|
| 213 |
|
| 214 |
with gr.Row():
|
| 215 |
with gr.Column(scale=2):
|
|
|
|
| 220 |
btn = gr.Button("Analyze with Gemma", variant="primary")
|
| 221 |
|
| 222 |
# Demo Questions for Judges
|
| 223 |
+
gr.Markdown("### Try These Demo Queries")
|
| 224 |
gr.Examples(
|
| 225 |
examples=[
|
| 226 |
["What are the symptoms of a common cold according to the documents?"],
|
|
|
|
| 241 |
ans = gr.Markdown(elem_id="ans")
|
| 242 |
|
| 243 |
# Reasoning Trace Accordion β Explainable AI for judges
|
| 244 |
+
with gr.Accordion(" View Gemma's Reasoning Trace", open=False, elem_id="trace-accordion"):
|
| 245 |
trace_output = gr.Markdown(
|
| 246 |
value="*Reasoning trace will appear here after analysis...*"
|
| 247 |
)
|