| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import importlib.util
- import json
- import unittest
- from pathlib import Path
- CHAT_PATH = Path(__file__).resolve().parents[1] / "routers" / "chat.py"
- spec = importlib.util.spec_from_file_location(
- "chat_under_test_exam", CHAT_PATH)
- chat = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(chat)
- def exam_payload(title="桩基础施工技术考核"):
- return {
- "title": title,
- "totalScore": 100,
- "totalQuestions": 1,
- "singleChoice": {
- "scorePerQuestion": 2,
- "totalScore": 2,
- "count": 1,
- "questions": [
- {
- "text": "钻孔灌注桩清孔完成后应重点检查哪项指标?",
- "options": [
- {"key": "A", "text": "孔底沉渣厚度"},
- {"key": "B", "text": "施工便道宽度"},
- {"key": "C", "text": "钢筋棚颜色"},
- {"key": "D", "text": "围挡广告内容"},
- ],
- "answer": "A",
- "analysis": "孔底沉渣厚度直接影响桩端承载力。",
- }
- ],
- },
- "judge": {"scorePerQuestion": 3, "totalScore": 0, "count": 0, "questions": []},
- "multiple": {"scorePerQuestion": 5, "totalScore": 0, "count": 0, "questions": []},
- "short": {"scorePerQuestion": 10, "totalScore": 0, "count": 0, "questions": []},
- }
- class ExamResponseSanitizerTests(unittest.TestCase):
- def test_removes_thinking_process_prefix(self):
- raw = "Thinking Process:\n\n1. Analyze the Request.\n\n" + \
- json.dumps(exam_payload(), ensure_ascii=False)
- cleaned = chat._sanitize_exam_response(raw)
- parsed = json.loads(cleaned)
- self.assertEqual(parsed["title"], "桩基础施工技术考核")
- self.assertIn("singleChoice", parsed)
- self.assertNotIn("Thinking Process", cleaned)
- def test_extracts_json_from_markdown_code_block(self):
- raw = "下面是生成结果:\n```json\n" + \
- json.dumps(exam_payload("桥梁考试"), ensure_ascii=False) + "\n```"
- cleaned = chat._sanitize_exam_response(raw)
- parsed = json.loads(cleaned)
- self.assertEqual(parsed["title"], "桥梁考试")
- def test_prefers_exam_payload_over_other_json_noise(self):
- raw = (
- "Thinking Process:\n"
- '{"note":"not exam"}\n'
- "Final Answer:\n"
- + json.dumps(exam_payload("最终试卷"), ensure_ascii=False)
- )
- cleaned = chat._sanitize_exam_response(raw)
- parsed = json.loads(cleaned)
- self.assertEqual(parsed["title"], "最终试卷")
- self.assertIn("singleChoice", parsed)
- def test_extracts_exam_payload_when_reasoning_contains_quotes_and_examples(self):
- raw = (
- 'Thinking Process:\n'
- 'The output must contain "title", "totalScore", "singleChoice".\n'
- 'Use {"key": "A", "text": "..."} as the option shape example.\n'
- 'Section example: {"scorePerQuestion": 2, "totalScore": 20, "count": 10, "questions": [...]}.\n\n'
- + json.dumps(exam_payload("带说明的最终试卷"), ensure_ascii=False)
- )
- cleaned = chat._sanitize_exam_response(raw)
- parsed = json.loads(cleaned)
- self.assertEqual(parsed["title"], "带说明的最终试卷")
- self.assertIn("singleChoice", parsed)
- self.assertFalse(cleaned.startswith("Thinking Process"))
- def test_extracts_trailing_exam_json_after_think_suffix(self):
- raw = (
- "Thinking Process:\n"
- 'Use {"key": "A", "text": "..."} as example.\n'
- "</think>\n\n"
- + json.dumps(exam_payload("尾部试卷"), ensure_ascii=False)
- )
- cleaned = chat._sanitize_exam_response(raw)
- parsed = json.loads(cleaned)
- self.assertEqual(parsed["title"], "尾部试卷")
- self.assertEqual(parsed["totalQuestions"], 1)
- def test_repairs_unescaped_quotes_inside_string_values(self):
- payload = json.dumps(exam_payload("引号容错"), ensure_ascii=False)
- payload = payload.replace(
- "钻孔灌注桩清孔完成后应重点检查哪项指标?", '钻孔灌注桩必须实行"一炮三检"制度吗?')
- payload = payload.replace("孔底沉渣厚度直接影响桩端承载力。", '"一炮三检"是爆破作业的常见安全检查制度。')
- cleaned = chat._sanitize_exam_response(payload)
- parsed = json.loads(cleaned)
- self.assertEqual(parsed["title"], "引号容错")
- self.assertIn('"一炮三检"', parsed["singleChoice"]["questions"][0]["text"])
- if __name__ == "__main__":
- unittest.main()
|