π Fix: Align backend routes with frontend (resolve 404/405 errors)
Browse files- backend/app.py +68 -22
backend/app.py
CHANGED
|
@@ -67,7 +67,7 @@ async def _init_db():
|
|
| 67 |
print(f"β οΈ MongoDB unavailable ({e}) β using in-memory storage")
|
| 68 |
|
| 69 |
async def _seed_journal():
|
| 70 |
-
"""Seed the journal with initial milestones
|
| 71 |
try:
|
| 72 |
existing = await _db_list_journal(1)
|
| 73 |
if existing: return
|
|
@@ -93,7 +93,7 @@ async def _db_insert_inspection(data):
|
|
| 93 |
else:
|
| 94 |
_mem_inspections.append(data)
|
| 95 |
|
| 96 |
-
async def _db_list_inspections(limit=
|
| 97 |
if _inspections_col is not None:
|
| 98 |
cursor = _inspections_col.find({}, {"_id": 0}).sort("timestamp", -1).limit(limit)
|
| 99 |
return await cursor.to_list(length=limit)
|
|
@@ -114,7 +114,8 @@ async def _db_list_journal(limit=50):
|
|
| 114 |
# ββ API ENDPOINTS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 115 |
|
| 116 |
@app.get("/api/health")
|
| 117 |
-
@app.get("/
|
|
|
|
| 118 |
async def health():
|
| 119 |
return {
|
| 120 |
"status": "online",
|
|
@@ -124,45 +125,46 @@ async def health():
|
|
| 124 |
}
|
| 125 |
|
| 126 |
@app.get("/api/inspections")
|
| 127 |
-
async def get_inspections():
|
| 128 |
-
items = await _db_list_inspections()
|
| 129 |
return items
|
| 130 |
|
| 131 |
-
@app.
|
| 132 |
-
async def
|
| 133 |
-
items = await _db_list_journal()
|
| 134 |
-
return items
|
| 135 |
-
|
| 136 |
-
@app.post("/api/inspect")
|
| 137 |
-
async def handle_inspect(request: Request):
|
| 138 |
"""Triggers the full multi-agent QC pipeline."""
|
| 139 |
try:
|
| 140 |
body = await request.json()
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
# Add a journal entry for the start
|
| 146 |
await _db_insert_journal({
|
| 147 |
"id": str(uuid.uuid4()),
|
| 148 |
"type": "process",
|
| 149 |
-
"content":
|
| 150 |
"created_at": datetime.now(timezone.utc).isoformat()
|
| 151 |
})
|
| 152 |
|
| 153 |
-
# Run pipeline
|
| 154 |
-
|
|
|
|
| 155 |
|
| 156 |
# Save to DB
|
| 157 |
inspection_data = {
|
| 158 |
"id": result.get("id", str(uuid.uuid4())),
|
| 159 |
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 160 |
-
"image_url": image_url,
|
| 161 |
"status": result.get("status", "COMPLETED"),
|
| 162 |
"score": result.get("score", 0),
|
| 163 |
"findings": result.get("findings", []),
|
| 164 |
"agents": result.get("agents", {}),
|
| 165 |
-
"
|
|
|
|
| 166 |
}
|
| 167 |
await _db_insert_inspection(inspection_data)
|
| 168 |
|
|
@@ -177,9 +179,31 @@ async def handle_inspect(request: Request):
|
|
| 177 |
except Exception as e:
|
| 178 |
return JSONResponse({"error": str(e), "traceback": traceback.format_exc()}, status_code=500)
|
| 179 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
@app.get("/api/metrics")
|
| 181 |
async def get_metrics():
|
| 182 |
-
"""Returns system-wide metrics for the dashboard."""
|
| 183 |
inspections = await _db_list_inspections(100)
|
| 184 |
total = len(inspections)
|
| 185 |
if total == 0:
|
|
@@ -198,7 +222,29 @@ async def get_metrics():
|
|
| 198 |
"system_load": "nominal"
|
| 199 |
}
|
| 200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
if __name__ == "__main__":
|
| 202 |
import uvicorn
|
| 203 |
-
import traceback
|
| 204 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
| 67 |
print(f"β οΈ MongoDB unavailable ({e}) β using in-memory storage")
|
| 68 |
|
| 69 |
async def _seed_journal():
|
| 70 |
+
"""Seed the journal with initial milestones."""
|
| 71 |
try:
|
| 72 |
existing = await _db_list_journal(1)
|
| 73 |
if existing: return
|
|
|
|
| 93 |
else:
|
| 94 |
_mem_inspections.append(data)
|
| 95 |
|
| 96 |
+
async def _db_list_inspections(limit=50):
|
| 97 |
if _inspections_col is not None:
|
| 98 |
cursor = _inspections_col.find({}, {"_id": 0}).sort("timestamp", -1).limit(limit)
|
| 99 |
return await cursor.to_list(length=limit)
|
|
|
|
| 114 |
# ββ API ENDPOINTS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 115 |
|
| 116 |
@app.get("/api/health")
|
| 117 |
+
@app.get("/api/")
|
| 118 |
+
@app.get("/")
|
| 119 |
async def health():
|
| 120 |
return {
|
| 121 |
"status": "online",
|
|
|
|
| 125 |
}
|
| 126 |
|
| 127 |
@app.get("/api/inspections")
|
| 128 |
+
async def get_inspections(limit: int = 50):
|
| 129 |
+
items = await _db_list_inspections(limit)
|
| 130 |
return items
|
| 131 |
|
| 132 |
+
@app.post("/api/inspections")
|
| 133 |
+
async def create_inspection(request: Request):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
"""Triggers the full multi-agent QC pipeline."""
|
| 135 |
try:
|
| 136 |
body = await request.json()
|
| 137 |
+
# Frontend uses image_base64, notes, product_spec, source
|
| 138 |
+
image_base64 = body.get("image_base64")
|
| 139 |
+
notes = body.get("notes", "")
|
| 140 |
+
product_spec = body.get("product_spec", "")
|
| 141 |
+
|
| 142 |
+
if not image_base64:
|
| 143 |
+
return JSONResponse({"error": "image_base64 is required"}, status_code=400)
|
| 144 |
|
| 145 |
# Add a journal entry for the start
|
| 146 |
await _db_insert_journal({
|
| 147 |
"id": str(uuid.uuid4()),
|
| 148 |
"type": "process",
|
| 149 |
+
"content": "Starting multimodal inspection via UI upload...",
|
| 150 |
"created_at": datetime.now(timezone.utc).isoformat()
|
| 151 |
})
|
| 152 |
|
| 153 |
+
# Run pipeline (assuming run_pipeline can handle base64 or we convert it)
|
| 154 |
+
# For the hackathon demo, we usually pass the raw data or a temp URL
|
| 155 |
+
result = await run_pipeline(image_base64)
|
| 156 |
|
| 157 |
# Save to DB
|
| 158 |
inspection_data = {
|
| 159 |
"id": result.get("id", str(uuid.uuid4())),
|
| 160 |
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 161 |
+
"image_url": result.get("image_url", "base64_stored"),
|
| 162 |
"status": result.get("status", "COMPLETED"),
|
| 163 |
"score": result.get("score", 0),
|
| 164 |
"findings": result.get("findings", []),
|
| 165 |
"agents": result.get("agents", {}),
|
| 166 |
+
"notes": notes,
|
| 167 |
+
"product_spec": product_spec
|
| 168 |
}
|
| 169 |
await _db_insert_inspection(inspection_data)
|
| 170 |
|
|
|
|
| 179 |
except Exception as e:
|
| 180 |
return JSONResponse({"error": str(e), "traceback": traceback.format_exc()}, status_code=500)
|
| 181 |
|
| 182 |
+
@app.get("/api/journal")
|
| 183 |
+
async def get_journal():
|
| 184 |
+
items = await _db_list_journal()
|
| 185 |
+
return items
|
| 186 |
+
|
| 187 |
+
@app.post("/api/journal")
|
| 188 |
+
async def create_journal(request: Request):
|
| 189 |
+
body = await request.json()
|
| 190 |
+
entry = {
|
| 191 |
+
"id": str(uuid.uuid4()),
|
| 192 |
+
"type": "user",
|
| 193 |
+
"content": body.get("body", ""),
|
| 194 |
+
"title": body.get("title", ""),
|
| 195 |
+
"created_at": datetime.now(timezone.utc).isoformat()
|
| 196 |
+
}
|
| 197 |
+
await _db_insert_journal(entry)
|
| 198 |
+
return entry
|
| 199 |
+
|
| 200 |
+
@app.post("/api/journal/seed")
|
| 201 |
+
async def seed_journal_api():
|
| 202 |
+
await _seed_journal()
|
| 203 |
+
return {"status": "seeded"}
|
| 204 |
+
|
| 205 |
@app.get("/api/metrics")
|
| 206 |
async def get_metrics():
|
|
|
|
| 207 |
inspections = await _db_list_inspections(100)
|
| 208 |
total = len(inspections)
|
| 209 |
if total == 0:
|
|
|
|
| 222 |
"system_load": "nominal"
|
| 223 |
}
|
| 224 |
|
| 225 |
+
@app.get("/api/telemetry")
|
| 226 |
+
async def get_telemetry():
|
| 227 |
+
"""Returns real-time system telemetry (mocked for demo)."""
|
| 228 |
+
import random
|
| 229 |
+
return {
|
| 230 |
+
"gpu_util": random.randint(45, 88),
|
| 231 |
+
"vram_used": random.randint(120, 160), # MI300X 192GB
|
| 232 |
+
"latency_ms": random.randint(120, 450),
|
| 233 |
+
"throughput": round(random.uniform(1.2, 4.5), 1),
|
| 234 |
+
"active_agents": 3,
|
| 235 |
+
"thermal_status": "stable"
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
@app.get("/api/blueprint")
|
| 239 |
+
async def get_blueprint():
|
| 240 |
+
"""Returns the system blueprint metadata."""
|
| 241 |
+
return {
|
| 242 |
+
"version": "1.0.0",
|
| 243 |
+
"architecture": "Multimodal Agentic Pipeline",
|
| 244 |
+
"infrastructure": "AMD MI300X vLLM Cluster",
|
| 245 |
+
"agents": ["Inspector", "Analyst", "Social"]
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
if __name__ == "__main__":
|
| 249 |
import uvicorn
|
|
|
|
| 250 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|