File size: 4,352 Bytes
88519e8
 
 
 
 
 
 
790d141
88519e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf39698
 
 
 
 
 
 
 
 
88519e8
 
bf39698
 
 
 
 
88519e8
 
 
 
 
 
bf39698
 
 
 
88519e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf39698
 
 
 
88519e8
 
790d141
88519e8
 
 
 
 
 
 
790d141
88519e8
 
790d141
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
"""Tests for sage.core.models — dataclass construction and methods."""

from sage.core.models import (
    ExplanationResult,
    EvidenceQuality,
    NewItem,
    ProductScore,
    RefusalType,
    RetrievedChunk,
    StreamingExplanation,
)


class TestNewItem:
    def test_minimal_construction(self):
        item = NewItem(product_id="P1", title="Test Product")
        assert item.product_id == "P1"
        assert item.title == "Test Product"
        assert item.brand is None
        assert item.category is None

    def test_full_construction(self):
        item = NewItem(
            product_id="P1",
            title="Test Product",
            description="A test",
            category="Electronics",
            price=29.99,
            features=["feature1"],
            brand="TestBrand",
        )
        assert item.brand == "TestBrand"
        assert item.price == 29.99


class TestProductScore:
    def test_top_evidence_returns_highest(self):
        chunks = [
            RetrievedChunk(
                text="low", score=0.5, product_id="P1", rating=4.0, review_id="r1"
            ),
            RetrievedChunk(
                text="high", score=0.9, product_id="P1", rating=4.0, review_id="r2"
            ),
            RetrievedChunk(
                text="mid", score=0.7, product_id="P1", rating=4.0, review_id="r3"
            ),
        ]
        product = ProductScore(
            product_id="P1",
            score=0.9,
            chunk_count=3,
            avg_rating=4.0,
            evidence=chunks,
        )
        assert product.top_evidence.text == "high"
        assert product.top_evidence.score == 0.9

    def test_top_evidence_empty(self):
        product = ProductScore(
            product_id="P1",
            score=0.5,
            chunk_count=0,
            avg_rating=4.0,
        )
        assert product.top_evidence is None


class TestExplanationResult:
    def test_to_evidence_dicts(self):
        result = ExplanationResult(
            explanation="test",
            product_id="P1",
            query="q",
            evidence_texts=["text1", "text2"],
            evidence_ids=["id1", "id2"],
            tokens_used=100,
            model="test-model",
        )
        dicts = result.to_evidence_dicts()
        assert len(dicts) == 2
        assert dicts[0] == {"id": "id1", "text": "text1"}
        assert dicts[1] == {"id": "id2", "text": "text2"}

    def test_to_evidence_dicts_empty(self):
        result = ExplanationResult(
            explanation="test",
            product_id="P1",
            query="q",
            evidence_texts=[],
            evidence_ids=[],
            tokens_used=0,
            model="test-model",
        )
        assert result.to_evidence_dicts() == []


class TestStreamingExplanation:
    def test_collects_tokens(self):
        tokens = ["Hello", " ", "world"]
        stream = StreamingExplanation(
            token_iterator=iter(tokens),
            product_id="P1",
            query="q",
            evidence_texts=["ev"],
            evidence_ids=["id1"],
            model="test",
        )
        collected = list(stream)
        assert collected == tokens

        result = stream.get_complete_result()
        assert result.explanation == "Hello world"
        assert result.product_id == "P1"

    def test_empty_stream(self):
        stream = StreamingExplanation(
            token_iterator=iter([]),
            product_id="P1",
            query="q",
            evidence_texts=[],
            evidence_ids=[],
            model="test",
        )
        list(stream)
        result = stream.get_complete_result()
        assert result.explanation == ""


class TestEvidenceQuality:
    def test_sufficient(self):
        eq = EvidenceQuality(
            is_sufficient=True,
            chunk_count=3,
            total_tokens=150,
            top_score=0.9,
        )
        assert eq.is_sufficient is True
        assert eq.refusal_type is None

    def test_insufficient_with_reason(self):
        eq = EvidenceQuality(
            is_sufficient=False,
            chunk_count=1,
            total_tokens=20,
            top_score=0.3,
            refusal_type=RefusalType.INSUFFICIENT_CHUNKS,
        )
        assert eq.is_sufficient is False
        assert eq.refusal_type == RefusalType.INSUFFICIENT_CHUNKS