| from __future__ import annotations |
|
|
| from typing import Any, Literal |
|
|
| from pydantic import BaseModel, ConfigDict, Field, field_validator |
|
|
| from src.schemas.detection import LocalizedFinding |
| from src.schemas.segmentation import MaskResult |
|
|
|
|
| class StructuredReport(BaseModel): |
| """Seven-field structured radiology report produced by Stage 3.""" |
|
|
| study_type: str = "" |
| summary: str = "" |
| main_findings: list[str] = Field(default_factory=list) |
| detail_findings: list[str] = Field(default_factory=list) |
| impression: str = "" |
| recommendations: str = "" |
| additional_informations: str = "" |
|
|
| @field_validator("main_findings", "detail_findings", mode="before") |
| @classmethod |
| def coerce_to_list(cls, v: Any) -> list[str]: |
| if isinstance(v, str): |
| return [v] if v.strip() else [] |
| if isinstance(v, list): |
| return [str(item) for item in v if str(item).strip()] |
| return [] |
|
|
|
|
| class ReportRequest(BaseModel): |
| """All inputs needed to generate a radiology report (Stage 3).""" |
|
|
| model_config = ConfigDict(arbitrary_types_allowed=True) |
|
|
| case_id: str | None = None |
| input_images: list[str] |
| images: list[Any] = Field(default_factory=list, exclude=True) |
| findings: list[LocalizedFinding] = Field(default_factory=list) |
| masks: list[MaskResult] = Field(default_factory=list) |
|
|
|
|
| class ReportResult(BaseModel): |
| case_id: str | None = None |
| report_text: str |
| structured: StructuredReport | None = None |
| status: Literal["success", "failed"] |
| error: str | None = None |
|
|
|
|
| class PipelineOutput(BaseModel): |
| """Final output of the three-stage CXR pipeline.""" |
|
|
| model_config = ConfigDict(arbitrary_types_allowed=True) |
|
|
| detection: Any |
| masks: list[MaskResult] = Field(default_factory=list) |
| report: ReportResult | None = None |
| processed_images: list[Any] = Field(default_factory=list, exclude=True) |
|
|