| from __future__ import annotations |
| import asyncio |
| import logging |
| import time |
| import uuid |
| from datetime import datetime |
| from typing import Dict, List |
|
|
| from agents.dedup_agent import DedupAgent |
| from agents.dispatch_agent import DispatchAgent |
| from agents.intake_agent import IntakeAgent |
| from agents.normalization_agent import NormalizationAgent |
| from agents.resource_agent import ResourceAgent |
| from agents.triage_agent import TriageAgent |
| from agents.transcription_agent import TranscriptionAgent |
| from agents.vision_agent import VisionAgent |
| from core.config import get_settings |
| from schemas.crisis_room import CrisisRoomSummary |
| from schemas.incident import Incident, Priority |
| from schemas.report import ReportInput, ReportType, UploadBatch |
| from schemas.signal import NormalizedSignal |
| from services.storage import get_storage |
| from services.vllm_client import get_vllm_client |
| from skills.fetch_amd_metrics import fetch_amd_metrics |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| class Pipeline: |
| def __init__(self) -> None: |
| self.settings = get_settings() |
| self.storage = get_storage() |
| self.vllm = get_vllm_client() |
|
|
| async def process_batch(self, batch: UploadBatch) -> CrisisRoomSummary: |
| t_start = time.monotonic() |
| session_id = batch.session_id |
| scenario_name = batch.scenario_name or "Disaster Response Session" |
|
|
| logger.info("Pipeline: processing batch session=%s (%d reports)", session_id, len(batch.reports)) |
|
|
| await self.storage.save_session(session_id, { |
| "session_id": session_id, |
| "scenario_name": scenario_name, |
| "total_reports": len(batch.reports), |
| "status": "processing", |
| "created_at": datetime.utcnow().isoformat(), |
| }) |
|
|
| |
| intake_agent = IntakeAgent() |
| raw_signals: List[NormalizedSignal] = [] |
| for report in batch.reports: |
| sig = await intake_agent.run(report) |
| raw_signals.append(sig) |
|
|
| |
| transcription_agent = TranscriptionAgent() |
| vision_agent = VisionAgent() |
| normalization_agent = NormalizationAgent(vllm_client=self.vllm) |
|
|
| text_signals: List[NormalizedSignal] = [] |
|
|
| audio_reports = [r for r in batch.reports if r.report_type == ReportType.AUDIO] |
| image_reports = [r for r in batch.reports if r.report_type == ReportType.IMAGE] |
| text_reports = [r for r in batch.reports if r.report_type in (ReportType.TEXT, ReportType.CSV, ReportType.LOCATION)] |
|
|
| async def process_audio(report: ReportInput) -> NormalizedSignal: |
| transcription = await transcription_agent.run(report) |
| return await normalization_agent.run(transcription, report.id, "audio") |
|
|
| async def process_image(report: ReportInput) -> NormalizedSignal: |
| caption = await vision_agent.run(report) |
| return await normalization_agent.run(caption, report.id, "image") |
|
|
| async def process_text(report: ReportInput) -> NormalizedSignal: |
| content = report.content or "" |
| return await normalization_agent.run(content, report.id, report.report_type.value) |
|
|
| tasks = ( |
| [process_audio(r) for r in audio_reports] |
| + [process_image(r) for r in image_reports] |
| + [process_text(r) for r in text_reports] |
| ) |
|
|
| if tasks: |
| results = await asyncio.gather(*tasks, return_exceptions=True) |
| for result in results: |
| if isinstance(result, NormalizedSignal): |
| text_signals.append(result) |
| elif isinstance(result, Exception): |
| logger.warning("Signal processing error: %s", result) |
|
|
| all_signals = text_signals if text_signals else raw_signals |
|
|
| |
| dedup_agent = DedupAgent(threshold=0.75) |
| unique_signals = await dedup_agent.run(all_signals) |
|
|
| |
| for signal in unique_signals: |
| await self.storage.save_signal(signal.id, signal.model_dump(mode="json")) |
|
|
| |
| triage_agent = TriageAgent(session_id=session_id, vllm_client=self.vllm) |
| incidents = await triage_agent.run(unique_signals) |
| for incident in incidents: |
| await self.storage.save_incident(incident.id, incident.model_dump(mode="json")) |
|
|
| |
| resource_agent = ResourceAgent(vllm_client=self.vllm) |
| resources = await resource_agent.run(incidents) |
| for resource in resources: |
| await self.storage.save_resource(resource.id, resource.model_dump(mode="json")) |
|
|
| |
| dispatch_agent = DispatchAgent(vllm_client=self.vllm) |
| dispatch_messages = await dispatch_agent.run(incidents, resources) |
| for msg in dispatch_messages: |
| await self.storage.save_dispatch(msg.id, msg.model_dump(mode="json")) |
|
|
| |
| amd_metrics = await fetch_amd_metrics( |
| self.settings.vllm_base_url, |
| demo_mode=self.settings.demo_mode, |
| ) |
|
|
| |
| incidents_by_priority: Dict[str, int] = {"P0": 0, "P1": 0, "P2": 0, "P3": 0} |
| for inc in incidents: |
| incidents_by_priority[inc.priority.value] += 1 |
|
|
| critical = [i for i in incidents if i.priority in (Priority.P0, Priority.P1)] |
|
|
| processing_time = time.monotonic() - t_start |
|
|
| summary = CrisisRoomSummary( |
| session_id=session_id, |
| scenario_name=scenario_name, |
| total_reports=len(batch.reports), |
| total_signals=len(unique_signals), |
| total_incidents=len(incidents), |
| incidents_by_priority=incidents_by_priority, |
| critical_incidents=critical, |
| resource_recommendations=resources, |
| dispatch_messages=dispatch_messages, |
| amd_metrics=amd_metrics, |
| processing_time_seconds=round(processing_time, 2), |
| created_at=datetime.utcnow(), |
| status="ready", |
| ) |
|
|
| await self.storage.save_session(session_id, { |
| "session_id": session_id, |
| "scenario_name": scenario_name, |
| "total_reports": len(batch.reports), |
| "total_signals": len(unique_signals), |
| "total_incidents": len(incidents), |
| "status": "ready", |
| "processing_time_seconds": round(processing_time, 2), |
| "created_at": datetime.utcnow().isoformat(), |
| }) |
|
|
| logger.info( |
| "Pipeline complete: session=%s incidents=%d time=%.2fs", |
| session_id, |
| len(incidents), |
| processing_time, |
| ) |
| return summary |
|
|