|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import json |
| 4 | +from pathlib import Path |
| 5 | + |
| 6 | +import streamlit as st |
| 7 | + |
| 8 | +from fireform.pipeline import run_pipeline |
| 9 | + |
| 10 | +st.set_page_config( |
| 11 | + page_title="FireForm Prototype", |
| 12 | + page_icon="🔥", |
| 13 | + layout="wide", |
| 14 | +) |
| 15 | +st.title("FireForm: Report Once, File Everywhere") |
| 16 | +st.caption( |
| 17 | + "Local pipeline demo: text/voice -> JSON extraction -> " |
| 18 | + "validation -> multi-agency PDF fill" |
| 19 | +) |
| 20 | + |
| 21 | +default_sample = "" |
| 22 | +sample_path = Path("samples/sample_incident.txt") |
| 23 | +if sample_path.exists(): |
| 24 | + default_sample = sample_path.read_text(encoding="utf-8") |
| 25 | + |
| 26 | +text_input = st.text_area( |
| 27 | + "Incident narrative", |
| 28 | + value=default_sample, |
| 29 | + height=180, |
| 30 | +) |
| 31 | +audio_path = st.text_input("Audio path (optional)", value="") |
| 32 | +model_name = st.text_input("Ollama model", value="llama3.1") |
| 33 | +max_retries = st.slider( |
| 34 | + "Max repair retries", |
| 35 | + min_value=0, |
| 36 | + max_value=5, |
| 37 | + value=2, |
| 38 | +) |
| 39 | +save_artifacts = st.checkbox("Save run artifacts", value=True) |
| 40 | +run_button = st.button("Run Pipeline", type="primary") |
| 41 | + |
| 42 | +if run_button: |
| 43 | + template_specs = [ |
| 44 | + { |
| 45 | + "template_path": "templates/fire_report_template.pdf", |
| 46 | + "mapping_path": "agencies/fema_ics214/field_mapping.json" if Path("agencies/fema_ics214/field_mapping.json").exists() else "schemas/template_maps/fire_department.json", |
| 47 | + "output_name": "fire_department_report.pdf", |
| 48 | + }, |
| 49 | + { |
| 50 | + "template_path": "templates/ems_report_template.pdf", |
| 51 | + "mapping_path": "agencies/ems_report/field_mapping.json" if Path("agencies/ems_report/field_mapping.json").exists() else "schemas/template_maps/ems_department.json", |
| 52 | + "output_name": "ems_department_report.pdf", |
| 53 | + }, |
| 54 | + ] |
| 55 | + |
| 56 | + try: |
| 57 | + artifacts = run_pipeline( |
| 58 | + text_input=text_input if text_input.strip() else None, |
| 59 | + audio_path=audio_path.strip() or None, |
| 60 | + schema_path="schemas/incident_schema.json", |
| 61 | + template_specs=template_specs, |
| 62 | + output_dir="outputs", |
| 63 | + model=model_name.strip() or "llama3.1", |
| 64 | + max_retries=max_retries, |
| 65 | + save_artifacts=save_artifacts, |
| 66 | + ) |
| 67 | + |
| 68 | + st.success("Pipeline complete") |
| 69 | + |
| 70 | + left, mid, right = st.columns(3) |
| 71 | + left.metric("Run ID", artifacts.run_id) |
| 72 | + mid.metric("Retries Used", str(artifacts.retries_used)) |
| 73 | + right.metric( |
| 74 | + "Total Seconds", |
| 75 | + f"{artifacts.stage_durations.get('total_seconds', 0.0):.3f}", |
| 76 | + ) |
| 77 | + |
| 78 | + if artifacts.stage_durations: |
| 79 | + st.subheader("Stage Durations (seconds)") |
| 80 | + st.json(artifacts.stage_durations) |
| 81 | + |
| 82 | + st.subheader("Extracted JSON") |
| 83 | + st.code( |
| 84 | + json.dumps(artifacts.extracted_json, indent=2), |
| 85 | + language="json", |
| 86 | + ) |
| 87 | + |
| 88 | + if artifacts.template_results: |
| 89 | + st.subheader("Template Results") |
| 90 | + table_rows = [ |
| 91 | + { |
| 92 | + "template": item.template_path, |
| 93 | + "output": item.output_name, |
| 94 | + "status": item.status, |
| 95 | + "fields_mapped": item.fields_mapped, |
| 96 | + "error": item.error, |
| 97 | + } |
| 98 | + for item in artifacts.template_results |
| 99 | + ] |
| 100 | + st.dataframe(table_rows, use_container_width=True) |
| 101 | + |
| 102 | + st.subheader("Generated files") |
| 103 | + for index, file_path in enumerate(artifacts.output_files): |
| 104 | + path = Path(file_path) |
| 105 | + st.write(str(path)) |
| 106 | + if path.exists(): |
| 107 | + st.download_button( |
| 108 | + label=f"Download {path.name}", |
| 109 | + data=path.read_bytes(), |
| 110 | + file_name=path.name, |
| 111 | + mime="application/pdf", |
| 112 | + key=f"pdf-download-{index}", |
| 113 | + ) |
| 114 | + |
| 115 | + if artifacts.artifact_files: |
| 116 | + st.subheader("Run Artifacts") |
| 117 | + for index, artifact_path in enumerate(artifacts.artifact_files): |
| 118 | + path = Path(artifact_path) |
| 119 | + st.write(str(path)) |
| 120 | + if path.exists(): |
| 121 | + st.download_button( |
| 122 | + label=f"Download {path.name}", |
| 123 | + data=path.read_bytes(), |
| 124 | + file_name=path.name, |
| 125 | + mime="application/json", |
| 126 | + key=f"artifact-download-{index}", |
| 127 | + ) |
| 128 | + except Exception as exc: |
| 129 | + st.error(f"Pipeline failed: {exc}") |
0 commit comments