File size: 4,230 Bytes
2e818da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import pytest
from unittest.mock import MagicMock
from app.agents.tutor_agent import TutorAgent
from app.schemas.graph import HTML5VisualPayload, NodeData

class AsyncIterator:
    def __init__(self, items):
        self.items = items
    def __aiter__(self):
        return self
    async def __anext__(self):
        if not self.items:
            raise StopAsyncIteration
        return self.items.pop(0)

@pytest.mark.asyncio
async def test_stream_lesson_content_only():
    tutor = TutorAgent.__new__(TutorAgent)
    mock_client = MagicMock()
    tutor._client = mock_client
    
    mock_client.stream_complete.return_value = AsyncIterator(["This ", "is ", "a ", "lesson."])
    
    node = NodeData(id="n1", label="Smart Pace", status="ongoing")
    chunks = [{"source": "adam.pdf", "text": "Adam optimizer uses moment estimation."}]
    
    tokens = []
    async for token in tutor.stream_lesson(node, chunks, "high_school", "content_only"):
        tokens.append(token)
        
    assert "".join(tokens) == "This is a lesson."
    mock_client.stream_complete.assert_called_once()
    messages = mock_client.stream_complete.call_args[0][0]
    assert len(messages) == 2
    assert "Smart Pace" in messages[1]["content"]
    assert "Base the lesson EXCLUSIVELY on the provided source material" in messages[0]["content"]

@pytest.mark.asyncio
async def test_stream_lesson_net_support():
    tutor = TutorAgent.__new__(TutorAgent)
    mock_client = MagicMock()
    tutor._client = mock_client
    
    mock_client.stream_complete.return_value = AsyncIterator(["This ", "is ", "web ", "lesson."])
    
    node = NodeData(id="n1", label="Smart Pace", status="ongoing")
    chunks = [{"source": "adam.pdf", "text": "Adam optimizer uses moment estimation."}]
    web_context = "Smart Pace is a new dynamic step size method."
    
    tokens = []
    async for token in tutor.stream_lesson(
        node, chunks, "high_school", "net_support", web_context=web_context
    ):
        tokens.append(token)
        
    assert "".join(tokens) == "This is web lesson."
    mock_client.stream_complete.assert_called_once()
    messages = mock_client.stream_complete.call_args[0][0]
    assert "WEB SOURCE MATERIAL:" in messages[1]["content"]
    assert "Smart Pace is a new dynamic step size method." in messages[1]["content"]
    assert "Ground your explanation in the provided SOURCE MATERIAL and WEB SOURCE MATERIAL" in messages[0]["content"]


def test_repair_visual_blank_render_error_gets_blank_render_guidance():
    tutor = TutorAgent.__new__(TutorAgent)
    mock_client = MagicMock()
    tutor._client = mock_client

    fake_payload = HTML5VisualPayload(html_code="<html></html>", animation_type="3d")
    mock_client.structured_complete.return_value = fake_payload

    result = tutor.repair_visual(
        original_html="<html>original</html>",
        error_message="BlankRender: no renderable objects were added within 900ms",
    )

    assert result is fake_payload
    mock_client.structured_complete.assert_called_once()
    messages = mock_client.structured_complete.call_args[0][0]
    system_content = messages[0]["content"]
    assert "did NOT crash" in system_content
    assert "group.add(...)" in system_content
    assert "return { ... }" in system_content or "return {" in system_content
    assert "scene.add(...)" in system_content


def test_repair_visual_regular_error_gets_normal_crash_prompt():
    tutor = TutorAgent.__new__(TutorAgent)
    mock_client = MagicMock()
    tutor._client = mock_client

    fake_payload = HTML5VisualPayload(html_code="<html></html>", animation_type="2d_anim")
    mock_client.structured_complete.return_value = fake_payload

    result = tutor.repair_visual(
        original_html="<html>original</html>",
        error_message="ReferenceError: foo is not defined",
    )

    assert result is fake_payload
    mock_client.structured_complete.assert_called_once()
    messages = mock_client.structured_complete.call_args[0][0]
    system_content = messages[0]["content"]
    assert "Fix the JavaScript error and return the corrected complete HTML." in system_content
    assert "did NOT crash" not in system_content
    assert "group.add(...)" not in system_content