Files
shirobana-bot/tests/test_multimodal.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

115 lines
4.6 KiB
Python

"""Native deepseek-flash multimodal chat tests; no real API calls."""
import base64
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import AsyncMock, patch
BASE = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(BASE / "bot-utility"), str(BASE)]
import agent_loop
from agent import ToolResult
from chat import engine
class MultimodalContentTests(unittest.IsolatedAsyncioTestCase):
async def test_local_image_and_task_share_one_user_content(self):
payload = b"\x89PNG\r\n\x1a\nsmall-test-image"
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "task.png"
path.write_bytes(payload)
content = await engine._build_user_content(
"总结图片,并读出其中要做的任务",
[{"url": str(path), "file_size": str(len(payload))}],
)
self.assertIsInstance(content, list)
self.assertEqual(content[0]["type"], "image_url")
self.assertEqual(content[1]["type"], "text")
self.assertEqual(content[1]["text"], "总结图片,并读出其中要做的任务")
data_url = content[0]["image_url"]["url"]
self.assertTrue(data_url.startswith("data:image/png;base64,"))
self.assertEqual(base64.b64decode(data_url.split(",", 1)[1]), payload)
async def test_text_only_content_is_unchanged(self):
content = await engine._build_user_content("普通聊天", None)
self.assertEqual(content, "普通聊天")
async def test_main_call_passes_same_multimodal_content_to_official_model(self):
parts = [
{"type": "image_url", "image_url": {"url": "data:image/png;base64,eA=="}},
{"type": "text", "text": "读图"},
]
official = AsyncMock(return_value="ok")
with patch.object(engine, "_modelscope_available", return_value=False), \
patch.object(engine, "_build_user_content", new=AsyncMock(return_value=parts)), \
patch.object(engine, "_call_deepseek_official", new=official):
result = await engine.call_deepseek("system", "读图", image_infos=[{"url": "x"}])
self.assertEqual(result, "ok")
self.assertIs(official.await_args.args[1], parts)
self.assertEqual(engine.MODEL_NAME, "deepseek-flash")
async def test_private_pending_flow_forwards_original_images_without_description_step(self):
images = [{"url": "/tmp/original.png"}]
reply = AsyncMock()
engine._user_pending_messages[123] = ["按图执行"]
engine._user_pending_images[123] = images
try:
with patch.object(engine, "_call_model_and_reply", new=reply):
await engine._process_pending_messages(123, 0)
finally:
engine._user_pending_messages.pop(123, None)
engine._user_pending_images.pop(123, None)
engine._user_pending_tasks.pop(123, None)
self.assertEqual(reply.await_args.args[2], "按图执行")
self.assertIs(reply.await_args.kwargs["image_infos"], images)
class FakeStore:
def start(self, uid, conversation):
return "task"
def record(self, *args):
pass
def finish(self, *args):
pass
class FakeExecutor:
async def execute(self, name, args, ctx):
return ToolResult(True, "ok", "done")
class MultiRoundEngine:
def __init__(self):
self.image_kwargs = []
self.reasoning_kwargs = []
async def call_deepseek(self, *args, **kwargs):
self.image_kwargs.append(kwargs.get("image_infos"))
self.reasoning_kwargs.append(kwargs.get("reasoning"))
return {"message": [{"content": "完成"}], "tools": []}
def parse_json_response(self, raw):
return raw
class AgentMultimodalTests(unittest.IsolatedAsyncioTestCase):
async def test_tool_followup_keeps_original_images(self):
images = [{"url": "/tmp/original.png"}]
model = MultiRoundEngine()
initial = {"message": [], "tools": [{"name": "mail", "args": {"action": "list"}}]}
channel = type("Channel", (), {"name": "tg", "is_admin": lambda self, uid: True})()
with patch.object(agent_loop, "AgentTaskStore", FakeStore), \
patch.object(agent_loop, "ToolExecutor", FakeExecutor):
result = await agent_loop.run_agent_loop(
model, initial, "system", "按图执行", channel, "private", 1, 2,
image_infos=images,
)
self.assertEqual(result["message"][0]["content"], "完成")
self.assertEqual(model.image_kwargs, [images])
self.assertEqual(model.reasoning_kwargs, [True])
if __name__ == "__main__":
unittest.main()