SmokeScan / schemas /output.py
KinetoLabs's picture
Initial commit: FDAM AI Pipeline v4.0.1
88bdcff
"""Pydantic output models for FDAM AI Pipeline.
Contains vision analysis results, calculation outputs, and final assessment output.
"""
from typing import Optional
from pydantic import BaseModel, Field
from .input import (
AssessmentInput,
BoundingBox,
ConditionLevel,
MaterialCategory,
MaterialType,
Priority,
SampleType,
ZoneType,
)
# --- Vision Analysis Output ---
class ZoneAnalysis(BaseModel):
"""Zone classification from vision analysis."""
classification: ZoneType
confidence: float = Field(..., ge=0, le=1)
reasoning: str
class ConditionAnalysis(BaseModel):
"""Condition assessment from vision analysis."""
level: ConditionLevel
confidence: float = Field(..., ge=0, le=1)
reasoning: str
class DetectedMaterial(BaseModel):
"""Material detected in image by vision model."""
type: MaterialType
category: MaterialCategory
confidence: float = Field(..., ge=0, le=1)
location_description: Optional[str] = None
bounding_box: Optional[BoundingBox] = None
class CombustionIndicators(BaseModel):
"""Combustion particle indicators from vision analysis."""
soot_visible: bool = False
soot_pattern: Optional[str] = None
char_visible: bool = False
char_description: Optional[str] = None
ash_visible: bool = False
ash_description: Optional[str] = None
class SamplingRecommendation(BaseModel):
"""Recommended sampling location from vision analysis."""
description: str
sample_type: SampleType
priority: Priority
class VisionAnalysisResult(BaseModel):
"""Complete vision analysis result for a single image.
Matches the VISION_OUTPUT_SCHEMA from the technical spec.
"""
zone: ZoneAnalysis
condition: ConditionAnalysis
materials: list[DetectedMaterial] = Field(default_factory=list)
combustion_indicators: CombustionIndicators
structural_concerns: list[str] = Field(default_factory=list)
access_issues: list[str] = Field(default_factory=list)
recommended_sampling_locations: list[SamplingRecommendation] = Field(default_factory=list)
flags_for_review: list[str] = Field(default_factory=list)
# --- Calculation Results ---
class RoomAreaSummary(BaseModel):
"""Area summary for a single room."""
floor_area: float
surface_area: float
volume: float
class SurfaceAreas(BaseModel):
"""Surface area calculations by various groupings."""
by_type: dict[str, float] = Field(default_factory=dict)
by_disposition: dict[str, float] = Field(default_factory=dict)
by_zone: dict[str, float] = Field(default_factory=dict)
by_room: dict[str, RoomAreaSummary] = Field(default_factory=dict)
total_floor_sf: float = 0
total_surface_sf: float = 0
total_volume_cf: float = 0
class AirFiltration(BaseModel):
"""Air filtration calculation results per NADCA ACR 2021."""
total_volume_cf: float
required_ach: int = 4
unit_cfm: int = 2000
units_required: int
calculation: str
standard_reference: str = "NADCA ACR 2021, Section 3.6"
class SampleDensity(BaseModel):
"""Sample density recommendations per FDAM 2.3."""
total_sf: float
size_category: str
surface_types_count: int
surface_types: list[str] = Field(default_factory=list)
tape_lifts_per_type: str
surface_wipes_per_type: str
recommended_tape_lifts: int
recommended_surface_wipes: int
ceiling_deck_note: Optional[str] = None
control_samples_recommended: bool = True
control_sample_note: str = "Control samples from unaffected areas recommended for baseline comparison"
class LaborEstimate(BaseModel):
"""Labor hour estimates by task."""
hepa_vacuum: float = 0
wet_wipe: float = 0
dry_sponge: float = 0
power_wash: float = 0
scrubber: float = 0
removal: float = 0
hvac_cleaning: float = 0
total_hours: float = 0
class EquipmentRequirements(BaseModel):
"""Equipment requirements for the project."""
air_scrubbers: int = 0
hepa_vacuums: int = 0
negative_air_machines: int = 0
dehumidifiers: int = 0
notes: list[str] = Field(default_factory=list)
class RegulatoryFlag(BaseModel):
"""Regulatory flag for potential hazards."""
flag_type: str
description: str
recommendation: str
reference: str
class RegulatoryFlags(BaseModel):
"""Regulatory flags based on construction era and facility type."""
lead_paint_flag: Optional[RegulatoryFlag] = None
acm_flag: Optional[RegulatoryFlag] = None
other_flags: list[RegulatoryFlag] = Field(default_factory=list)
class CalculationResults(BaseModel):
"""All calculation results from FDAM logic engine."""
surface_areas: SurfaceAreas
air_filtration: AirFiltration
sample_density: SampleDensity
labor_estimate: LaborEstimate
equipment: EquipmentRequirements
regulatory_flags: RegulatoryFlags
# --- Document Output ---
class GeneratedDocuments(BaseModel):
"""Generated document outputs."""
cleaning_specification_md: str = Field(..., description="Cleaning Specification / SOW in Markdown")
sampling_plan_md: Optional[str] = Field(None, description="Sampling plan recommendations in Markdown")
confidence_report_md: Optional[str] = Field(None, description="Confidence report in Markdown")
# --- Confidence Report ---
class FlaggedItem(BaseModel):
"""Item flagged for professional review."""
type: str
room: Optional[str] = None
surface: Optional[str] = None
image_id: Optional[str] = None
confidence: Optional[float] = None
recommendation: str
class ConfidenceReport(BaseModel):
"""Confidence report for assessment."""
flagged_items: list[FlaggedItem] = Field(default_factory=list)
overall_confidence: float = Field(..., ge=0, le=1)
review_required: bool = False
# --- Complete Assessment Output ---
class AssessmentOutput(BaseModel):
"""Complete output from FDAM AI assessment pipeline."""
# Original input (with AI-enriched fields)
input: AssessmentInput
# Vision analysis results (by image ID)
vision_results: dict[str, VisionAnalysisResult] = Field(default_factory=dict)
# Calculation results
calculations: CalculationResults
# Generated documents
documents: GeneratedDocuments
# Confidence report
confidence_report: ConfidenceReport
# Processing metadata
processing_time_seconds: Optional[float] = None
model_versions: dict[str, str] = Field(default_factory=dict)