feat: add blueprint tab and pdf report download functionality
Browse files- hf_space/app.py +106 -0
- hf_space/requirements.txt +0 -0
hf_space/app.py
CHANGED
|
@@ -12,6 +12,8 @@ import time
|
|
| 12 |
import uuid
|
| 13 |
import gradio as gr
|
| 14 |
from datetime import datetime, timezone
|
|
|
|
|
|
|
| 15 |
|
| 16 |
# ββ Import the agent pipeline βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 17 |
from agents import run_pipeline, generate_social_post
|
|
@@ -50,11 +52,16 @@ async def inspect(image_base64: str, notes: str = "", product_spec: str = "", so
|
|
| 50 |
_inspections.insert(0, inspection)
|
| 51 |
|
| 52 |
summary = _summarize(inspection)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
return json.dumps({
|
| 54 |
"id": inspection["id"],
|
| 55 |
"created_at": inspection["created_at"],
|
| 56 |
"transcript": transcript,
|
| 57 |
"summary": summary,
|
|
|
|
| 58 |
})
|
| 59 |
|
| 60 |
|
|
@@ -233,6 +240,63 @@ async def _seed_journal():
|
|
| 233 |
})
|
| 234 |
|
| 235 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
# ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 237 |
def _summarize(inspection: dict) -> dict:
|
| 238 |
agents = inspection.get("transcript", {}).get("agents", [])
|
|
@@ -368,6 +432,48 @@ with gr.Blocks(title="ForgeSight β AMD MI300X QC Copilot") as demo:
|
|
| 368 |
return {**h, **m}
|
| 369 |
status_btn.click(fn=check_status, inputs=[], outputs=status_out)
|
| 370 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
|
| 372 |
if __name__ == "__main__":
|
| 373 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 12 |
import uuid
|
| 13 |
import gradio as gr
|
| 14 |
from datetime import datetime, timezone
|
| 15 |
+
import tempfile
|
| 16 |
+
from fpdf import FPDF
|
| 17 |
|
| 18 |
# ββ Import the agent pipeline βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
from agents import run_pipeline, generate_social_post
|
|
|
|
| 52 |
_inspections.insert(0, inspection)
|
| 53 |
|
| 54 |
summary = _summarize(inspection)
|
| 55 |
+
|
| 56 |
+
# Generate a simple text-based report path (optional placeholder)
|
| 57 |
+
report_path = _generate_pdf_report(inspection)
|
| 58 |
+
|
| 59 |
return json.dumps({
|
| 60 |
"id": inspection["id"],
|
| 61 |
"created_at": inspection["created_at"],
|
| 62 |
"transcript": transcript,
|
| 63 |
"summary": summary,
|
| 64 |
+
"report_url": report_path
|
| 65 |
})
|
| 66 |
|
| 67 |
|
|
|
|
| 240 |
})
|
| 241 |
|
| 242 |
|
| 243 |
+
def _generate_pdf_report(inspection: dict) -> str:
|
| 244 |
+
"""Generates a PDF report for an inspection and returns the temporary file path."""
|
| 245 |
+
summary = _summarize(inspection)
|
| 246 |
+
transcript = inspection.get("transcript", {})
|
| 247 |
+
agents = transcript.get("agents", [])
|
| 248 |
+
|
| 249 |
+
pdf = FPDF()
|
| 250 |
+
pdf.add_page()
|
| 251 |
+
|
| 252 |
+
# Header
|
| 253 |
+
pdf.set_font("Arial", 'B', 16)
|
| 254 |
+
pdf.cell(190, 10, "ForgeSight Quality Control Report", ln=True, align='C')
|
| 255 |
+
pdf.set_font("Arial", '', 10)
|
| 256 |
+
pdf.cell(190, 10, f"Generated at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", ln=True, align='C')
|
| 257 |
+
pdf.ln(5)
|
| 258 |
+
|
| 259 |
+
# Summary Section
|
| 260 |
+
pdf.set_font("Arial", 'B', 12)
|
| 261 |
+
pdf.set_fill_color(240, 240, 240)
|
| 262 |
+
pdf.cell(190, 10, "1. EXECUTIVE SUMMARY", ln=True, fill=True)
|
| 263 |
+
pdf.set_font("Arial", '', 10)
|
| 264 |
+
pdf.cell(40, 10, "Inspection ID:", border=0)
|
| 265 |
+
pdf.cell(100, 10, summary["id"], ln=True)
|
| 266 |
+
pdf.cell(40, 10, "Verdict:", border=0)
|
| 267 |
+
pdf.set_font("Arial", 'B', 10)
|
| 268 |
+
pdf.cell(100, 10, summary["verdict"].upper(), ln=True)
|
| 269 |
+
pdf.set_font("Arial", '', 10)
|
| 270 |
+
pdf.cell(40, 10, "Confidence:", border=0)
|
| 271 |
+
pdf.cell(100, 10, f"{summary['confidence']:.2%}", ln=True)
|
| 272 |
+
pdf.cell(40, 10, "Headline:", border=0)
|
| 273 |
+
pdf.multi_cell(150, 10, summary["headline"])
|
| 274 |
+
pdf.ln(5)
|
| 275 |
+
|
| 276 |
+
# Agent Findings
|
| 277 |
+
pdf.set_font("Arial", 'B', 12)
|
| 278 |
+
pdf.cell(190, 10, "2. MULTI-AGENT ANALYSIS", ln=True, fill=True)
|
| 279 |
+
for agent in agents:
|
| 280 |
+
role = agent.get("role", "unknown").capitalize()
|
| 281 |
+
pdf.set_font("Arial", 'B', 10)
|
| 282 |
+
pdf.cell(190, 8, f"Agent: {role}", ln=True)
|
| 283 |
+
pdf.set_font("Arial", '', 9)
|
| 284 |
+
output = agent.get("output", {}).get("raw", "No detailed output.")
|
| 285 |
+
# Sanitize for PDF
|
| 286 |
+
output = output.encode('latin-1', 'replace').decode('latin-1')
|
| 287 |
+
pdf.multi_cell(190, 6, output)
|
| 288 |
+
pdf.ln(2)
|
| 289 |
+
|
| 290 |
+
# Footer
|
| 291 |
+
pdf.ln(10)
|
| 292 |
+
pdf.set_font("Arial", 'I', 8)
|
| 293 |
+
pdf.cell(190, 10, "Powered by AMD Instinct MI300X + ROCm | ForgeSight Multi-Agent Pipeline", ln=True, align='C')
|
| 294 |
+
|
| 295 |
+
temp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
|
| 296 |
+
pdf.output(temp.name)
|
| 297 |
+
return temp.name
|
| 298 |
+
|
| 299 |
+
|
| 300 |
# ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 301 |
def _summarize(inspection: dict) -> dict:
|
| 302 |
agents = inspection.get("transcript", {}).get("agents", [])
|
|
|
|
| 432 |
return {**h, **m}
|
| 433 |
status_btn.click(fn=check_status, inputs=[], outputs=status_out)
|
| 434 |
|
| 435 |
+
with gr.Tab("ποΈ Blueprint"):
|
| 436 |
+
gr.Markdown("### ForgeSight Architecture & Tech Stack")
|
| 437 |
+
bp_view_btn = gr.Button("Fetch Blueprint")
|
| 438 |
+
bp_display = gr.JSON(label="System Specs")
|
| 439 |
+
|
| 440 |
+
async def get_bp():
|
| 441 |
+
return json.loads(await blueprint())
|
| 442 |
+
|
| 443 |
+
bp_view_btn.click(fn=get_bp, inputs=[], outputs=bp_display)
|
| 444 |
+
|
| 445 |
+
gr.Markdown("""
|
| 446 |
+
#### Agent Pipeline Flow
|
| 447 |
+
1. **Inspector**: Vision-reasoning agent identifies defects and assigns a base verdict.
|
| 448 |
+
2. **Diagnostician**: Correlates defects with product specs and historical maintenance data.
|
| 449 |
+
3. **Action Agent**: Determines severity and creates high-priority work orders.
|
| 450 |
+
4. **Reporter**: Generates multi-channel summaries (Social, PDF, Email).
|
| 451 |
+
""")
|
| 452 |
+
|
| 453 |
+
with gr.Tab("π Inspection Report"):
|
| 454 |
+
gr.Markdown("### Download Inspection Report")
|
| 455 |
+
with gr.Row():
|
| 456 |
+
report_select = gr.Dropdown(label="Select Inspection", choices=[])
|
| 457 |
+
report_refresh = gr.Button("π Refresh List")
|
| 458 |
+
|
| 459 |
+
report_download = gr.File(label="Download PDF Report")
|
| 460 |
+
|
| 461 |
+
async def refresh_inspections():
|
| 462 |
+
data = json.loads(await list_inspections())
|
| 463 |
+
choices = [f"{i['id']} - {i['headline'][:30]}..." for i in data["items"]]
|
| 464 |
+
return gr.update(choices=choices)
|
| 465 |
+
|
| 466 |
+
async def fetch_report(selection):
|
| 467 |
+
if not selection: return None
|
| 468 |
+
iid = selection.split(" - ")[0]
|
| 469 |
+
# Find the inspection in our list
|
| 470 |
+
inspection = next((i for i in _inspections if i["id"] == iid), None)
|
| 471 |
+
if not inspection: return None
|
| 472 |
+
return _generate_pdf_report(inspection)
|
| 473 |
+
|
| 474 |
+
report_refresh.click(fn=refresh_inspections, inputs=[], outputs=report_select)
|
| 475 |
+
report_select.change(fn=fetch_report, inputs=[report_select], outputs=report_download)
|
| 476 |
+
|
| 477 |
|
| 478 |
if __name__ == "__main__":
|
| 479 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
hf_space/requirements.txt
CHANGED
|
Binary files a/hf_space/requirements.txt and b/hf_space/requirements.txt differ
|
|
|