File size: 3,123 Bytes
cf0fa8f
f2bf8a5
cf0fa8f
 
 
35d82ad
cf0fa8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2bf8a5
 
 
 
cf0fa8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2bd6f
cf0fa8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2bd6f
cf0fa8f
 
 
9e2bd6f
 
cf0fa8f
 
 
 
 
 
 
35d82ad
cf0fa8f
 
 
 
 
 
52308e4
cf0fa8f
 
 
 
 
 
 
9e2bd6f
972f1fa
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from dataclasses import dataclass
from datetime import date, datetime
from pydantic import BaseModel, Field
from typing import List, Literal, Optional, TypedDict, Union


class PatientInfo(BaseModel):
    name: Optional[str] = Field(None, description="Patient's full name")
    age: Optional[int] = Field(None, description="Patient's age in years")
    sex: Optional[str] = Field(None, description="Male/Female/Other")
    medical_record_number: Optional[str] = None

    class Config:
        extra = "forbid"  # 🚨 ensures schema matches OpenAI’s strict rules


class TestResultReferenceRange(BaseModel):
    min: Optional[float] = None
    max: Optional[float] = None


class LabResult(BaseModel):
    test_name: str
    result_value: str
    test_unit: str
    test_reference_range: Optional[TestResultReferenceRange] = None
    test_date: Optional[str] = Field(
        None,
        description="Date of test in ISO format (YYYY-MM-DD). Convert all dates into this format.",
    )
    inferred_range: Literal["low", "normal", "high"]

    class Config:
        extra = "forbid"


class CompositeLabResult(BaseModel):
    section_name: str  # e.g., "CUE - COMPLETE URINE ANALYSIS"
    sub_results: List[LabResult]  # each individual parameter under the section

    class Config:
        extra = "forbid"


class StandardizedReport(BaseModel):
    original_report_file_name: str
    patient_info: PatientInfo
    lab_results: List[
        Union[LabResult, CompositeLabResult]
    ]  # ✅ supports both flat + grouped results
    diagnosis: List[str]
    recommendations: List[str]

    class Config:
        extra = "forbid"


@dataclass
class HealthReport:
    report_file_name_with_path: str
    report_file_name: str
    report_contents: str


@dataclass
class SheamiMilestone:
    step_name: str
    status: str  # e.g., "started", "in_progress", "completed", "failed"
    start_time: Optional[datetime] = None
    end_time: Optional[datetime] = None

    @property
    def time_taken(self) -> Optional[float]:
        """Return time taken in seconds if available."""
        if self.start_time and self.end_time:
            return (self.end_time - self.start_time).total_seconds()
        return None

    @property
    def status_icon(self) -> str:
        if self.status == "started" or self.status == "inprogress":
            return "⏳"
        elif self.status == "completed":
            return "✅"
        elif self.status == "failed":
            return "❌"
        else:
            return self.status


class SheamiState(TypedDict):
    user_email: str
    patient_id: str
    report_id_list: str
    run_id: str
    messages: list[str]
    thread_id: str
    uploaded_reports: List[HealthReport]
    standardized_reports: List[StandardizedReport]
    trends_json: dict
    pdf_path: str
    current_index: int
    process_desc: str
    units_processed: int
    units_total: int
    overall_units_processed: int
    overall_units_total: int
    milestones: list[SheamiMilestone]
    interpretation_html: str

@dataclass
class SheamiUser:
    email : str
    name : str
    picture_url : str