use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct PatientId(String); impl PatientId { pub fn new(value: String) -> Result { let trimmed = value.trim(); if trimmed.is_empty() { return Err("patient_id cannot be empty".to_string()); } Ok(Self(trimmed.to_string())) } pub fn as_str(&self) -> &str { &self.0 } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct ConsentHash(String); impl ConsentHash { pub fn new(value: String) -> Result { let trimmed = value.trim().to_lowercase(); if trimmed.len() < 8 { return Err("consent_hash is too short".to_string()); } Ok(Self(trimmed)) } pub fn as_str(&self) -> &str { &self.0 } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct RecordId(String); impl RecordId { pub fn new() -> Self { Self(format!("rv-{}", Uuid::now_v7())) } pub fn as_str(&self) -> &str { &self.0 } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TriageForm { pub patient_id: String, pub reason: String, pub note: String, pub consent_hash: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TriageRequest { pub patient_id: PatientId, pub reason: String, pub note: String, pub consent_hash: ConsentHash, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TriageRecord { pub record_id: RecordId, pub created_at: DateTime, pub patient_id: PatientId, pub reason: String, pub redacted_note: String, pub pii_map: Vec, pub triage_result: String, pub model_used: String, pub device_used: String, pub redaction_proof: crate::pipeline::proof::RedactionProof, pub cid: String, pub transaction_hash: String, pub consortium_attestations: Vec, pub enrichment: MedicalEnrichment, pub dicom_report: Option, pub degraded_reason: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MedicalEnrichment { pub query: String, pub pubmed_hits: Vec, pub sources: Vec, } impl MedicalEnrichment { pub fn empty() -> Self { Self { query: String::new(), pubmed_hits: Vec::new(), sources: Vec::new(), } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PubMedHit { pub pmid: String, pub title: String, pub url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DicomReport { pub filename: String, pub detected_fields: Vec, pub redacted_preview: String, pub burned_in_text_preview: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConsortiumAttestation { pub hospital: String, pub attestation_hash: String, pub signature_status: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AgentEvent { pub ts: DateTime, pub agent: String, pub phase: String, pub message: String, pub progress: u8, pub record_id: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StatusResponse { pub status: String, pub device: String, pub model: String, pub last_record_id: Option, pub triage_count: usize, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FineTuneResponse { pub status: String, pub job_id: String, pub command: String, pub model: String, pub dataset_cid: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MedicalApiResponse { pub query: String, pub hits: Vec, pub summary: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuditContractInfo { pub contract_address: String, pub record_id: String, pub cid: String, pub proof_commitment: String, pub attestations: Vec, pub transaction_hash: String, }