Files
llm-in-text/backend/tests/test_llm_extended.py
T

212 lines
6.4 KiB
Python
Raw Normal View History

import asyncio
import importlib
import sys
from pathlib import Path
import pytest
BACKEND_DIR = Path(__file__).resolve().parents[1]
if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
try:
llm = importlib.import_module("llm")
except ModuleNotFoundError:
pytest.skip("llm module dependencies are not available", allow_module_level=True)
def test_extract_message_with_object_message_content_and_thinking():
class Msg:
def __init__(self, content, thinking):
self.content = content
self.thinking = thinking
class Resp:
def __init__(self, message):
self.message = message
resp = Resp(Msg("hello world", "thinking about it"))
content, thinking = llm._extract_message(resp)
assert content == "hello world"
assert thinking == "thinking about it"
def test_extract_message_with_object_message_empty_content():
class Msg:
def __init__(self, content, thinking):
self.content = content
self.thinking = thinking
class Resp:
def __init__(self, message):
self.message = message
resp = Resp(Msg("", None))
content, thinking = llm._extract_message(resp)
assert content == ""
assert thinking == ""
def test_extract_message_with_dict_message():
resp = {"message": {"content": "ok", "thinking": "calc"}}
content, thinking = llm._extract_message(resp)
assert content == "ok"
assert thinking == "calc"
def test_extract_message_dict_no_message_key():
resp = {"not_message": {"content": "irrelevant"}}
content, thinking = llm._extract_message(resp)
assert content == ""
assert thinking == ""
def test_extract_message_dict_message_content_none_and_thinking_none():
resp = {"message": {"content": None, "thinking": None}}
content, thinking = llm._extract_message(resp)
assert content == ""
assert thinking == ""
def test_extract_message_dict_message_thinking_none():
resp = {"message": {"content": "val", "thinking": None}}
content, thinking = llm._extract_message(resp)
assert content == "val"
assert thinking == ""
def test_extract_message_empty_dict():
resp = {}
content, thinking = llm._extract_message(resp)
assert content == ""
assert thinking == ""
def test_call_ollama_no_system_message(monkeypatch):
captured = {}
async def fake_chat(**kwargs):
captured["messages"] = kwargs.get("messages", [])
return {"message": {"content": "ok", "thinking": ""}}
monkeypatch.setattr(llm.client, "chat", fake_chat)
result = asyncio.run(
llm.call_ollama("user prompt body", system_prompt=None, tag="no-system", temperature=0.1)
)
assert result["content"] == "ok"
assert len(captured["messages"]) == 1
assert captured["messages"][0]["role"] == "user"
assert captured["messages"][0]["content"] == "user prompt body"
def test_call_ollama_whitespace_system_message(monkeypatch):
captured = {}
async def fake_chat(**kwargs):
captured["messages"] = kwargs.get("messages", [])
return {"message": {"content": "ok", "thinking": ""}}
monkeypatch.setattr(llm.client, "chat", fake_chat)
result = asyncio.run(
llm.call_ollama("user prompt", system_prompt=" ", tag="whitespace-system", temperature=0.1)
)
assert result["content"] == "ok"
assert len(captured["messages"]) == 1
assert captured["messages"][0]["role"] == "user"
def test_call_ollama_thinking_in_kwargs(monkeypatch):
captured = {}
async def fake_chat(**kwargs):
captured.update(kwargs)
return {"message": {"content": "ok", "thinking": "boom"}}
monkeypatch.setattr(llm.client, "chat", fake_chat)
res = asyncio.run(
llm.call_ollama("prompt", thinking="boom", tag="think-flag", temperature=0.7)
)
assert res["content"] == "ok" and res["think"] == "boom"
assert captured.get("think") == "boom"
def test_call_ollama_cancelled_reraises(monkeypatch):
async def fake_chat(**kwargs):
raise asyncio.CancelledError
monkeypatch.setattr(llm.client, "chat", fake_chat)
with pytest.raises(asyncio.CancelledError):
asyncio.run(
llm.call_ollama("prompt", system_prompt=None, tag="cancel", temperature=0.7)
)
def test_call_ollama_chat_raises_rethrows(monkeypatch):
async def fake_chat(**kwargs):
raise ValueError("boom")
monkeypatch.setattr(llm.client, "chat", fake_chat)
with pytest.raises(ValueError):
asyncio.run(
llm.call_ollama("prompt", system_prompt=None, tag="exception", temperature=0.7)
)
def test_call_ollama_returns_content_and_think_from_response(monkeypatch):
async def fake_chat(**kwargs):
return {"message": {"content": "final", "thinking": "process"}}
monkeypatch.setattr(llm.client, "chat", fake_chat)
res = asyncio.run(
llm.call_ollama("prompt", system_prompt=None, tag="return", temperature=0.7)
)
assert res["content"] == "final" and res["think"] == "process"
def test_call_vlm_ocr_passes_image_and_prompt(monkeypatch):
image_bytes = b"image-bytes"
called = {}
monkeypatch.setattr(llm, "get_vlm_ocr_prompt", lambda: "OCR PROMPT")
async def fake_chat(**kwargs):
called["kwargs"] = kwargs
return {"message": {"content": "ocr result", "thinking": ""}}
monkeypatch.setattr(llm.client, "chat", fake_chat)
result = asyncio.run(llm.call_vlm_ocr(image_bytes, language="auto"))
messages = called["kwargs"].get("messages", [])
assert messages[0]["role"] == "user"
assert messages[0]["content"] == "OCR PROMPT"
assert messages[0]["images"] == [image_bytes]
assert result == "ocr result"
def test_call_vlm_ocr_chat_raises_rethrows(monkeypatch):
image_bytes = b"image-bytes"
monkeypatch.setattr(llm, "get_vlm_ocr_prompt", lambda: "OCR PROMPT")
async def fake_chat(**kwargs):
raise RuntimeError("ocr fail")
monkeypatch.setattr(llm.client, "chat", fake_chat)
with pytest.raises(RuntimeError):
asyncio.run(llm.call_vlm_ocr(image_bytes))
def test_call_vlm_ocr_returns_content_from_response(monkeypatch):
image_bytes = b"img"
monkeypatch.setattr(llm, "get_vlm_ocr_prompt", lambda: "OCR PROMPT")
async def fake_chat(**kwargs):
return {"message": {"content": "ocr text", "thinking": ""}}
monkeypatch.setattr(llm.client, "chat", fake_chat)
content = asyncio.run(llm.call_vlm_ocr(image_bytes))
assert content == "ocr text"