Skip to content

14 — Sequence Diagrams: Full MVP Journey

Aisha → Frontend → Clerk Auth → POST /api/v1/patients → PostgreSQL (patient record)
  → event: patient_registered → POST /api/v1/consent (4 purposes) → PostgreSQL (consent records)
  → event: consent_granted → Intake Agent initialized → SSE: welcome message

Flow 2: Document Upload & Auto-Chain

Aisha uploads X-ray report
  → Frontend: POST /uploads/presign → { upload_url, doc_id, storage_key }
  → Frontend: PUT upload_url (direct to R2, bypasses API)
  → Frontend: POST /uploads/confirm → document_reference created (status: uploaded)
  → event: document_uploaded → QStash dispatches OCR job
  → SSE: status = parsing
  → PyMuPDF extracts text → status = parsed
  → event: document_parsed

AUTO-CHAIN:
  → Clinical Context Agent triggers
  → extract_clinical_entities (Claude Haiku): "Primary osteoarthritis, right knee"
  → map_to_medical_codes: M17.11 (confidence: 0.95), Kellgren-Lawrence Grade 4
  → generate_fhir_resources: FHIR Condition, FHIR Procedure (27447)
  → store_resources: validated via fhir.resources → PostgreSQL
  → event: fhir_resource_created

EHR UPDATE:
  → Neo4j: Patient→DIAGNOSED_WITH→Condition(M17.11)→INDICATED_FOR→Procedure(27447)
  → Qdrant: patient clinical embedding re-indexed
  → EHR Builder: patient record updated
  → intake_progress recalculated (e.g., 0.35 → 0.55)
  → SSE: ehr_update + intake_progress events
  → Left panel refreshes with new condition

Flow 3: Chat Data Extraction

Aisha types: "I also have Type 2 diabetes and mild hypertension"
  → POST /api/v1/patients/{id}/chat
  → Orchestrator: classify_intent → "intake_update"
  → Intake Agent: collect_information node
  → Extracts: E11 (diabetes, confidence 0.88), I10 (hypertension, confidence 0.85)
  → FHIR Condition resources created and validated
  → event: clinical_entities_extracted

EHR MERGE:
  → EHR Builder: merge new conditions (additive, no overwrite of existing M17.11)
  → Conflict check: no conflict (new conditions, not updates)
  → intake_progress: 0.55 → 0.68
  → SSE: ehr_update + progress update

AGENT RESPONSE:
  → Intake Agent: suggest_actions node
  → "Thanks, Aisha. I've noted your diabetes and hypertension. Given your diabetes,
     we'll need a recent HbA1c test result. Do you have one?"
  → suggested_actions: [{type: "upload", label: "Upload HbA1c", icon: "📋"}]
  → SSE: agent_response with content + actions

Key: Single chat message triggers clinical extraction, FHIR creation, graph update, progress recalculation, and intelligent follow-up — all within one conversational turn.

Flow 4: Matching Pipeline

intake_progress >= 0.75 (threshold)
  → POST /api/v1/patients/{id}/match
  → Feature flag check: agent_enhanced_matching = ENABLED

STAGE 0 — QDRANT (Semantic Discovery):
  → Voyage AI embeds patient profile (M17.11 + E11 + I10 + preferences)
  → Qdrant ANN search → 25 candidate providers with similarity scores

STAGE 1 — NEO4J (Hard Constraints):
  → Graph traversal: procedure=27447, accredited, visa corridor UAE→{India,Turkey,Thailand,Spain}
  → 25 → 14 candidates (11 eliminated: wrong specialty, no visa corridor, inactive)

STAGE 2 — POSTGRESQL (Weighted Scoring):
  → clinical_fit(0.4) + outcomes(0.2) + cost(0.15) + travel(0.15) + preferences(0.1)
  → 14 providers scored and ranked

STAGE 3 — LLM (Agent-Enhanced):
  → Match Agent: analyze_clinical_picture (Claude Sonnet)
    Reviews: M17.11 + E11 + I10 + medications + BMI
    Notes: "Diabetes requires endocrinology consult. Metformin bridging protocol needed."
  → Match Agent: rerank_edge_cases (Claude Sonnet, top 5)
    Adjusts: Provider X moved up (has integrated diabetes management program)
  → Explanation Agent: generate_explanations (Claude Haiku, Arabic locale)
    For each top provider: clinical + cultural + cost reasoning

RESULT:
  → event: match_completed
  → SSE: match results streamed to frontend
  → Provider cards with confidence scores, costs, differentiators, AI reasoning
  → Aisha sees results in conversation thread
  → Can ask: "Why was Apollo ranked first?" → routes to Explanation Agent

Flow 5: Follow-Up & Iteration

Aisha: "Can you re-match but prioritize cost over outcomes?"
  → POST /chat → Orchestrator: "preference_update" + "match_query"
  → Update preference weights: cost → 0.30, outcomes → 0.10
  → Re-execute matching pipeline with adjusted weights
  → New results with cost-optimized ranking
  → Explanation Agent generates updated reasoning