Spaces:
Sleeping
Sleeping
Improve MindVisualizer streamed probe explanations
Browse files
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import os
|
| 2 |
import sys
|
| 3 |
from functools import lru_cache
|
|
@@ -7,6 +8,7 @@ from typing import Literal
|
|
| 7 |
import numpy as np
|
| 8 |
from fastapi import FastAPI, HTTPException
|
| 9 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 10 |
from pydantic import BaseModel, Field
|
| 11 |
|
| 12 |
|
|
@@ -150,26 +152,79 @@ def trajectory(req: TrajectoryRequest):
|
|
| 150 |
|
| 151 |
@app.post("/api/mindvis/explain")
|
| 152 |
def explain(req: ExplainRequest):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
svc = services()
|
| 154 |
traj = np.asarray(req.trajectory, dtype=np.float32)
|
| 155 |
if traj.ndim != 2 or traj.shape[1] != 3:
|
| 156 |
raise HTTPException(status_code=400, detail="trajectory must be an array of [x,y,z] points")
|
| 157 |
|
|
|
|
| 158 |
world = denormalized(np.clip(traj, 0.0, 1.0), svc["amin"], svc["span"])
|
| 159 |
sampler = svc["samplers"].get(req.fieldMode, svc["samplers"]["mean"])
|
| 160 |
mags = np.linalg.norm(sampler.sample_vec(world), axis=1).astype(np.float32)
|
|
|
|
| 161 |
transitions = analyze_regions(world, mags, req.callLlm)
|
|
|
|
| 162 |
text = format_transition_text(transitions)
|
| 163 |
|
| 164 |
if req.callLlm:
|
| 165 |
from src.region_analyzer import analyze_with_gpt
|
| 166 |
|
|
|
|
| 167 |
text = analyze_with_gpt(
|
| 168 |
transitions,
|
| 169 |
use_rag=os.environ.get("MINDVIS_USE_RAG", "0") == "1",
|
| 170 |
model=os.environ.get("OPENAI_MODEL", "gpt-5.4-mini"),
|
| 171 |
debug=os.environ.get("MINDVIS_DEBUG", "0") == "1",
|
| 172 |
)
|
|
|
|
| 173 |
|
| 174 |
return {"text": text, "transitions": transitions, "regionText": format_transition_text(transitions)}
|
| 175 |
|
|
|
|
| 1 |
+
import json
|
| 2 |
import os
|
| 3 |
import sys
|
| 4 |
from functools import lru_cache
|
|
|
|
| 8 |
import numpy as np
|
| 9 |
from fastapi import FastAPI, HTTPException
|
| 10 |
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
+
from fastapi.responses import StreamingResponse
|
| 12 |
from pydantic import BaseModel, Field
|
| 13 |
|
| 14 |
|
|
|
|
| 152 |
|
| 153 |
@app.post("/api/mindvis/explain")
|
| 154 |
def explain(req: ExplainRequest):
|
| 155 |
+
return build_explanation(req)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
@app.post("/api/mindvis/explain/stream")
|
| 159 |
+
def explain_stream(req: ExplainRequest):
|
| 160 |
+
def event_stream():
|
| 161 |
+
def event(payload: dict) -> str:
|
| 162 |
+
return json.dumps(payload) + "\n"
|
| 163 |
+
|
| 164 |
+
try:
|
| 165 |
+
yield event({"type": "log", "message": "Loading MindVisualizer field samplers and brain label grid."})
|
| 166 |
+
svc = services()
|
| 167 |
+
traj = np.asarray(req.trajectory, dtype=np.float32)
|
| 168 |
+
if traj.ndim != 2 or traj.shape[1] != 3:
|
| 169 |
+
raise HTTPException(status_code=400, detail="trajectory must be an array of [x,y,z] points")
|
| 170 |
+
|
| 171 |
+
yield event({"type": "log", "message": f"Received {len(traj)} normalized probe samples in {req.fieldMode} mode."})
|
| 172 |
+
world = denormalized(np.clip(traj, 0.0, 1.0), svc["amin"], svc["span"])
|
| 173 |
+
sampler = svc["samplers"].get(req.fieldMode, svc["samplers"]["mean"])
|
| 174 |
+
mags = np.linalg.norm(sampler.sample_vec(world), axis=1).astype(np.float32)
|
| 175 |
+
|
| 176 |
+
yield event({"type": "log", "message": "Sampling flow strength and entropy along the traveled path."})
|
| 177 |
+
transitions = analyze_regions(world, mags, req.callLlm)
|
| 178 |
+
yield event({"type": "log", "message": f"Detected {len(transitions)} entered or nearby anatomical regions."})
|
| 179 |
+
|
| 180 |
+
text = format_transition_text(transitions)
|
| 181 |
+
if req.callLlm:
|
| 182 |
+
from src.region_analyzer import analyze_with_gpt
|
| 183 |
+
|
| 184 |
+
yield event({"type": "log", "message": "Calling the OpenAI model for the neuroscience interpretation."})
|
| 185 |
+
text = analyze_with_gpt(
|
| 186 |
+
transitions,
|
| 187 |
+
use_rag=os.environ.get("MINDVIS_USE_RAG", "0") == "1",
|
| 188 |
+
model=os.environ.get("OPENAI_MODEL", "gpt-5.4-mini"),
|
| 189 |
+
debug=os.environ.get("MINDVIS_DEBUG", "0") == "1",
|
| 190 |
+
)
|
| 191 |
+
yield event({"type": "log", "message": "LLM interpretation finished."})
|
| 192 |
+
|
| 193 |
+
yield event({"type": "result", "text": text, "transitions": transitions, "regionText": format_transition_text(transitions)})
|
| 194 |
+
except Exception as exc:
|
| 195 |
+
yield event({"type": "error", "message": str(exc)})
|
| 196 |
+
|
| 197 |
+
return StreamingResponse(event_stream(), media_type="application/x-ndjson")
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def build_explanation(req: ExplainRequest, log=None):
|
| 201 |
+
log = log or (lambda _message: None)
|
| 202 |
+
log("Loading MindVisualizer field samplers and brain label grid.")
|
| 203 |
svc = services()
|
| 204 |
traj = np.asarray(req.trajectory, dtype=np.float32)
|
| 205 |
if traj.ndim != 2 or traj.shape[1] != 3:
|
| 206 |
raise HTTPException(status_code=400, detail="trajectory must be an array of [x,y,z] points")
|
| 207 |
|
| 208 |
+
log(f"Received {len(traj)} normalized probe samples in {req.fieldMode} mode.")
|
| 209 |
world = denormalized(np.clip(traj, 0.0, 1.0), svc["amin"], svc["span"])
|
| 210 |
sampler = svc["samplers"].get(req.fieldMode, svc["samplers"]["mean"])
|
| 211 |
mags = np.linalg.norm(sampler.sample_vec(world), axis=1).astype(np.float32)
|
| 212 |
+
log("Sampling flow strength and entropy along the traveled path.")
|
| 213 |
transitions = analyze_regions(world, mags, req.callLlm)
|
| 214 |
+
log(f"Detected {len(transitions)} entered or nearby anatomical regions.")
|
| 215 |
text = format_transition_text(transitions)
|
| 216 |
|
| 217 |
if req.callLlm:
|
| 218 |
from src.region_analyzer import analyze_with_gpt
|
| 219 |
|
| 220 |
+
log("Calling the OpenAI model for the neuroscience interpretation.")
|
| 221 |
text = analyze_with_gpt(
|
| 222 |
transitions,
|
| 223 |
use_rag=os.environ.get("MINDVIS_USE_RAG", "0") == "1",
|
| 224 |
model=os.environ.get("OPENAI_MODEL", "gpt-5.4-mini"),
|
| 225 |
debug=os.environ.get("MINDVIS_DEBUG", "0") == "1",
|
| 226 |
)
|
| 227 |
+
log("LLM interpretation finished.")
|
| 228 |
|
| 229 |
return {"text": text, "transitions": transitions, "regionText": format_transition_text(transitions)}
|
| 230 |
|