lllin000_PaperForge/tests/test_openai_compatible.py
LLLin000 49f84a1aad feat(#31): OpenAICompatibleProvider uses openai SDK with requests fallback
Rewrite embedding provider to use openai.OpenAI client instead of
raw requests.post. Old code preserved in requests_fallback.py,
selectable via VECTOR_DB_PROVIDER_TYPE env/setting.

Changes:
- paperforge/embedding/providers/openai_compatible.py: rewrite to SDK
- paperforge/embedding/providers/requests_fallback.py: NEW old impl
- paperforge/embedding/_config.py: add get_provider_type()
- tests/test_openai_compatible.py: 6 new tests (encode, timeout,
  provider_type switch, no-key error, custom base url)

feat(#33): E2E test fixtures — 3 synthetic PDFs + expected outputs

3 synthetic papers generated with PyMuPDF, each 3 pages:
- paper_a: The Effect of Machine Learning on Clinical Outcomes
- paper_b: A Randomized Trial of Remimazolam vs Propofol (with tables)
- paper_c: Deep Learning for Medical Image Segmentation (with figures)

Changes:
- tests/fixtures/papers/: 3 PDFs
- tests/fixtures/expected_outputs/: expected blocks + body units
- tests/conftest.py: e2e_fixture_dir + synthetic_paper_paths fixtures

feat(#26): sqlite-vec schema — vec0 virtual tables + build_state

Schema version bumped 4→5. Adds:
- 3 vec0 virtual tables: vec_fulltext, vec_body, vec_objects (1536-dim)
- 3 companion metadata tables: vec_*_meta (paper_id, chunk_index, text)
- build_state table (key/value/updated_at)
- ensure_vec_extension() in db.py for extension loading

Changes:
- paperforge/memory/schema.py: schema v5, new tables in ensure_schema()
- paperforge/memory/db.py: ensure_vec_extension() helper
- tests/test_vector_schema.py: 5 new tests (tables, virtual, idempotent, v5)
2026-07-09 17:15:19 +08:00

95 lines
3.3 KiB
Python

"""Unit tests for OpenAICompatibleProvider (openai SDK path)."""
from __future__ import annotations
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
from paperforge.embedding.providers.openai_compatible import OpenAICompatibleProvider
@pytest.fixture
def mock_client():
"""Patch openai.OpenAI and return the mock client instance."""
with patch("paperforge.embedding.providers.openai_compatible.openai.OpenAI") as mock_cls:
client = MagicMock()
mock_cls.return_value = client
embedding_response = MagicMock()
emb1 = MagicMock()
emb1.embedding = [0.1, 0.2, 0.3]
emb2 = MagicMock()
emb2.embedding = [0.4, 0.5, 0.6]
embedding_response.data = [emb1, emb2]
client.embeddings.create.return_value = embedding_response
yield client
@pytest.fixture(autouse=True)
def _env(monkeypatch):
monkeypatch.setenv("VECTOR_DB_API_KEY", "test-key-123")
class TestOpenAICompatibleProvider:
def test_encode_calls_client_embeddings_create(
self, mock_client, tmp_path: Path,
):
provider = OpenAICompatibleProvider(tmp_path)
result = provider.encode(["hello", "world"])
mock_client.embeddings.create.assert_called_once_with(
model="text-embedding-3-small",
input=["hello", "world"],
)
assert result == [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
def test_encode_single_returns_one_embedding(self, mock_client, tmp_path: Path):
provider = OpenAICompatibleProvider(tmp_path)
result = provider.encode_single("hello")
assert result == [0.1, 0.2, 0.3]
def test_timeout_and_retries_applied(self, tmp_path: Path):
with patch("paperforge.embedding.providers.openai_compatible.openai.OpenAI") as mock_cls:
OpenAICompatibleProvider(tmp_path)
mock_cls.assert_called_once()
_args, kwargs = mock_cls.call_args
assert kwargs["timeout"] == 30.0
assert kwargs["max_retries"] == 2
def test_provider_type_requests_delegates_to_fallback(self, monkeypatch, tmp_path: Path):
monkeypatch.setenv("VECTOR_DB_PROVIDER_TYPE", "requests")
with patch(
"paperforge.embedding.providers.requests_fallback.requests.post",
) as mock_post:
mock_post.return_value.json.return_value = {
"data": [{"embedding": [0.7, 0.8, 0.9]}],
}
mock_post.return_value.raise_for_status = lambda: None
provider = OpenAICompatibleProvider(tmp_path)
result = provider.encode(["test"])
mock_post.assert_called_once()
assert result == [[0.7, 0.8, 0.9]]
def test_raises_when_no_api_key(self, monkeypatch, tmp_path: Path):
monkeypatch.delenv("VECTOR_DB_API_KEY", raising=False)
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
with pytest.raises(ValueError, match="No API key configured"):
OpenAICompatibleProvider(tmp_path)
def test_accepts_custom_base_url(self, mock_client, monkeypatch, tmp_path: Path):
monkeypatch.setenv("VECTOR_DB_API_BASE", "https://custom.api.com/v1")
provider = OpenAICompatibleProvider(tmp_path)
provider.encode(["test"])
_args, kwargs = mock_client.embeddings.create.call_args
assert kwargs["model"] == "text-embedding-3-small"