File size: 5,632 Bytes
e1624f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
AgentState — Shared state schema for the OncoAgent LangGraph execution.

Design principles (inspired by Claude Code + Hermes Agent):
  - Immutable input: ``clinical_text`` is never mutated.
  - Additive outputs: each node writes to its own isolated keys.
  - Deterministic routing: ``routing_decision`` and ``selected_tier``
    are set by the Router node using structured logic, never free text.
  - Per-patient memory: ``patient_id`` isolates session history.
"""

from typing import TypedDict, Annotated, List, Dict, Any, Optional
import operator
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages


class AgentState(TypedDict):
    """
    Represents the state of the LangGraph execution for OncoAgent.

    Sections are ordered by the pipeline stage that writes them.
    Keys prefixed with ``#`` comments indicate which node owns each group.
    """

    # ------------------------------------------------------------------ #
    # 0. Session & Patient Context                                       #
    # ------------------------------------------------------------------ #
    patient_id: str                  # Unique patient profile ID
    session_id: str                  # Current session identifier
    user_tier_override: Optional[int]  # Manual tier override (1 or 2, None = auto)
    messages: Annotated[List[BaseMessage], add_messages]  # Chat history

    # ------------------------------------------------------------------ #
    # 1. Input (Immutable — set once at invocation)                      #
    # ------------------------------------------------------------------ #
    clinical_text: str

    # ------------------------------------------------------------------ #
    # 2. Router Node                                                     #
    # ------------------------------------------------------------------ #
    routing_decision: str            # "simple" | "complex" | "insufficient"
    selected_tier: int               # 1 (Qwen 3.5 9B) or 2 (Qwen 3.6 27B)
    complexity_score: float          # 0.0–1.0 complexity estimate

    # ------------------------------------------------------------------ #
    # 3. Ingestion Node (PHI clean + entity extraction)                  #
    # ------------------------------------------------------------------ #
    extracted_entities: Dict[str, Any]
    phi_detected: bool

    # ------------------------------------------------------------------ #
    # 4. Corrective RAG Node                                             #
    # ------------------------------------------------------------------ #
    rag_context: List[str]
    rag_sources: List[str]
    graph_rag_context: List[str]       # Clinical Knowledge Graph results
    api_evidence_context: List[str]    # CIViC / ClinicalTrials.gov results
    rag_confidence: float              # Mean cross-encoder score (0–1)
    rag_retrieval_count: int           # Results that passed the distance gate
    rag_grading_pass_count: int        # Documents graded RELEVANT by CRAG
    rag_query_rewrites: int            # Number of query rewrites performed

    # ------------------------------------------------------------------ #
    # 5. Specialist Node (Tier-adaptive reasoning)                       #
    # ------------------------------------------------------------------ #
    clinical_recommendation: str
    reasoning_trace: str               # Chain-of-thought breakdown

    # ------------------------------------------------------------------ #
    # 6. Critic Node (Reflexion loop)                                    #
    # ------------------------------------------------------------------ #
    critic_verdict: str                # "PASS" | "FAIL"
    critic_feedback: str               # Specific issues for specialist retry
    critic_attempts: int               # Current iteration count (max 2)

    # ------------------------------------------------------------------ #
    # 7. HITL Gate                                                       #
    # ------------------------------------------------------------------ #
    acuity_level: str                  # "low" | "medium" | "high"
    hitl_required: bool                # True if clinician approval needed
    hitl_approved: bool                # Set by clinician via UI interrupt

    # ------------------------------------------------------------------ #
    # 8. Formatter Node (final output)                                   #
    # ------------------------------------------------------------------ #
    formatted_recommendation: str      # Markdown-formatted for Gradio
    confidence_report: Dict[str, Any]  # Full metrics (tier, RAG, critic iters)
    source_citations: List[str]        # Formatted bibliography

    # ------------------------------------------------------------------ #
    # 9. Fallback Node                                                   #
    # ------------------------------------------------------------------ #
    fallback_reason: str               # Why the system fell back to safe mode

    # ------------------------------------------------------------------ #
    # 10. Safety (legacy compat + validator output)                      #
    # ------------------------------------------------------------------ #
    safety_status: str
    is_safe: bool

    # ------------------------------------------------------------------ #
    # 11. Error accumulator (append-only via operator.add)               #
    # ------------------------------------------------------------------ #
    errors: Annotated[List[str], operator.add]