Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,46 +7,57 @@ from mann_engram_en.router import MANNEngramRouter
|
|
| 7 |
# 强制缓存路径
|
| 8 |
os.environ["HF_HOME"] = "/home/user/.cache/huggingface"
|
| 9 |
|
| 10 |
-
#
|
| 11 |
-
hf_client = InferenceClient("Qwen/Qwen2.5-72B-Instruct")
|
| 12 |
router_engine = None
|
| 13 |
|
| 14 |
def init_engine():
|
| 15 |
global router_engine
|
| 16 |
if router_engine is None:
|
| 17 |
print("Initializing SiGLIP Tensor Routing Core...")
|
| 18 |
-
# 💡 架构升级 2:关闭笨重且缓慢的本地 CPU LLM,纯粹依靠 SiGLIP 做边缘路由
|
| 19 |
router_engine = MANNEngramRouter(
|
| 20 |
ckpt_path="./weights/skew_model_v4full_en.pt",
|
| 21 |
enable_local_intent=False
|
| 22 |
)
|
| 23 |
return router_engine
|
| 24 |
|
| 25 |
-
def extract_intent_via_api(messy_text):
|
| 26 |
-
"""使用 72B 大脑进行
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
try:
|
| 29 |
-
res =
|
| 30 |
messages=[
|
| 31 |
{"role": "system", "content": sys_prompt},
|
| 32 |
{"role": "user", "content": messy_text}
|
| 33 |
],
|
| 34 |
-
max_tokens=
|
| 35 |
)
|
| 36 |
return res.choices[0].message.content.strip()
|
| 37 |
except Exception as e:
|
| 38 |
-
|
| 39 |
-
return messy_text # 兜底逻辑
|
| 40 |
|
| 41 |
-
def process_input(query, files):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
if not query:
|
| 43 |
-
return "⚠️ Please enter a clinical query.", "
|
| 44 |
|
| 45 |
engine = init_engine()
|
| 46 |
|
| 47 |
file_paths = []
|
| 48 |
image_paths = []
|
| 49 |
-
|
| 50 |
if files:
|
| 51 |
for f in files:
|
| 52 |
ext = f.name.lower().split('.')[-1]
|
|
@@ -57,104 +68,91 @@ def process_input(query, files):
|
|
| 57 |
|
| 58 |
start_time = time.time()
|
| 59 |
|
| 60 |
-
# --- 阶段 1: Cloud Intelligence (
|
| 61 |
-
clean_intent = extract_intent_via_api(query)
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
| 65 |
results = engine.compress(
|
| 66 |
query=clean_intent,
|
| 67 |
-
context_pool=[],
|
| 68 |
image_pool=image_paths,
|
| 69 |
top_p=0.85
|
| 70 |
)
|
| 71 |
|
| 72 |
latency = time.time() - start_time
|
| 73 |
|
| 74 |
-
# 构建
|
| 75 |
-
intent = clean_intent
|
| 76 |
-
context = results.get("purified_context", "N/A")
|
| 77 |
stats_dict = results.get("stats", {})
|
| 78 |
-
|
| 79 |
stats_report = (
|
| 80 |
f"✅ Extraction Complete\n"
|
| 81 |
f"---------------------------\n"
|
| 82 |
-
f"🧠 Cloud
|
| 83 |
-
f"👁️ Edge Vision
|
| 84 |
-
f"🖼️ Images
|
| 85 |
-
f"⚡ Total Latency: {latency:.2f}
|
| 86 |
)
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
return intent, context, stats_report, routed_images
|
| 91 |
|
| 92 |
# ==========================================
|
| 93 |
-
#
|
| 94 |
# ==========================================
|
| 95 |
with gr.Blocks(title="MANN-Engram Router") as demo:
|
| 96 |
|
| 97 |
-
gr.Markdown(
|
| 98 |
-
"""
|
| 99 |
-
# 🧠 MANN-Engram: Edge-Cloud Multimodal Semantic Router
|
| 100 |
-
### A Privacy-First, Zero-Hallucination Shield for Clinical Vision-Language Models.
|
| 101 |
-
"""
|
| 102 |
-
)
|
| 103 |
-
|
| 104 |
-
with gr.Accordion("⚙️ System Architecture & Capabilities (Click to expand)", open=False):
|
| 105 |
-
gr.Markdown(
|
| 106 |
-
"""
|
| 107 |
-
* **Cloud-side Intent Extraction (Qwen 72B):** Uses zero-shot reasoning to parse messy, emotion-heavy patient inputs into clean medical vectors.
|
| 108 |
-
* **Edge-side Tensor Routing (SiGLIP + MANN):** Projects clinical text and medical imaging (CT, MRI, X-ray) into a shared latent space to dynamically filter out irrelevant exams.
|
| 109 |
-
* **Zero-Hallucination Guarantee:** The router does *not* generate new medical facts. It acts purely as a strict extraction and filtering pipeline.
|
| 110 |
-
"""
|
| 111 |
-
)
|
| 112 |
-
|
| 113 |
-
gr.Markdown("---")
|
| 114 |
|
| 115 |
with gr.Row():
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
gr.Markdown("### 📥 Input Console")
|
| 118 |
query_input = gr.Textbox(
|
| 119 |
-
label="
|
| 120 |
-
lines=
|
| 121 |
-
placeholder="Enter
|
| 122 |
)
|
| 123 |
-
file_input = gr.File(label="Upload
|
| 124 |
-
submit_btn = gr.Button("🚀
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
with gr.Row():
|
| 132 |
-
out_stats = gr.Textbox(label="📊 Compression Stats", interactive=False, lines=6)
|
| 133 |
-
|
| 134 |
out_gallery = gr.Gallery(
|
| 135 |
-
label="🔭
|
| 136 |
-
columns=2,
|
| 137 |
-
object_fit="contain",
|
| 138 |
-
height="auto"
|
| 139 |
)
|
| 140 |
-
|
| 141 |
-
gr.Markdown("---")
|
| 142 |
-
gr.Markdown("### 🧪 Quick Try: 'Hell-Mode' Clinical Scenarios")
|
| 143 |
-
gr.Examples(
|
| 144 |
-
examples=[
|
| 145 |
-
[
|
| 146 |
-
"Doctor, you have to help me. The food in this ward is absolutely terrible, it's giving me severe stomach aches and diarrhea (see my abdominal scan). Also, my legs have been cramping up lately when I walk to the bathroom, and I have this chronic smoker's cough that won't go away. But honestly, the reason I came to the ER yesterday is that my vision suddenly blurred, I had a seizure, and the left side of my body went numb. My family is threatening to sue the hospital if you don't look at my head scans immediately. Is the mass growing?",
|
| 147 |
-
None
|
| 148 |
-
]
|
| 149 |
-
],
|
| 150 |
-
inputs=[query_input, file_input],
|
| 151 |
-
label="Click a scenario below to auto-fill the input (You will still need to upload the corresponding test images manually):"
|
| 152 |
-
)
|
| 153 |
|
|
|
|
| 154 |
submit_btn.click(
|
| 155 |
fn=process_input,
|
| 156 |
-
inputs=[query_input, file_input],
|
| 157 |
-
outputs=[out_intent,
|
| 158 |
)
|
| 159 |
|
| 160 |
if __name__ == "__main__":
|
|
|
|
| 7 |
# 强制缓存路径
|
| 8 |
os.environ["HF_HOME"] = "/home/user/.cache/huggingface"
|
| 9 |
|
| 10 |
+
# 全局引擎变量
|
|
|
|
| 11 |
router_engine = None
|
| 12 |
|
| 13 |
def init_engine():
|
| 14 |
global router_engine
|
| 15 |
if router_engine is None:
|
| 16 |
print("Initializing SiGLIP Tensor Routing Core...")
|
|
|
|
| 17 |
router_engine = MANNEngramRouter(
|
| 18 |
ckpt_path="./weights/skew_model_v4full_en.pt",
|
| 19 |
enable_local_intent=False
|
| 20 |
)
|
| 21 |
return router_engine
|
| 22 |
|
| 23 |
+
def extract_intent_via_api(messy_text, hf_token):
|
| 24 |
+
"""使用 72B 大脑进行意图提取,动态使用用户提供的 Token"""
|
| 25 |
+
if not hf_token:
|
| 26 |
+
return "ERROR: MISSING TOKEN"
|
| 27 |
+
|
| 28 |
+
# 动态创建 Client
|
| 29 |
+
client = InferenceClient("Qwen/Qwen2.5-72B-Instruct", token=hf_token)
|
| 30 |
+
|
| 31 |
+
sys_prompt = (
|
| 32 |
+
"You are an expert clinical triage AI. Extract ONLY the critical medical symptoms and targeted body parts. "
|
| 33 |
+
"IGNORE all noise like hospital food, billing, emotions, or social complaints. "
|
| 34 |
+
"Output a single concise English sentence."
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
try:
|
| 38 |
+
res = client.chat_completion(
|
| 39 |
messages=[
|
| 40 |
{"role": "system", "content": sys_prompt},
|
| 41 |
{"role": "user", "content": messy_text}
|
| 42 |
],
|
| 43 |
+
max_tokens=60
|
| 44 |
)
|
| 45 |
return res.choices[0].message.content.strip()
|
| 46 |
except Exception as e:
|
| 47 |
+
return f"API ERROR: {str(e)}"
|
|
|
|
| 48 |
|
| 49 |
+
def process_input(query, files, hf_token):
|
| 50 |
+
"""处理输入的主函数"""
|
| 51 |
+
if not hf_token:
|
| 52 |
+
return "❌ Error", "Please provide a Hugging Face API Token in the settings.", "Status: Failed due to missing token.", []
|
| 53 |
+
|
| 54 |
if not query:
|
| 55 |
+
return "⚠️ Warning", "Please enter a clinical query.", "Status: Waiting for input.", []
|
| 56 |
|
| 57 |
engine = init_engine()
|
| 58 |
|
| 59 |
file_paths = []
|
| 60 |
image_paths = []
|
|
|
|
| 61 |
if files:
|
| 62 |
for f in files:
|
| 63 |
ext = f.name.lower().split('.')[-1]
|
|
|
|
| 68 |
|
| 69 |
start_time = time.time()
|
| 70 |
|
| 71 |
+
# --- 阶段 1: Cloud Intelligence (使用用户 Token) ---
|
| 72 |
+
clean_intent = extract_intent_via_api(query, hf_token)
|
| 73 |
|
| 74 |
+
if "API ERROR" in clean_intent or "ERROR: MISSING" in clean_intent:
|
| 75 |
+
return "Cloud API Error", clean_intent, "Please check your HF Token and try again.", []
|
| 76 |
+
|
| 77 |
+
# --- 阶段 2: Edge Tensor Routing ---
|
| 78 |
results = engine.compress(
|
| 79 |
query=clean_intent,
|
| 80 |
+
context_pool=[],
|
| 81 |
image_pool=image_paths,
|
| 82 |
top_p=0.85
|
| 83 |
)
|
| 84 |
|
| 85 |
latency = time.time() - start_time
|
| 86 |
|
| 87 |
+
# 构建统计报告
|
|
|
|
|
|
|
| 88 |
stats_dict = results.get("stats", {})
|
|
|
|
| 89 |
stats_report = (
|
| 90 |
f"✅ Extraction Complete\n"
|
| 91 |
f"---------------------------\n"
|
| 92 |
+
f"🧠 Cloud Brain: Qwen-72B-Instruct\n"
|
| 93 |
+
f"👁️ Edge Vision: SiGLIP-So400M\n"
|
| 94 |
+
f"🖼️ Images Filtered: {stats_dict.get('retained_images', 0)} / {stats_dict.get('original_images', 0)}\n"
|
| 95 |
+
f"⚡ Total Latency: {latency:.2f}s"
|
| 96 |
)
|
| 97 |
|
| 98 |
+
return clean_intent, "", stats_report, results.get("purified_images_pil", [])
|
|
|
|
|
|
|
| 99 |
|
| 100 |
# ==========================================
|
| 101 |
+
# 重新布局:左侧设置,中间输入,右侧输出
|
| 102 |
# ==========================================
|
| 103 |
with gr.Blocks(title="MANN-Engram Router") as demo:
|
| 104 |
|
| 105 |
+
gr.Markdown("# 🧠 MANN-Engram: Edge-Cloud Multimodal Router")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
with gr.Row():
|
| 108 |
+
# --- 左侧边栏:设置区 ---
|
| 109 |
+
with gr.Column(scale=2, min_width=250):
|
| 110 |
+
gr.Markdown("### ⚙️ Settings")
|
| 111 |
+
token_input = gr.Textbox(
|
| 112 |
+
label="Hugging Face API Token",
|
| 113 |
+
placeholder="hf_xxxxxxxxxxxxxxxxx",
|
| 114 |
+
type="password", # 密码模式保护隐私
|
| 115 |
+
info="Get your token at: hf.co/settings/tokens"
|
| 116 |
+
)
|
| 117 |
+
gr.Markdown(
|
| 118 |
+
"**Why Token?**\n"
|
| 119 |
+
"We use Qwen-72B (Cloud) for intent parsing and SiGLIP (Edge) for image routing. "
|
| 120 |
+
"A free HF token is required for the cloud API."
|
| 121 |
+
)
|
| 122 |
+
gr.Markdown("---")
|
| 123 |
+
gr.Markdown("### 🧪 Quick Try")
|
| 124 |
+
example_btn = gr.Examples(
|
| 125 |
+
examples=[["The hospital food is bad, it gives me stomach pain, but my real concern is the sudden numbness in my left arm and the seizures I had. Please check my head MRI.", None]],
|
| 126 |
+
inputs=[None, None], # 仅作展示,不直接填入 Token
|
| 127 |
+
label="Sample Scenario"
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
# --- 中间:输入区 ---
|
| 131 |
+
with gr.Column(scale=4):
|
| 132 |
gr.Markdown("### 📥 Input Console")
|
| 133 |
query_input = gr.Textbox(
|
| 134 |
+
label="Patient Complaint / Session Logs",
|
| 135 |
+
lines=8,
|
| 136 |
+
placeholder="Enter messy clinical text here..."
|
| 137 |
)
|
| 138 |
+
file_input = gr.File(label="Upload Diagnostic Images", file_count="multiple")
|
| 139 |
+
submit_btn = gr.Button("🚀 Process & Route", variant="primary", size="lg")
|
| 140 |
+
|
| 141 |
+
# --- 右侧:输出区 ---
|
| 142 |
+
with gr.Column(scale=4):
|
| 143 |
+
gr.Markdown("### 📤 Routing Result")
|
| 144 |
+
out_intent = gr.Textbox(label="🎯 Purified Core Intent (By Cloud 72B)", interactive=False)
|
| 145 |
+
out_stats = gr.Textbox(label="📊 Performance Metrics", interactive=False, lines=5)
|
|
|
|
|
|
|
|
|
|
| 146 |
out_gallery = gr.Gallery(
|
| 147 |
+
label="🔭 Selected Core Evidence (By Edge SiGLIP)",
|
| 148 |
+
columns=2, height="auto"
|
|
|
|
|
|
|
| 149 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
+
# 绑定事件
|
| 152 |
submit_btn.click(
|
| 153 |
fn=process_input,
|
| 154 |
+
inputs=[query_input, file_input, token_input],
|
| 155 |
+
outputs=[out_intent, gr.State(), out_stats, out_gallery]
|
| 156 |
)
|
| 157 |
|
| 158 |
if __name__ == "__main__":
|