Files
shirobana-bot/tests/test_reasoning.py
T
RRRosmontis 34089d0042 Initial import of Shirobana bot backend
Include utility modules, shared channels, prompts, read-only skills, wardrobe memory and regression tests. Externalize legacy credentials and exclude private runtime state.
2026-11-11 00:25:30 +08:00

86 lines
4.1 KiB
Python

"""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()