File size: 2,925 Bytes
14184e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import json
from unittest.mock import MagicMock

import pytest

from app.agents.study_buddy_agent import StudyBuddyAgent


class AsyncIterator:
    def __init__(self, items):
        self.items = list(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_pair_buddy_default_ignores_node_label_and_uses_normal_chat():
    agent = StudyBuddyAgent.__new__(StudyBuddyAgent)
    client = MagicMock()
    client.stream_complete.return_value = AsyncIterator(["hello"])
    agent._client = client

    tokens = []
    async for token in agent.generate_initial_question(
        node_label="Old Topic Node",
        chunks=[{"source": "paper.pdf", "text": "The paper studies attention heads."}],
        familiarity="graduate",
        student_profile="",
    ):
        tokens.append(token)

    assert "".join(tokens) == "hello"
    messages = client.stream_complete.call_args[0][0]
    serialized = json.dumps(messages)
    assert "Old Topic Node" not in serialized
    assert "master the topic" not in serialized
    assert "Socratic" not in serialized
    assert "normal research companion" in serialized


@pytest.mark.asyncio
async def test_feynman_mode_keeps_questioning_pipeline():
    agent = StudyBuddyAgent.__new__(StudyBuddyAgent)
    client = MagicMock()
    client.complete.return_value = '{"has_gap": true, "reasoning": "missing mechanism"}'
    agent._client = client

    tokens = []
    async for token in agent.evaluate_and_ask_next(
        node_label="Ignored Label",
        chunks=[{"source": "paper.pdf", "text": "Attention uses query-key dot products."}],
        familiarity="graduate",
        history=[],
        student_answer="It is just a dictionary.",
        feynman_mode=True,
    ):
        tokens.append(token)

    assert "".join(tokens).startswith("__FLAG__")


@pytest.mark.asyncio
async def test_pair_buddy_accepts_optional_web_context_as_tool_context():
    agent = StudyBuddyAgent.__new__(StudyBuddyAgent)
    client = MagicMock()
    client.stream_complete.return_value = AsyncIterator(["ok"])
    agent._client = client

    tokens = []
    async for token in agent.evaluate_and_ask_next(
        node_label="Ignored Label",
        chunks=[],
        familiarity="graduate",
        history=[],
        student_answer="What is the latest DeepSeek model?",
        web_context="WEB RESEARCH:\n[DeepSeek update](https://example.com) says a new model shipped.",
        context_tools_summary="web_search",
    ):
        tokens.append(token)

    assert "".join(tokens) == "ok"
    messages = client.stream_complete.call_args[0][0]
    serialized = json.dumps(messages)
    assert "OPTIONAL CONTEXT TOOLS USED: web_search" in serialized
    assert "WEB RESEARCH:" in serialized
    assert "SOURCE MATERIAL" not in serialized