import importlib
import sys
from types import SimpleNamespace
import numpy as np
import pytest
def _fake_embedding_vector(dim=1024):
return [0.1] * dim
def _fake_chat_response(content="", reasoning_content=""):
message = SimpleNamespace(
content=content,
reasoning_content=reasoning_content,
)
return SimpleNamespace(choices=[SimpleNamespace(message=message)])
def _load_zhipu_module(monkeypatch, client_factory):
fake_pm = SimpleNamespace(
is_installed=lambda name: True,
install=lambda name: None,
)
fake_openai = SimpleNamespace(
APIConnectionError=type("APIConnectionError", (Exception,), {}),
RateLimitError=type("RateLimitError", (Exception,), {}),
APITimeoutError=type("APITimeoutError", (Exception,), {}),
)
fake_zhipuai = SimpleNamespace(ZhipuAI=client_factory)
monkeypatch.setitem(sys.modules, "pipmaster", fake_pm)
monkeypatch.setitem(sys.modules, "openai", fake_openai)
monkeypatch.setitem(sys.modules, "zhipuai", fake_zhipuai)
sys.modules.pop("lightrag.llm.zhipu", None)
return importlib.import_module("lightrag.llm.zhipu")
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_embedding_sends_dimensions_when_embedding_dim_provided(
monkeypatch,
):
captured_calls = []
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.embeddings = SimpleNamespace(create=self.create)
def create(self, **kwargs):
captured_calls.append(kwargs)
return SimpleNamespace(
data=[SimpleNamespace(embedding=_fake_embedding_vector())]
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
result = await zhipu_module.zhipu_embedding.func(
["hello"],
api_key="test-key",
embedding_dim=2048,
)
assert isinstance(result, np.ndarray)
assert result.shape == (1, 1024)
assert captured_calls == [
{"model": "embedding-3", "input": ["hello"], "dimensions": 2048}
]
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_embedding_omits_dimensions_when_embedding_dim_not_provided(
monkeypatch,
):
captured_calls = []
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.embeddings = SimpleNamespace(create=self.create)
def create(self, **kwargs):
captured_calls.append(kwargs)
return SimpleNamespace(
data=[SimpleNamespace(embedding=_fake_embedding_vector())]
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
await zhipu_module.zhipu_embedding.func(["hello"], api_key="test-key")
assert captured_calls == [{"model": "embedding-3", "input": ["hello"]}]
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_complete_forwards_official_thinking(monkeypatch):
captured_calls = []
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.chat = SimpleNamespace(completions=SimpleNamespace(create=self.create))
def create(self, **kwargs):
captured_calls.append(kwargs)
return _fake_chat_response(content="final answer")
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
result = await zhipu_module.zhipu_complete_if_cache(
prompt="hello",
api_key="test-key",
thinking={"type": "enabled"},
)
assert result == "final answer"
assert captured_calls[0]["thinking"] == {"type": "enabled"}
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_complete_filters_reasoning_when_cot_disabled(monkeypatch):
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.chat = SimpleNamespace(completions=SimpleNamespace(create=self.create))
def create(self, **kwargs):
return _fake_chat_response(
content="visible answer",
reasoning_content="hidden chain of thought",
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
result = await zhipu_module.zhipu_complete_if_cache(
prompt="hello",
api_key="test-key",
enable_cot=False,
)
assert result == "visible answer"
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_complete_includes_reasoning_when_cot_enabled(monkeypatch):
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.chat = SimpleNamespace(completions=SimpleNamespace(create=self.create))
def create(self, **kwargs):
return _fake_chat_response(
content="visible answer",
reasoning_content="hidden chain of thought",
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
result = await zhipu_module.zhipu_complete_if_cache(
prompt="hello",
api_key="test-key",
enable_cot=True,
)
assert result == "hidden chain of thoughtvisible answer"
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_keyword_extraction_ignores_reasoning_content(monkeypatch):
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.chat = SimpleNamespace(completions=SimpleNamespace(create=self.create))
def create(self, **kwargs):
return _fake_chat_response(
content='{"high_level_keywords": ["AI"], "low_level_keywords": ["RAG"]}',
reasoning_content="this should not be parsed",
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
with pytest.warns(DeprecationWarning):
result = await zhipu_module.zhipu_complete(
prompt="hello",
api_key="test-key",
keyword_extraction=True,
enable_cot=True,
)
assert result == '{"high_level_keywords": ["AI"], "low_level_keywords": ["RAG"]}'
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_if_cache_entity_extraction_maps_to_json_object(monkeypatch):
captured_calls = []
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.chat = SimpleNamespace(completions=SimpleNamespace(create=self.create))
def create(self, **kwargs):
captured_calls.append(kwargs)
return _fake_chat_response(
content='{"entities":[],"relationships":[]}',
reasoning_content="this should not be parsed",
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
with pytest.warns(DeprecationWarning):
result = await zhipu_module.zhipu_complete_if_cache(
prompt="hello",
api_key="test-key",
entity_extraction=True,
enable_cot=True,
)
assert result == '{"entities":[],"relationships":[]}'
assert captured_calls[0]["response_format"] == {"type": "json_object"}
assert "entity_extraction" not in captured_calls[0]
@pytest.mark.offline
@pytest.mark.asyncio
async def test_zhipu_if_cache_structured_output_disables_cot(monkeypatch):
class FakeClient:
def __init__(self, api_key=None):
self.api_key = api_key
self.chat = SimpleNamespace(completions=SimpleNamespace(create=self.create))
def create(self, **kwargs):
return _fake_chat_response(
content='{"answer":"ok"}',
reasoning_content="this should not be included",
)
zhipu_module = _load_zhipu_module(monkeypatch, FakeClient)
result = await zhipu_module.zhipu_complete_if_cache(
prompt="hello",
api_key="test-key",
response_format={"type": "json_object"},
enable_cot=True,
)
assert result == '{"answer":"ok"}'
assert "" not in result