"""Hybrid reasoning policy: casual chat off, Agent planning on.""" from pathlib import Path import sys import unittest from unittest.mock import AsyncMock, patch BASE = Path(__file__).resolve().parents[1] sys.path[:0] = [str(BASE / "bot-utility"), str(BASE)] from chat import engine class ReasoningPolicyTests(unittest.IsolatedAsyncioTestCase): def test_reasoning_parameters_are_explicit(self): self.assertEqual( engine._reasoning_params(False), {"thinking": {"type": "disabled"}}, ) self.assertEqual( engine._reasoning_params(True), {"thinking": {"type": "enabled"}, "reasoning_effort": "high"}, ) def test_any_tool_call_requires_agent_reasoning(self): context = object() for tool in ("setu", "web_search", "fetch", "mail"): parsed = {"understood": True, "tools": [{"name": tool}]} self.assertTrue(engine._tool_plan_requires_reasoning(parsed, context)) self.assertFalse(engine._tool_plan_requires_reasoning({"tools": []}, context)) self.assertFalse(engine._tool_plan_requires_reasoning({"understood": False, "tools": [{}]}, context)) self.assertFalse(engine._tool_plan_requires_reasoning({"tools": [{}]}, None)) def test_model_self_assessment_can_request_reasoning(self): context = object() parsed = {"need_reasoning": True, "tools": []} self.assertTrue(engine._chat_requires_reasoning(parsed, context, False)) self.assertFalse(engine._chat_requires_reasoning(parsed, context, True)) self.assertFalse(engine._chat_requires_reasoning({"need_reasoning": False, "tools": []}, context, False)) prompt = engine.load_instruction("agent_instructions.txt") self.assertIn("need_reasoning", prompt) self.assertIn("没有把握", prompt) def test_last_json_retry_uses_reasoning(self): self.assertFalse(engine._json_retry_uses_reasoning(0)) self.assertTrue(engine._json_retry_uses_reasoning(engine.MAX_RETRIES - 2)) self.assertFalse(engine._json_retry_uses_reasoning(engine.MAX_RETRIES - 1)) def test_reminder_tone_forbids_threatening_or_startling_users(self): prompt = engine.BASE_PROMPT self.assertIn("威胁、恐吓、惊吓", prompt) self.assertIn("底色必须温和、让人安心", prompt) self.assertIn("别被白花吓一跳", prompt) self.assertIn("只响这一次", prompt) self.assertIn("只响一次", prompt) self.assertIn("今晚八点提醒你吃饭", prompt) async def test_plain_chat_defaults_to_non_thinking(self): official = AsyncMock(return_value="ok") with patch.object(engine, "_modelscope_available", return_value=False), \ patch.object(engine, "_build_user_content", new=AsyncMock(return_value="聊天")), \ patch.object(engine, "_call_deepseek_official", new=official): await engine.call_deepseek("system", "聊天") self.assertIs(official.await_args.kwargs["reasoning"], False) async def test_agent_request_explicitly_uses_high_reasoning(self): official = AsyncMock(return_value="ok") with patch.object(engine, "_modelscope_available", return_value=False), \ patch.object(engine, "_build_user_content", new=AsyncMock(return_value="调用工具")), \ patch.object(engine, "_call_deepseek_official", new=official): await engine.call_deepseek("system", "调用工具", reasoning=True) self.assertIs(official.await_args.kwargs["reasoning"], True) async def test_modelscope_receives_same_reasoning_flag(self): modelscope = AsyncMock(return_value="ok") with patch.object(engine, "_modelscope_available", return_value=True), \ patch.object(engine, "_build_user_content", new=AsyncMock(return_value="聊天")), \ patch.object(engine, "_call_modelscope", new=modelscope), \ patch.object(engine, "_modelscope_mark_success"): await engine.call_deepseek("system", "聊天", reasoning=True) self.assertIs(modelscope.await_args.kwargs["reasoning"], True) if __name__ == "__main__": unittest.main()