2026-08-01 06:02:20 -07:00
|
|
|
import sys
|
|
|
|
|
import unittest
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MODULE_DIR = Path(__file__).resolve().parents[1]
|
|
|
|
|
sys.path.insert(0, str(MODULE_DIR))
|
|
|
|
|
|
|
|
|
|
import export_swe_chat
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def row(session, turn, prompt, **overrides):
|
|
|
|
|
value = {
|
|
|
|
|
"session_id": session,
|
|
|
|
|
"turn_id": turn,
|
|
|
|
|
"conversation_turn_number": int(turn[1:]),
|
|
|
|
|
"turn_number": int(turn[1:]),
|
|
|
|
|
"turn_type": "user_prompt",
|
|
|
|
|
"role": "user",
|
|
|
|
|
"is_conversational": True,
|
|
|
|
|
"is_continuation": False,
|
|
|
|
|
"content": prompt,
|
|
|
|
|
}
|
|
|
|
|
value.update(overrides)
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExportSWEChatTests(unittest.TestCase):
|
2026-08-01 06:29:50 -07:00
|
|
|
def test_selects_first_prompt_and_its_response_dedupes_and_caps_sources(self):
|
2026-08-01 06:02:20 -07:00
|
|
|
rows = [
|
2026-08-01 06:29:50 -07:00
|
|
|
row("s1", "t2", "answer", conversation_turn_number=1, turn_type="assistant_response", role="assistant"),
|
|
|
|
|
row("s1", "t1", "first", conversation_turn_number=0),
|
|
|
|
|
row("s2", "t1", " first ", conversation_turn_number=0),
|
|
|
|
|
row("s2", "t2", "answer", conversation_turn_number=1, turn_type="assistant_response", role="assistant"),
|
|
|
|
|
row("s3", "t1", "one"),
|
|
|
|
|
row("s4", "t1", "another one", conversation_turn_number=0),
|
|
|
|
|
row("s4", "t2", "answer", conversation_turn_number=1, turn_type="assistant_response", role="assistant"),
|
2026-08-01 06:02:20 -07:00
|
|
|
]
|
|
|
|
|
candidates, funnel = export_swe_chat.select_candidates(
|
|
|
|
|
rows,
|
|
|
|
|
sessions={"s1": ("repo", "user"), "s2": ("repo", "user"), "s4": ("repo", "user")},
|
|
|
|
|
max_per_repo=1, max_per_user=1,
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(1, len(candidates))
|
2026-08-01 06:29:50 -07:00
|
|
|
self.assertEqual(["t1", "t2"], [candidates[0].prompt_turn.turn_id, candidates[0].response_turn.turn_id])
|
|
|
|
|
self.assertEqual("first", candidates[0].prompt_turn.prompt)
|
2026-08-01 06:02:20 -07:00
|
|
|
self.assertEqual(1, funnel["rejectedDuplicateFirstPrompt"])
|
2026-08-01 06:29:50 -07:00
|
|
|
self.assertEqual(1, funnel["sessionsWithoutFirstAssistantResponse"])
|
2026-08-01 06:02:20 -07:00
|
|
|
self.assertEqual(1, funnel["rejectedRepoCap"])
|
|
|
|
|
|
2026-08-01 06:29:50 -07:00
|
|
|
def test_filters_non_user_continuation_empty_and_missing_response(self):
|
2026-08-01 06:02:20 -07:00
|
|
|
rows = [
|
|
|
|
|
row("s1", "t1", "x", role="assistant"), row("s1", "t2", "x", is_continuation=True), row("s1", "t3", ""),
|
2026-08-01 06:29:50 -07:00
|
|
|
row("s2", "t1", "a", conversation_turn_number=0), row("s2", "t2", "wrong ordinal", conversation_turn_number=2, turn_type="assistant_response", role="assistant"),
|
2026-08-01 06:02:20 -07:00
|
|
|
]
|
|
|
|
|
candidates, funnel = export_swe_chat.select_candidates(rows, sessions={}, max_per_repo=10, max_per_user=10)
|
|
|
|
|
self.assertEqual([], candidates)
|
|
|
|
|
self.assertEqual(1, funnel["rejectedRole"])
|
|
|
|
|
self.assertEqual(1, funnel["rejectedContinuation"])
|
2026-08-01 06:29:50 -07:00
|
|
|
self.assertEqual(1, funnel["rejectedUserMalformedOrEmpty"])
|
|
|
|
|
self.assertEqual(1, funnel["sessionsWithoutFirstAssistantResponse"])
|
2026-08-01 06:02:20 -07:00
|
|
|
|
|
|
|
|
def test_candidate_only_retains_first_prompt_as_student_text(self):
|
2026-08-01 06:29:50 -07:00
|
|
|
prompt = export_swe_chat.Turn("s", "t1", 0, 0, "first")
|
|
|
|
|
response = export_swe_chat.Turn("s", "t2", 1, 1, "answer")
|
|
|
|
|
value = export_swe_chat.Candidate("s", "r", "u", prompt, response).json("a" * 40)
|
2026-08-01 06:02:20 -07:00
|
|
|
self.assertEqual("first", value["prompt"])
|
2026-08-01 06:29:50 -07:00
|
|
|
self.assertEqual("answer", value["teacherResponse"])
|
|
|
|
|
self.assertNotIn("answer", value["teacherResponseHash"])
|
2026-08-01 06:02:20 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
unittest.main()
|