mathpulse-api-v3test / tests /test_model_reply_passthrough.py
github-actions[bot]
deploy: backend from 72e77ac
4211c75
Raw History Blame Contribute Delete
3.37 kB
"""Model replies reach their callers whole.
call_hf_chat used to pass every non-streaming reply through a repetition stripper written for small models that
looped. It took ordinary Markdown paragraphs for repeats and deleted them: a tutor reply came back as its last
paragraph, a first-turn chat came back empty, and pretty-printed quiz JSON stopped parsing.
"""
from __future__ import annotations
import json
import os
import sys
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
import main
TUTOR_PARAGRAPHS = [
"Great question! Finding the domain of a rational function comes down to one idea.",
"**Step 1:** Look at the denominator, because division by zero is undefined for every real number.",
"**Step 2:** Set the denominator equal to zero and solve, so you know which x-values to exclude.",
"What value of x makes x - 2 equal to zero?",
"Once you find that value, think about what it means for the domain. Give it a try and tell me what you get!",
]
TUTOR_REPLY = "\n\n".join(TUTOR_PARAGRAPHS)
QUIZ_REPLY = json.dumps(
[
{
"questionType": "multiple_choice",
"question": f"What is the domain of f(x) = 1/(x - {number})?",
"options": [f"A) x != {number}", "B) all real numbers", "C) x > 0", "D) x < 0"],
"correctAnswer": "A",
"bloomLevel": "understand",
"difficulty": "medium",
"topic": "Domain and Range of Functions",
"points": 1,
"explanation": f"Division by zero is undefined, so x cannot equal {number}.",
}
for number in range(1, 11)
],
indent=2,
)
class ReplyingInferenceClient:
"""The inference client's contract: a request in, the model's reply text out."""
def __init__(self, reply: str) -> None:
self.reply = reply
def generate_from_messages(self, _request: object) -> str:
return self.reply
def serve_model_reply(monkeypatch: pytest.MonkeyPatch, reply: str) -> None:
client = ReplyingInferenceClient(reply)
monkeypatch.setattr(main, "get_inference_client", lambda: client)
@pytest.mark.parametrize("reply", [TUTOR_REPLY, QUIZ_REPLY], ids=["markdown-tutor-reply", "pretty-printed-quiz"])
def test_call_hf_chat_returns_the_reply_unchanged(monkeypatch: pytest.MonkeyPatch, reply: str) -> None:
serve_model_reply(monkeypatch, reply)
assert main.call_hf_chat([{"role": "user", "content": "Help me"}], task_type="chat") == reply
@pytest.mark.asyncio
async def test_a_first_turn_chat_reply_reaches_the_student_whole(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(main, "HAS_MEMORY_SERVICE", False)
monkeypatch.setattr(main, "HAS_FIREBASE_ADMIN", False)
monkeypatch.setattr(main, "retrieve_curriculum_context", lambda **_: [])
monkeypatch.setattr(main, "retrieve_chat_context", lambda _message: "")
async def route_intent(_message: str) -> dict[str, object]:
return {"choice": "conceptual_confusion", "confidence": 0.91}
monkeypatch.setattr(main, "route_student_intent", route_intent)
serve_model_reply(monkeypatch, TUTOR_REPLY)
response = await main.chat_tutor(
main.ChatRequest(message="How do I find the domain of f(x) = (x + 1)/(x - 2)?")
)
for paragraph in TUTOR_PARAGRAPHS:
assert paragraph in response.response