File size: 3,804 Bytes
81e3ca2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import unittest
from unittest.mock import patch

from ai_prof.agent import AgentAction, TeachingBeat
from ai_prof.pdf_utils import Deck, Slide
import app


def _state() -> dict:
    state = app._new_state()
    state["deck"] = Deck(
        slides=[
            Slide(index=0, image_path="/tmp/slide-1.png", text="One"),
            Slide(index=1, image_path="/tmp/slide-2.png", text="Two"),
            Slide(index=2, image_path="/tmp/slide-3.png", text="Three"),
        ]
    )
    state["readings"] = {
        0: "TITLE: One\nCONCEPTS: first",
        1: "TITLE: Two\nCONCEPTS: second",
        2: "TITLE: Three\nCONCEPTS: third",
    }
    state["deck_index"] = "1. One\n2. Two\n3. Three"
    return state


class OrchestratorTests(unittest.TestCase):
    def test_execute_actions_navigates_and_updates_whiteboard(self):
        state = _state()
        navigated = app._execute_actions(
            state,
            (
                AgentAction("goto_slide", {"index": 3}),
                AgentAction(
                    "write_note",
                    {"title": "Remember", "body": "Use the third slide."},
                ),
            ),
        )

        self.assertTrue(navigated)
        self.assertEqual(state["index"], 2)
        self.assertEqual(state["whiteboard"][0]["title"], "Remember")

    @patch.object(app, "tts_speak_full", return_value=None)
    @patch.object(app, "plan_teaching_beat")
    def test_question_can_move_to_supporting_slide(self, plan, _tts):
        plan.return_value = TeachingBeat(
            narration="Let’s revisit the definition.",
            actions=(AgentAction("goto_slide", {"index": 2}),),
            continue_lecture=False,
        )
        outputs = list(app.on_ask("What was the definition?", _state(), []))
        final = outputs[-1]

        self.assertEqual(final[0]["index"], 1)
        self.assertEqual(final[2], "Slide 2 / 3")
        self.assertEqual(final[3][-1]["content"], "Let’s revisit the definition.")

    @patch.object(app, "tts_speak_full", return_value=None)
    @patch.object(app, "plan_teaching_beat")
    def test_lecture_advances_when_agent_does_not_navigate(self, plan, _tts):
        plan.side_effect = [
            TeachingBeat("First beat.", continue_lecture=True),
            TeachingBeat("Second beat.", continue_lecture=False),
        ]
        outputs = list(app.on_teach_deck(_state(), []))
        final = outputs[-1]

        self.assertEqual(final[0]["index"], 1)
        self.assertEqual(
            [message["content"] for message in final[3]],
            ["First beat.", "Second beat."],
        )

    @patch.object(app, "tts_speak_full", return_value=None)
    @patch.object(app, "plan_teaching_beat")
    def test_lecture_ignores_agent_navigation_and_stays_sequential(self, plan, _tts):
        plan.side_effect = [
            TeachingBeat(
                "First beat.",
                actions=(AgentAction("goto_slide", {"index": 3}),),
                continue_lecture=True,
            ),
            TeachingBeat("Second beat.", continue_lecture=False),
        ]

        outputs = list(app.on_teach_deck(_state(), []))
        final = outputs[-1]

        self.assertEqual(final[0]["index"], 1)
        self.assertEqual(final[2], "Slide 2 / 3")

    def test_index_select_moves_to_requested_slide(self):
        state, _img, caption, _board = app.on_index_select(2, _state())

        self.assertEqual(state["index"], 2)
        self.assertEqual(caption, "Slide 3 / 3")

    def test_whiteboard_emits_math_block_for_latex(self):
        state = _state()
        state["whiteboard"] = [{"type": "latex", "expression": r"x^2 + y^2"}]

        board = app._whiteboard_view(state)

        self.assertIn("$$\nx^2 + y^2\n$$", board)


if __name__ == "__main__":
    unittest.main()