feat: integrate MongoDB Atlas persistence and update backend config
Browse files- backend/app.py +71 -13
backend/app.py
CHANGED
|
@@ -16,10 +16,55 @@ from datetime import datetime, timezone
|
|
| 16 |
# ββ Import the agent pipeline βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 17 |
from agents import run_pipeline, generate_social_post
|
| 18 |
|
| 19 |
-
# ββ
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
def _now_iso() -> str:
|
|
@@ -47,7 +92,7 @@ async def inspect(image_base64: str, notes: str = "", product_spec: str = "", so
|
|
| 47 |
"source": source or "upload",
|
| 48 |
"transcript": transcript,
|
| 49 |
}
|
| 50 |
-
|
| 51 |
|
| 52 |
summary = _summarize(inspection)
|
| 53 |
return json.dumps({
|
|
@@ -60,18 +105,20 @@ async def inspect(image_base64: str, notes: str = "", product_spec: str = "", so
|
|
| 60 |
|
| 61 |
# ββ 2. List inspections βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 62 |
async def list_inspections(limit: int = 50):
|
| 63 |
-
|
|
|
|
| 64 |
return json.dumps({"items": items, "total": len(items)})
|
| 65 |
|
| 66 |
|
| 67 |
# ββ 3. Metrics βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
async def metrics():
|
| 69 |
-
|
|
|
|
| 70 |
verdict_counts = {"pass": 0, "warn": 0, "fail": 0}
|
| 71 |
defect_type_counts = {}
|
| 72 |
confidences = []
|
| 73 |
|
| 74 |
-
for doc in
|
| 75 |
summary = _summarize(doc)
|
| 76 |
v = summary["verdict"] if summary["verdict"] in verdict_counts else "warn"
|
| 77 |
verdict_counts[v] += 1
|
|
@@ -175,10 +222,12 @@ async def blueprint():
|
|
| 175 |
|
| 176 |
# ββ 6. Journal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 177 |
async def journal_list():
|
|
|
|
| 178 |
# Auto-seed if empty
|
| 179 |
-
if not
|
| 180 |
await _seed_journal()
|
| 181 |
-
|
|
|
|
| 182 |
|
| 183 |
|
| 184 |
async def journal_create(title: str, body: str, tags: str = ""):
|
|
@@ -197,11 +246,14 @@ async def journal_create(title: str, body: str, tags: str = ""):
|
|
| 197 |
"x_post": social.get("x_post", ""),
|
| 198 |
"linkedin_post": social.get("linkedin_post", ""),
|
| 199 |
}
|
| 200 |
-
|
| 201 |
return json.dumps(entry)
|
| 202 |
|
| 203 |
|
| 204 |
async def _seed_journal():
|
|
|
|
|
|
|
|
|
|
| 205 |
seeds = [
|
| 206 |
{
|
| 207 |
"title": "Kickoff: ForgeSight on AMD Developer Cloud",
|
|
@@ -224,13 +276,14 @@ async def _seed_journal():
|
|
| 224 |
social = await generate_social_post(s["title"], s["body"])
|
| 225 |
except Exception:
|
| 226 |
social = {"x_post": "", "linkedin_post": ""}
|
| 227 |
-
|
| 228 |
"id": str(uuid.uuid4()),
|
| 229 |
"created_at": _now_iso(),
|
| 230 |
**s,
|
| 231 |
"x_post": social.get("x_post", ""),
|
| 232 |
"linkedin_post": social.get("linkedin_post", ""),
|
| 233 |
-
}
|
|
|
|
| 234 |
|
| 235 |
|
| 236 |
# ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -370,4 +423,9 @@ with gr.Blocks(title="ForgeSight β AMD MI300X QC Copilot") as demo:
|
|
| 370 |
|
| 371 |
|
| 372 |
if __name__ == "__main__":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 16 |
# ββ Import the agent pipeline βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 17 |
from agents import run_pipeline, generate_social_post
|
| 18 |
|
| 19 |
+
# ββ MONGODB PERSISTENCE (optional, falls back to in-memory) ββββββββββββββββββ
|
| 20 |
+
MONGO_URL = os.getenv("MONGO_URL", "")
|
| 21 |
+
_db = None
|
| 22 |
+
_inspections_col = None
|
| 23 |
+
_journal_col = None
|
| 24 |
+
|
| 25 |
+
# In-memory fallback
|
| 26 |
+
_mem_inspections: list = []
|
| 27 |
+
_mem_journal: list = []
|
| 28 |
+
|
| 29 |
+
async def _init_db():
|
| 30 |
+
"""Attempt to connect to MongoDB; silently fall back to in-memory if unavailable."""
|
| 31 |
+
global _db, _inspections_col, _journal_col
|
| 32 |
+
if not MONGO_URL:
|
| 33 |
+
return
|
| 34 |
+
try:
|
| 35 |
+
from motor.motor_asyncio import AsyncIOMotorClient
|
| 36 |
+
client = AsyncIOMotorClient(MONGO_URL, serverSelectionTimeoutMS=4000)
|
| 37 |
+
await client.admin.command("ping")
|
| 38 |
+
_db = client["forgesight"]
|
| 39 |
+
_inspections_col = _db["inspections"]
|
| 40 |
+
_journal_col = _db["journal"]
|
| 41 |
+
print("β
MongoDB connected β persistence enabled")
|
| 42 |
+
except Exception as e:
|
| 43 |
+
print(f"β οΈ MongoDB unavailable ({e}) β using in-memory storage")
|
| 44 |
+
|
| 45 |
+
async def _db_insert_inspection(doc: dict):
|
| 46 |
+
if _inspections_col is not None:
|
| 47 |
+
await _inspections_col.insert_one({**doc, "_id": doc["id"]})
|
| 48 |
+
else:
|
| 49 |
+
_mem_inspections.insert(0, doc)
|
| 50 |
+
|
| 51 |
+
async def _db_list_inspections(limit=50) -> list:
|
| 52 |
+
if _inspections_col is not None:
|
| 53 |
+
cursor = _inspections_col.find({}, {"_id": 0}).sort("created_at", -1).limit(limit)
|
| 54 |
+
return await cursor.to_list(length=limit)
|
| 55 |
+
return _mem_inspections[:limit]
|
| 56 |
+
|
| 57 |
+
async def _db_insert_journal(doc: dict):
|
| 58 |
+
if _journal_col is not None:
|
| 59 |
+
await _journal_col.insert_one({**doc, "_id": doc["id"]})
|
| 60 |
+
else:
|
| 61 |
+
_mem_journal.insert(0, doc)
|
| 62 |
+
|
| 63 |
+
async def _db_list_journal(limit=50) -> list:
|
| 64 |
+
if _journal_col is not None:
|
| 65 |
+
cursor = _journal_col.find({}, {"_id": 0}).sort("created_at", -1).limit(limit)
|
| 66 |
+
return await cursor.to_list(length=limit)
|
| 67 |
+
return _mem_journal[:limit]
|
| 68 |
|
| 69 |
|
| 70 |
def _now_iso() -> str:
|
|
|
|
| 92 |
"source": source or "upload",
|
| 93 |
"transcript": transcript,
|
| 94 |
}
|
| 95 |
+
await _db_insert_inspection(inspection)
|
| 96 |
|
| 97 |
summary = _summarize(inspection)
|
| 98 |
return json.dumps({
|
|
|
|
| 105 |
|
| 106 |
# ββ 2. List inspections βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 107 |
async def list_inspections(limit: int = 50):
|
| 108 |
+
docs = await _db_list_inspections(limit)
|
| 109 |
+
items = [_summarize(doc) for doc in docs]
|
| 110 |
return json.dumps({"items": items, "total": len(items)})
|
| 111 |
|
| 112 |
|
| 113 |
# ββ 3. Metrics βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 114 |
async def metrics():
|
| 115 |
+
docs = await _db_list_inspections(500)
|
| 116 |
+
total = len(docs)
|
| 117 |
verdict_counts = {"pass": 0, "warn": 0, "fail": 0}
|
| 118 |
defect_type_counts = {}
|
| 119 |
confidences = []
|
| 120 |
|
| 121 |
+
for doc in docs:
|
| 122 |
summary = _summarize(doc)
|
| 123 |
v = summary["verdict"] if summary["verdict"] in verdict_counts else "warn"
|
| 124 |
verdict_counts[v] += 1
|
|
|
|
| 222 |
|
| 223 |
# ββ 6. Journal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 224 |
async def journal_list():
|
| 225 |
+
docs = await _db_list_journal(50)
|
| 226 |
# Auto-seed if empty
|
| 227 |
+
if not docs:
|
| 228 |
await _seed_journal()
|
| 229 |
+
docs = await _db_list_journal(50)
|
| 230 |
+
return json.dumps({"items": docs, "total": len(docs)})
|
| 231 |
|
| 232 |
|
| 233 |
async def journal_create(title: str, body: str, tags: str = ""):
|
|
|
|
| 246 |
"x_post": social.get("x_post", ""),
|
| 247 |
"linkedin_post": social.get("linkedin_post", ""),
|
| 248 |
}
|
| 249 |
+
await _db_insert_journal(entry)
|
| 250 |
return json.dumps(entry)
|
| 251 |
|
| 252 |
|
| 253 |
async def _seed_journal():
|
| 254 |
+
existing = await _db_list_journal(1)
|
| 255 |
+
if existing:
|
| 256 |
+
return
|
| 257 |
seeds = [
|
| 258 |
{
|
| 259 |
"title": "Kickoff: ForgeSight on AMD Developer Cloud",
|
|
|
|
| 276 |
social = await generate_social_post(s["title"], s["body"])
|
| 277 |
except Exception:
|
| 278 |
social = {"x_post": "", "linkedin_post": ""}
|
| 279 |
+
entry = {
|
| 280 |
"id": str(uuid.uuid4()),
|
| 281 |
"created_at": _now_iso(),
|
| 282 |
**s,
|
| 283 |
"x_post": social.get("x_post", ""),
|
| 284 |
"linkedin_post": social.get("linkedin_post", ""),
|
| 285 |
+
}
|
| 286 |
+
await _db_insert_journal(entry)
|
| 287 |
|
| 288 |
|
| 289 |
# ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 423 |
|
| 424 |
|
| 425 |
if __name__ == "__main__":
|
| 426 |
+
import asyncio
|
| 427 |
+
# Initialize DB before launching
|
| 428 |
+
loop = asyncio.get_event_loop()
|
| 429 |
+
loop.run_until_complete(_init_db())
|
| 430 |
+
|
| 431 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|