File size: 8,621 Bytes
00bd0c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import pytest
import numpy as np
from unittest.mock import patch
from intent_classification.intent_router_ml import route_intent
from intent_classification.intent_classification_ml import classify_intent


def mock_classify_ready(label="PATIENT_EVIDENCE_QUERY", confidence=0.85):
    """Returns a mock confident classification result."""
    return {"status": "READY", "label": label, "confidence": confidence}

def mock_classify_uncertain(label="PATIENT_EVIDENCE_QUERY", confidence=0.40):
    """Returns a mock uncertain classification result."""
    return {"status": "UNCERTAIN", "label": label, "confidence": confidence}



#──── route_intent ────────────────────────────────────────────────────────────────────────────────

class TestRouteIntent:

    def test_confident_classification_returns_route_status(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_ready()):
            result = route_intent("patient has persistent cough", 1, 1)
            assert result["status"] == "ROUTE"

    def test_confident_classification_returns_correct_intent(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_ready()):
            result = route_intent("patient has persistent cough", 1, 1)
            assert result["intent"] == "PATIENT_EVIDENCE_QUERY"

    def test_confident_classification_returns_confidence(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_ready()):

            result = route_intent("patient has persistent cough", 1, 1)
            assert result["confidence"] == 0.85

    def test_uncertain_classification_returns_needs_clarification(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_uncertain()):

            result = route_intent("something ambiguous", 1, 1)
            assert result["status"] == "NEEDS_CLARIFICATION"

    def test_uncertain_classification_returns_options(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_uncertain()):

            result = route_intent("something ambiguous", 1, 1)
            assert "options" in result
            assert isinstance(result["options"], list)
            assert len(result["options"]) > 0

    def test_uncertain_classification_options_are_strings(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_uncertain()):

            result = route_intent("something ambiguous", 1, 1)
            for opt in result["options"]:
                assert isinstance(opt, str)

    def test_uncertain_classification_returns_confidence(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_uncertain()):

            result = route_intent("something ambiguous", 1, 1)
            assert result["confidence"] == 0.40

    def test_clarification_options_contain_expected_labels(self):
        with patch("intent_classification.intent_router_ml.classify_intent",
                   return_value=mock_classify_uncertain()):

            result = route_intent("something ambiguous", 1, 1)
            assert "Add new evidence" in result["options"]
            assert "Ask for explanation" in result["options"]
            assert "Request source" in result["options"]
            assert "General help" in result["options"]

    def test_all_intents_route_correctly(self):
        intents = [
            "PATIENT_EVIDENCE_QUERY",
            "FOLLOW_UP_EXPLANATION",
            "SOURCE_REQUEST",
            "HELP_OR_OTHER"
        ]
        for intent in intents:
            with patch("intent_classification.intent_router_ml.classify_intent",
                       return_value=mock_classify_ready(label=intent)):
    
                result = route_intent("some input", 1, 1)
                assert result["intent"] == intent


#────── classify_intent────────────────────────────────────────────────────────────

class TestClassifyIntent:

    @patch("intent_classification.intent_classification_ml.embedder")
    @patch("intent_classification.intent_classification_ml.classifier")
    def test_status_is_ready_when_confident(self, mock_classifier, mock_embedder):
        mock_embedder.encode.return_value = np.zeros((1, 384))
        mock_probs = np.array([0.05, 0.85, 0.05, 0.05])
        mock_classifier.predict_proba.return_value = [mock_probs]

        mock_id_to_label = {0: "HELP_OR_OTHER", 1: "PATIENT_EVIDENCE_QUERY",
                            2: "FOLLOW_UP_EXPLANATION", 3: "SOURCE_REQUEST"}

        with patch("intent_classification.intent_classification_ml.id_to_label", mock_id_to_label):
            result = classify_intent("patient has a fever")
            assert result["status"] == "READY"

    @patch("intent_classification.intent_classification_ml.embedder")
    @patch("intent_classification.intent_classification_ml.classifier")
    def test_status_is_uncertain_when_low_confidence(self, mock_classifier, mock_embedder):
        mock_embedder.encode.return_value = np.zeros((1, 384))
        mock_probs = np.array([0.30, 0.35, 0.20, 0.15])
        mock_classifier.predict_proba.return_value = [mock_probs]

        mock_id_to_label = {0: "HELP_OR_OTHER", 1: "PATIENT_EVIDENCE_QUERY",
                            2: "FOLLOW_UP_EXPLANATION", 3: "SOURCE_REQUEST"}

        with patch("intent_classification.intent_classification_ml.id_to_label", mock_id_to_label):

            result = classify_intent("patient has a cough")
            assert result["status"] == "UNCERTAIN"

    @patch("intent_classification.intent_classification_ml.embedder")
    @patch("intent_classification.intent_classification_ml.classifier")
    def test_returns_correct_label(self, mock_classifier, mock_embedder):
        mock_embedder.encode.return_value = np.zeros((1, 384))
        mock_probs = np.array([0.05, 0.05, 0.85, 0.05])
        mock_classifier.predict_proba.return_value = [mock_probs]

        mock_id_to_label = {0: "HELP_OR_OTHER", 1: "PATIENT_EVIDENCE_QUERY",
                            2: "FOLLOW_UP_EXPLANATION", 3: "SOURCE_REQUEST"}

        with patch("intent_classification.intent_classification_ml.id_to_label", mock_id_to_label):

            result = classify_intent("why does smoking cause cancer?")
            assert result["label"] == "FOLLOW_UP_EXPLANATION"


    @patch("intent_classification.intent_classification_ml.embedder")
    @patch("intent_classification.intent_classification_ml.classifier")
    def test_exactly_at_threshold_is_uncertain(self, mock_classifier, mock_embedder):
        """Confidence exactly at CONFIDENCE_THRESHOLD (0.55) should be UNCERTAIN
        since the check is confidence < CONFIDENCE_THRESHOLD."""
        mock_embedder.encode.return_value = np.zeros((1, 384))
        mock_probs = np.array([0.15, 0.55, 0.15, 0.15])
        mock_classifier.predict_proba.return_value = [mock_probs]

        mock_id_to_label = {0: "HELP_OR_OTHER", 1: "PATIENT_EVIDENCE_QUERY",
                            2: "FOLLOW_UP_EXPLANATION", 3: "SOURCE_REQUEST"}

        with patch("intent_classification.intent_classification_ml.id_to_label", mock_id_to_label):

            result = classify_intent("some borderline threshold input")
            assert result["status"] == "READY"

    @patch("intent_classification.intent_classification_ml.embedder")
    @patch("intent_classification.intent_classification_ml.classifier")
    def test_below_threshold_is_uncertain(self, mock_classifier, mock_embedder):
        mock_embedder.encode.return_value = np.zeros((1, 384))
        mock_probs = np.array([0.18, 0.54, 0.16, 0.12])
        mock_classifier.predict_proba.return_value = [mock_probs]

        mock_id_to_label = {0: "HELP_OR_OTHER", 1: "PATIENT_EVIDENCE_QUERY",
                            2: "FOLLOW_UP_EXPLANATION", 3: "SOURCE_REQUEST"}

        with patch("intent_classification.intent_classification_ml.id_to_label", mock_id_to_label):

            result = classify_intent("borderline input")
            assert result["status"] == "UNCERTAIN"