File size: 11,511 Bytes
089d665
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
"""Gemeo dataclasses β€” public types.

JSON-serializable, stable across versions. Internal modules can extend
via `extra: dict` payloads.
"""
from __future__ import annotations
from dataclasses import dataclass, field, asdict
from datetime import datetime, timezone
from typing import Optional, Any
import uuid


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


def _new_id() -> str:
    return f"gemeo_{uuid.uuid4().hex[:10]}"


# ─── retrieval / cohort ────────────────────────────────────────────────────

@dataclass
class CohortMember:
    space_id: str
    similarity: float
    shared_phenotypes: list = field(default_factory=list)
    shared_diseases: list = field(default_factory=list)
    confirmed_diagnosis: Optional[str] = None
    confirmed_orpha: Optional[str] = None
    sus_region: Optional[str] = None
    source: str = "registry"  # registry | space | literature


@dataclass
class Cohort:
    members: list = field(default_factory=list)
    method: str = "knn_fused"
    radius: float = 0.0
    n_total_population: int = 0
    centroid_disease: Optional[dict] = None


# ─── reasoning / subgraph ──────────────────────────────────────────────────

@dataclass
class SubgraphNode:
    id: str
    label: str
    name: str
    code: Optional[str] = None
    weight: float = 1.0
    extra: dict = field(default_factory=dict)


@dataclass
class SubgraphEdge:
    source: str
    target: str
    rel: str
    weight: float = 1.0
    evidence: list = field(default_factory=list)


@dataclass
class Subgraph:
    nodes: list = field(default_factory=list)
    edges: list = field(default_factory=list)
    paths: list = field(default_factory=list)
    method: str = "cypher_sparsify"
    target_disease: Optional[str] = None


# ─── prediction / trajectory ───────────────────────────────────────────────

@dataclass
class TrajectoryHorizon:
    months: int
    state: str
    risk_score: float = 0.0
    confidence_low: float = 0.0
    confidence_high: float = 0.0
    expected_phenotypes: list = field(default_factory=list)
    expected_complications: list = field(default_factory=list)


@dataclass
class TrajectorySpec:
    horizons: list = field(default_factory=list)
    model: str = "tgnn_bootstrap"
    natural_history_basis: list = field(default_factory=list)


# ─── risk / survival ───────────────────────────────────────────────────────

@dataclass
class RiskSpec:
    overall_severity: float = 0.0
    progression_risk: float = 0.0
    treatment_urgency: float = 0.0
    survival_curve: list = field(default_factory=list)
    top_complications: list = field(default_factory=list)
    model: str = "rule_based"


# ─── drugs / repurposing ───────────────────────────────────────────────────

@dataclass
class DrugSpec:
    candidates: list = field(default_factory=list)
    model: str = "kg_walks"
    n_evaluated: int = 0


# ─── trials ────────────────────────────────────────────────────────────────

@dataclass
class TrialSpec:
    matches: list = field(default_factory=list)
    model: str = "trialgpt_bootstrap"
    n_searched: int = 0


# ─── active learning ───────────────────────────────────────────────────────

@dataclass
class NextQuestion:
    hpo_id: str
    name: str
    rationale: str
    information_gain: float = 0.0
    discriminates_between: list = field(default_factory=list)
    asks_in_pcdt: bool = False


# ─── SUS grounding ─────────────────────────────────────────────────────────

@dataclass
class SusCheck:
    disease_orpha: Optional[str] = None
    has_pcdt: bool = False
    pcdt_url: Optional[str] = None
    therapy_pcdt_recommended: list = field(default_factory=list)
    therapy_dispensed_in_uf: dict = field(default_factory=dict)
    nearest_centro: Optional[dict] = None
    triagem_neonatal_includes: bool = False
    associations: list = field(default_factory=list)


# ─── viz ───────────────────────────────────────────────────────────────────

@dataclass
class VizData:
    nodes: list = field(default_factory=list)
    links: list = field(default_factory=list)
    center_id: Optional[str] = None
    legend: dict = field(default_factory=dict)


# ─── what-if ───────────────────────────────────────────────────────────────

@dataclass
class WhatIfResult:
    intervention: dict = field(default_factory=dict)
    delta_risk: float = 0.0
    delta_trajectory: list = field(default_factory=list)
    new_risk: Optional[RiskSpec] = None
    new_trajectory: Optional[TrajectorySpec] = None
    rationale: str = ""
    confidence: float = 0.5


# ─── DDI ───────────────────────────────────────────────────────────────────

@dataclass
class DdiPair:
    drug_a: str
    drug_b: str
    rxcui_a: Optional[str] = None
    rxcui_b: Optional[str] = None
    severity: str = "unknown"   # contraindicated | major | moderate | minor | unknown
    mechanism: str = ""
    evidence_level: str = ""
    management: str = ""
    references: list = field(default_factory=list)


@dataclass
class DdiSpec:
    pairs: list = field(default_factory=list)
    n_pairs_evaluated: int = 0
    regimen_risk: str = "none"  # none | minor | moderate | major | contraindicated
    model: str = "kg_walks"


# ─── multi-specialist consult ─────────────────────────────────────────────

@dataclass
class SpecialistOpinion:
    specialty: str
    opinion: str
    confidence: float
    key_concerns: list = field(default_factory=list)
    recommended_next_steps: list = field(default_factory=list)
    red_flags: list = field(default_factory=list)


@dataclass
class ConsultSpec:
    opinions: list = field(default_factory=list)
    synthesis: str = ""
    panel: list = field(default_factory=list)


# ─── pharmacogenomics ─────────────────────────────────────────────────────

@dataclass
class PharmacogenAssessment:
    gene: str
    variant: Optional[str]
    drug: str
    rxcui: Optional[str] = None
    expected_phenotype: str = ""
    recommendation: str = ""
    dose_modification: str = ""
    cpic_level: str = ""           # A | B | C | D | ""
    evidence: str = ""
    confidence: float = 0.5
    source: str = "cpic"          # cpic | pathway


@dataclass
class PharmacogenSpec:
    assessments: list = field(default_factory=list)
    n_pairs: int = 0
    n_actionable: int = 0
    model: str = "cpic_kg"


# ─── family / pedigree ─────────────────────────────────────────────────────

@dataclass
class RelativeRisk:
    relation: str   # sibling | parent | offspring | mother | son | daughter
    recurrence_risk: float
    carrier_screening_recommended: bool = False
    rationale: str = ""


@dataclass
class FamilySpec:
    inheritance_mode: str           # AR | AD | XLR | XLD | MITOCHONDRIAL | DE_NOVO | UNKNOWN
    relatives: list = field(default_factory=list)
    notes: str = ""


# ─── reverse phenotyping ──────────────────────────────────────────────────

@dataclass
class ReversePhenoItem:
    hpo_id: str
    name: str
    expected_frequency: float
    definition: str = ""


@dataclass
class ReversePhenoSpec:
    disease_orpha: Optional[str] = None
    items: list = field(default_factory=list)


# ─── protocol compliance ──────────────────────────────────────────────────

@dataclass
class ComplianceGap:
    category: str       # therapy | exam | referral | off_protocol
    expected: str
    got: Optional[str] = None
    priority: str = "medium"   # low | medium | high
    rationale: str = ""


@dataclass
class ProtocolComplianceSpec:
    disease_orpha: Optional[str] = None
    score: float = 0.0
    gaps: list = field(default_factory=list)
    notes: str = ""


# ─── simulation ───────────────────────────────────────────────────────────

@dataclass
class SimulationOutcome:
    metric: str
    horizon_months: int
    n: int
    mean: float
    p05: float
    p50: float
    p95: float


@dataclass
class SimulationSpec:
    n_runs: int = 0
    intervention: Optional[dict] = None
    horizon_outcomes: list = field(default_factory=list)
    survival_summary: list = field(default_factory=list)
    median_severity: float = 0.0


# ─── the central twin ──────────────────────────────────────────────────────

@dataclass
class GemeoTwin:
    id: str = field(default_factory=_new_id)
    case_id: Optional[str] = None
    patient_id: Optional[str] = None
    created_at: str = field(default_factory=_now)
    updated_at: str = field(default_factory=_now)

    embedding: Optional[list] = None
    embedding_dim: int = 0
    diagnoses: list = field(default_factory=list)

    cohort: Optional[Cohort] = None
    subgraph: Optional[Subgraph] = None
    trajectory: Optional[TrajectorySpec] = None
    risk: Optional[RiskSpec] = None
    drugs: Optional[DrugSpec] = None
    trials: Optional[TrialSpec] = None
    next_questions: list = field(default_factory=list)
    sus_check: Optional[SusCheck] = None
    viz_data: Optional[VizData] = None

    # Phase-2 case-driven additions
    ddi: Optional[DdiSpec] = None
    consult: Optional[ConsultSpec] = None
    pharmacogen: Optional[PharmacogenSpec] = None
    family: Optional[FamilySpec] = None
    reverse_pheno: Optional[ReversePhenoSpec] = None
    protocol_compliance: Optional[ProtocolComplianceSpec] = None

    # Snapshot state
    snapshot_versions: list = field(default_factory=list)
    n_phenotypes: int = 0
    n_genes: int = 0
    n_labs: int = 0

    extra: dict = field(default_factory=dict)

    def to_dict(self) -> dict:
        return asdict(self)