Files
e5deed8d62 fix(knowledge): warn when container recreate drops local runtimes (#2130)
Packages installed from the dashboard live in the image venv and disappear
when the container is recreated, while settings and indexes stay on the data
volume. Say so at startup, and bake the same extras into the manual FnOS
source image.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 23:14:12 +08:00

169 lines
5.8 KiB
Python

"""Unit tests for the knowledge-base capability gate."""
from __future__ import annotations
import logging
import pytest
from octop.infra.knowledge import gate
def test_capability_is_disabled_by_default(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(gate, "local_embedding_deps_available", lambda: True)
monkeypatch.setattr(gate, "is_model_downloaded", lambda _model: True)
capability = gate.get_capability(lambda _key: None)
assert capability["feature_enabled"] is False
assert capability["selected_model"] == ""
assert capability["prerequisites_ok"] is False
assert capability["usable"] is False
assert capability["checks"] == {
"model_selected": False,
"model_downloaded": False,
"deps_available": True,
"provider_ready": False,
}
def test_enabled_capability_uses_selected_model_not_onnx_service(
monkeypatch: pytest.MonkeyPatch,
) -> None:
model = "BAAI/bge-small-zh-v1.5"
monkeypatch.setattr(
gate,
"embedding_prerequisites_ok_for_model",
lambda candidate: candidate == model,
)
monkeypatch.setattr(gate, "is_model_downloaded", lambda candidate: candidate == model)
monkeypatch.setattr(gate, "local_embedding_deps_available", lambda: True)
values = {
"knowledge_bases_enabled": "true",
"knowledge_embedding_model": model,
"onnx_local_service": '{"enabled": false}',
}
capability = gate.get_capability(values.get)
assert capability["feature_enabled"] is True
assert capability["selected_model"] == model
assert capability["prerequisites_ok"] is True
assert capability["usable"] is True
def test_enabling_requires_model_and_persists_verified_selection(
monkeypatch: pytest.MonkeyPatch,
) -> None:
values: dict[str, str] = {}
model = "BAAI/bge-small-zh-v1.5"
monkeypatch.setattr(
gate,
"require_embedding_prerequisites_for_model",
lambda candidate: candidate.strip(),
)
with pytest.raises(ValueError, match="requires an embedding model"):
gate.set_feature_enabled(values.get, values.__setitem__, enabled=True, model=None)
gate.set_feature_enabled(values.get, values.__setitem__, enabled=True, model=model)
assert values == {
"knowledge_bases_enabled": "true",
"knowledge_embedding_backend": "onnx",
"knowledge_embedding_model": model,
"knowledge_embedding_provider_id": "",
}
def test_assert_knowledge_usable_distinguishes_disabled_from_prerequisites(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(gate, "embedding_prerequisites_ok_for_model", lambda _model: False)
with pytest.raises(RuntimeError, match="disabled"):
gate.assert_knowledge_usable(lambda _key: None)
settings = {
"knowledge_bases_enabled": "true",
"knowledge_embedding_model": "BAAI/bge-small-zh-v1.5",
}
with pytest.raises(RuntimeError, match="prerequisites"):
gate.assert_knowledge_usable(settings.get)
def test_remote_capability_requires_enabled_embedding_provider(
monkeypatch: pytest.MonkeyPatch,
) -> None:
provider = type(
"Provider",
(),
{
"enabled": True,
"api_key": "key",
"base_url": "https://example.test/v1",
"name": "Remote",
"get_models": lambda self: [{"id": "embed-1", "embedding": True}],
},
)()
monkeypatch.setattr(gate, "_provider_for_settings", lambda _repo, _id: provider)
values = {
"knowledge_bases_enabled": "true",
"knowledge_embedding_backend": "remote",
"knowledge_embedding_model": "embed-1",
"knowledge_embedding_provider_id": "7",
}
capability = gate.get_capability(values.get)
assert capability["backend"] == "remote"
assert capability["provider_id"] == "7"
assert capability["prerequisites_ok"] is True
assert capability["checks"]["provider_ready"] is True
def test_warns_when_enabled_local_embedding_deps_are_missing(
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
monkeypatch.setattr(gate, "local_embedding_deps_available", lambda: False)
monkeypatch.setattr(gate, "is_model_downloaded", lambda _model: True)
monkeypatch.setattr(gate, "embedding_prerequisites_ok_for_model", lambda _model: False)
values = {
"knowledge_bases_enabled": "true",
"knowledge_embedding_backend": "onnx",
"knowledge_embedding_model": "BAAI/bge-small-zh-v1.5",
}
with caplog.at_level(logging.WARNING):
gate.warn_if_local_embedding_deps_missing(values.get)
assert "local-embedding" in caplog.text
def test_skips_embedding_dep_warning_when_runtime_is_present_or_unused(
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
monkeypatch.setattr(gate, "local_embedding_deps_available", lambda: True)
monkeypatch.setattr(gate, "is_model_downloaded", lambda _model: True)
monkeypatch.setattr(gate, "embedding_prerequisites_ok_for_model", lambda _model: True)
enabled = {
"knowledge_bases_enabled": "true",
"knowledge_embedding_backend": "onnx",
"knowledge_embedding_model": "BAAI/bge-small-zh-v1.5",
}
disabled = {"knowledge_bases_enabled": "false", "knowledge_embedding_backend": "onnx"}
remote = {
"knowledge_bases_enabled": "true",
"knowledge_embedding_backend": "remote",
"knowledge_embedding_model": "embed-1",
}
with caplog.at_level(logging.WARNING):
gate.warn_if_local_embedding_deps_missing(enabled.get)
gate.warn_if_local_embedding_deps_missing(disabled.get)
monkeypatch.setattr(gate, "local_embedding_deps_available", lambda: False)
gate.warn_if_local_embedding_deps_missing(remote.get)
assert caplog.records == []