Files
2026-07-13 13:12:33 +08:00

137 lines
4.0 KiB
Python

from __future__ import annotations
import pytest
from opensquilla.memory.embedding import LocalEmbeddingProvider, OpenAIEmbeddingProvider
class _FakeEmbeddingResponse:
def __init__(self, data: dict) -> None:
self._data = data
def raise_for_status(self) -> None:
return None
def json(self) -> dict:
return self._data
class _FakeEmbeddingClient:
def __init__(self, captured: dict[str, object]) -> None:
self._captured = captured
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb) -> bool:
return False
async def post(self, url, *, headers, json, timeout):
self._captured["url"] = url
self._captured["headers"] = headers
self._captured["json"] = json
self._captured["timeout"] = timeout
inputs = json["input"] if isinstance(json["input"], list) else [json["input"]]
return _FakeEmbeddingResponse(
{
"data": [
{"index": index, "embedding": [float(index), 1.0]}
for index, _ in enumerate(inputs)
]
}
)
def _patch_embedding_client(monkeypatch, captured: dict[str, object]) -> None:
monkeypatch.setattr(
"opensquilla.memory.embedding.httpx.AsyncClient",
lambda **kwargs: _FakeEmbeddingClient(captured),
)
def test_local_embedding_tokenizes_for_onnx_inputs() -> None:
pytest.importorskip("numpy", reason="local ONNX embedding tokenization needs numpy")
class _Encoding:
ids = [101, 102]
attention_mask = [1, 1]
type_ids = [0, 0]
class _Tokenizer:
def encode_batch(self, texts):
assert texts == ["hello"]
return [_Encoding()]
provider = LocalEmbeddingProvider()
provider._onnx_tokenizer = _Tokenizer()
feed = provider._tokenize_onnx(["hello"], ["input_ids", "attention_mask"])
assert sorted(feed) == ["attention_mask", "input_ids"]
assert feed["input_ids"].tolist() == [[101, 102]]
assert feed["attention_mask"].tolist() == [[1, 1]]
@pytest.mark.asyncio
async def test_openai_embedding_provider_adds_openrouter_app_attribution_for_query(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
_patch_embedding_client(monkeypatch, captured)
provider = OpenAIEmbeddingProvider(
api_key="or-test",
base_url="https://openrouter.ai/api/v1",
model="openai/text-embedding-3-small",
)
embedding = await provider.embed_query("memory query")
assert embedding == [0.0, 1.0]
assert captured["url"] == "https://openrouter.ai/api/v1/embeddings"
assert captured["headers"] == {
"Authorization": "Bearer or-test",
"HTTP-Referer": "https://opensquilla.ai",
"X-Title": "OpenSquilla",
}
@pytest.mark.asyncio
async def test_openai_embedding_provider_skips_app_attribution_for_non_openrouter(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
_patch_embedding_client(monkeypatch, captured)
provider = OpenAIEmbeddingProvider(
api_key="openai-test",
base_url="https://api.openai.com/v1",
model="text-embedding-3-small",
)
embeddings = await provider.embed_batch(["first", "second"])
assert embeddings == [[0.0, 1.0], [1.0, 1.0]]
assert captured["url"] == "https://api.openai.com/v1/embeddings"
assert captured["headers"] == {"Authorization": "Bearer openai-test"}
@pytest.mark.asyncio
async def test_openai_embedding_provider_sends_dimensions_when_configured(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
_patch_embedding_client(monkeypatch, captured)
provider = OpenAIEmbeddingProvider(
api_key="openai-test",
base_url="https://api.openai.com/v1",
model="text-embedding-3-small",
dimensions=512,
)
await provider.embed_query("memory query")
assert captured["json"] == {
"input": "memory query",
"model": "text-embedding-3-small",
"dimensions": 512,
}