132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
import pytest
|
|
|
|
from app.core.contracts import RequestContext
|
|
from app.core.knowledge_contracts import KnowledgeHit, KnowledgeQuery, KnowledgeSearchResult
|
|
from app.service import knowledge_tool_service
|
|
from app.service.knowledge_tool_service import DatabaseEmbeddingAdapter, query_knowledge_tool
|
|
|
|
|
|
class FakeGateway:
|
|
def __init__(self) -> None:
|
|
self.calls: list[tuple[str, str, int]] = []
|
|
|
|
async def embed(self, *, endpoint_code: str, text: str, timeout_ms: int) -> list[float]:
|
|
self.calls.append((endpoint_code, text, timeout_ms))
|
|
return [0.1] * 1024
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_embedding_adapter_uses_single_text_gateway_contract() -> None:
|
|
gateway = FakeGateway()
|
|
adapter = DatabaseEmbeddingAdapter("knowledge-embedding", 15000, gateway=gateway)
|
|
|
|
vector = await adapter.embed("基金开户")
|
|
|
|
assert len(vector) == 1024
|
|
assert gateway.calls == [("knowledge-embedding", "基金开户", 15000)]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_query_tool_degrades_when_embedding_endpoint_is_unconfigured(monkeypatch) -> None:
|
|
class Settings:
|
|
knowledge_embedding_endpoint_code = ""
|
|
|
|
monkeypatch.setattr("app.service.knowledge_tool_service.get_settings", lambda: Settings())
|
|
|
|
result = await query_knowledge_tool(
|
|
KnowledgeQuery(query="基金开户", intents=("faq",)),
|
|
RequestContext(
|
|
user_id="visitor-1", trace_id="trace", roles=("visitor",), data_scope="public"
|
|
),
|
|
)
|
|
|
|
assert result.degraded is True
|
|
assert result.degradation_reason == "embedding_endpoint_unconfigured"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_query_tool_uses_configured_embedding_endpoint_and_read_only_dependencies(
|
|
monkeypatch,
|
|
) -> None:
|
|
class Settings:
|
|
knowledge_embedding_endpoint_code = "knowledge-embedding"
|
|
knowledge_embedding_timeout_ms = 15000
|
|
milvus_uri = "http://milvus:19530"
|
|
milvus_token = ""
|
|
|
|
class FakeGateway:
|
|
calls: list[tuple[str, str, int]] = []
|
|
|
|
async def embed(
|
|
self, *, endpoint_code: str, text: str, timeout_ms: int
|
|
) -> list[float]:
|
|
self.calls.append((endpoint_code, text, timeout_ms))
|
|
return [0.1] * 1024
|
|
|
|
class FakeMilvus:
|
|
def __init__(self, uri: str, token: str | None) -> None:
|
|
self.uri = uri
|
|
self.token = token
|
|
|
|
class FakeSession:
|
|
async def __aenter__(self) -> object:
|
|
return object()
|
|
|
|
async def __aexit__(self, exc_type, exc, traceback) -> None:
|
|
return None
|
|
|
|
class FakeAuthority:
|
|
def __init__(self, session: object) -> None:
|
|
self.session = session
|
|
|
|
class FakeRetrievalService:
|
|
def __init__(self, embedder, vector_store, config, authority) -> None:
|
|
self.embedder = embedder
|
|
self.vector_store = vector_store
|
|
self.config = config
|
|
self.authority = authority
|
|
|
|
async def search(
|
|
self, query: KnowledgeQuery, context: RequestContext
|
|
) -> KnowledgeSearchResult:
|
|
vector = await self.embedder.embed(query.query)
|
|
assert len(vector) == 1024
|
|
assert isinstance(self.vector_store, FakeMilvus)
|
|
assert isinstance(self.authority, FakeAuthority)
|
|
assert self.config.routes["faq"] == ("fin_faq_collection", 3)
|
|
assert context.data_scope == "public"
|
|
return KnowledgeSearchResult(
|
|
hits=(
|
|
KnowledgeHit(
|
|
knowledge_id="1",
|
|
collection="fin_faq_collection",
|
|
snippet="snippet",
|
|
answer="answer",
|
|
),
|
|
),
|
|
searched_collections=("fin_faq_collection",),
|
|
)
|
|
|
|
gateway = FakeGateway()
|
|
monkeypatch.setattr(knowledge_tool_service, "get_settings", lambda: Settings())
|
|
monkeypatch.setattr(knowledge_tool_service, "DatabaseModelGateway", lambda: gateway)
|
|
monkeypatch.setattr(knowledge_tool_service, "MilvusKnowledgeClient", FakeMilvus)
|
|
monkeypatch.setattr(knowledge_tool_service, "KnowledgeMysqlAuthority", FakeAuthority)
|
|
monkeypatch.setattr(
|
|
knowledge_tool_service, "KnowledgeRetrievalService", FakeRetrievalService
|
|
)
|
|
monkeypatch.setattr(knowledge_tool_service, "SessionFactory", FakeSession)
|
|
|
|
result = await query_knowledge_tool(
|
|
KnowledgeQuery(query="基金开户", intents=("faq",)),
|
|
RequestContext(
|
|
user_id="visitor-1",
|
|
trace_id="trace",
|
|
roles=("visitor",),
|
|
data_scope="public",
|
|
),
|
|
)
|
|
|
|
assert result.hits[0].answer == "answer"
|
|
assert gateway.calls == [("knowledge-embedding", "基金开户", 15000)]
|