feat: complete production hardening implementation, specialty canonicalization, google-genai migration, voice reliability, sqlite concurrency, and test suite consolidation

This commit is contained in:
2026-09-07 12:57:54 +05:30
parent c23d979338
commit 92fa71ae4a
99 changed files with 5167 additions and 3550 deletions
@@ -0,0 +1,35 @@
"""
Domain models package.
"""
from app.domain.models.patient_state import (
SymptomItem,
MedicationItem,
AllergyItem,
MedicalHistoryItem,
SurgicalHistoryItem,
PreviousEpisodeItem,
FamilyHistoryItem,
FieldContainer,
PatientState,
)
from app.domain.models.extraction import ExtractionResult, QuestionIntent
from app.domain.models.contracts import ResponseContext, ResponseResult, ResponseType, FactConfidence
__all__ = [
"SymptomItem",
"MedicationItem",
"AllergyItem",
"MedicalHistoryItem",
"SurgicalHistoryItem",
"PreviousEpisodeItem",
"FamilyHistoryItem",
"FieldContainer",
"PatientState",
"ExtractionResult",
"QuestionIntent",
"ResponseContext",
"ResponseResult",
"ResponseType",
"FactConfidence",
]
@@ -0,0 +1,67 @@
"""
Response contracts, enums, and data models (Rules 102, 103, 104, 105, 116):
- ResponseType Enum
- QuestionIntent Enum
- ResponseContext Input Contract
- ResponseResult Output Contract
- FactConfidence Model
"""
from enum import Enum
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field
class ResponseType(str, Enum):
GREETING = "greeting"
ACKNOWLEDGEMENT = "acknowledgement"
FOLLOW_UP = "follow_up"
CLARIFICATION = "clarification"
REFUSAL = "refusal"
UNKNOWN = "unknown"
SAFETY = "safety"
CRISIS = "crisis"
COMPLETION = "completion"
class QuestionIntent(str, Enum):
SYMPTOM_NAME = "symptom_name"
SYMPTOM_DURATION = "symptom_duration"
SYMPTOM_SEVERITY = "symptom_severity"
SYMPTOM_LOCATION = "symptom_location"
SYMPTOM_ONSET = "symptom_onset"
SYMPTOM_FREQUENCY = "symptom_frequency"
SYMPTOM_TRIGGER = "symptom_trigger"
ASSOCIATED_SYMPTOMS = "associated_symptoms"
MEDICAL_HISTORY = "medical_history"
MEDICATIONS = "medications"
ALLERGIES = "allergies"
FAMILY_HISTORY = "family_history"
SURGICAL_HISTORY = "surgical_history"
class ResponseContext(BaseModel):
language: str = "en"
patient_message: str
intent: str = "fact_extraction"
primary_symptom: Optional[str] = None
missing_information: Optional[str] = None
selected_question: Optional[str] = None
question_intent: Optional[QuestionIntent] = None
safety_status: str = "normal"
onboarding_completed: bool = False
current_state: Optional[Dict[str, Any]] = None
class ResponseResult(BaseModel):
response: str
language: str = "en"
question_intent: Optional[str] = None
response_type: ResponseType = ResponseType.FOLLOW_UP
class FactConfidence(BaseModel):
source: str = "patient"
method: str = "explicit"
confidence: float = 1.0
@@ -0,0 +1,28 @@
"""
Extraction result and question intent domain models.
"""
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class ExtractedFact(BaseModel):
category: str # 'symptom', 'medication', 'allergy', 'medical_history', 'family_history', etc.
data: Dict[str, Any]
class ExtractionResult(BaseModel):
raw_message: str
normalized_message: str
detected_language: str = "en"
updates: Dict[str, Any] = Field(default_factory=dict)
is_emergency: bool = False
is_crisis: bool = False
emergency_reason: Optional[str] = None
class QuestionIntent(BaseModel):
intent_code: str # e.g., 'reason_for_visit', 'symptom_duration', 'medical_history'
field: str
attribute: Optional[str] = None
target_symptom: Optional[str] = None
@@ -0,0 +1,87 @@
"""
Strongly typed domain models for Patient Onboarding state management.
"""
from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel, Field
class SymptomItem(BaseModel):
name: str
canonical_name: Optional[str] = None
duration: Optional[str] = None
severity: Optional[str] = None
location: Optional[str] = None
frequency: Optional[str] = None
onset: Optional[str] = None
pattern: Optional[str] = None
trigger: Optional[str] = None
relieving_factor: Optional[str] = None
associated_findings: Optional[str] = None
class MedicationItem(BaseModel):
name: str
canonical_name: Optional[str] = None
dosage: Optional[str] = None
frequency: Optional[str] = None
duration: Optional[str] = None
purpose: Optional[str] = None
class AllergyItem(BaseModel):
name: str
canonical_name: Optional[str] = None
type: Optional[str] = None # e.g., 'drug', 'food', 'environment'
reaction: Optional[str] = None # e.g., 'rash', 'swelling'
class MedicalHistoryItem(BaseModel):
condition: str
canonical_name: Optional[str] = None
duration: Optional[str] = None
status: Optional[str] = "active"
details: Optional[str] = None
class SurgicalHistoryItem(BaseModel):
procedure: str
body_site: Optional[str] = None
approximate_date: Optional[str] = None
reason: Optional[str] = None
class PreviousEpisodeItem(BaseModel):
description: str
time: Optional[str] = None
outcome: Optional[str] = None
class FamilyHistoryItem(BaseModel):
relation: str # e.g., 'father', 'mother', 'sibling'
condition: str
canonical_name: Optional[str] = None
class FieldContainer(BaseModel):
status: str = "not_collected" # 'not_collected' | 'reported' | 'confirmed_none' | 'unknown' | 'declined'
items: List[Dict[str, Any]] = Field(default_factory=list)
class SpecialtyConfidence(BaseModel):
code: str
name: str
confidence: float = 1.0
evidence: List[str] = Field(default_factory=list)
class PatientState(BaseModel):
reason_for_visit: Optional[str] = None
symptoms: List[SymptomItem] = Field(default_factory=list)
medical_history: Union[FieldContainer, List[MedicalHistoryItem], Dict[str, Any]] = Field(default_factory=list)
medications: Union[FieldContainer, List[MedicationItem], Dict[str, Any]] = Field(default_factory=list)
allergies: Union[FieldContainer, List[AllergyItem], Dict[str, Any]] = Field(default_factory=list)
surgical_history: List[SurgicalHistoryItem] = Field(default_factory=list)
previous_episodes: List[PreviousEpisodeItem] = Field(default_factory=list)
family_history: List[FamilyHistoryItem] = Field(default_factory=list)
additional_information: List[Dict[str, Any]] = Field(default_factory=list)