mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-05 00:13:01 +00:00
Bumps the Python backend dependency set, holding back the ones that are
resolver-blocked or that regress behaviour this repo depends on.
Notable upgrades:
cryptography 46.0.7 -> 50.0.0 (requires msal 1.37.0, which relaxes its cap)
protobuf 6.33.6 -> 7.35.1 (floats opentelemetry-* to 1.44.0)
openai 2.32.0 -> 2.53.0, anthropic 0.88.0 -> 0.121.0
google-genai 1.73.1 -> 2.17.0 (the 2.0 break is scoped to the Interactions
API, which this repo does not use)
fastmcp 3.2.4 -> 3.4.6, gunicorn 25.3.0 -> 26.0.0
starlette 1.0.0 -> 1.6.0, uvicorn 0.42.0 -> 0.52.1
sentence-transformers 5.3.0 -> 5.7.0, numpy 2.4.4 -> 2.5.1
faiss-cpu 1.13.2 -> 1.15.0, pillow -> 12.3.0 (pinned; security release)
pypdf 6.9.2 -> 6.15.0, lxml 6.0.2 -> 6.1.1 (CVE-2026-41066)
Code changes needed by the bumps:
- openai >= 2.53 rejects a falsy api_key at construction. Keyless
OpenAI-compatible backends (Ollama, llama.cpp, vLLM) legitimately have
none, and pydantic-settings yields "" for a bare `API_KEY=` in .env, so
both call sites now fall back to a placeholder.
- Flask >= 3.1.2 tears a stream_with_context request down twice, and a
ContextVar token may only be reset once. The log-context teardown hook
is now idempotent.
- sentence-transformers renamed get_sentence_embedding_dimension to
get_embedding_dimension in 5.4; use the new name with a fallback.
Held back deliberately:
torch 2.11.0 - 2.13.0 drags torchvision 0.26 -> 0.28 and the whole
docling stack; torch is only probed for cuda.empty_cache()
tokenizers 0.22.2 - transformers pins <=0.23.0 and no stable 0.23.0 exists
transformers - capped <5.9.0: 5.9+ breaks docling's PDF layout model on
Apple Silicon (MPS float64), matching docling-core's own
darwin pin
docling 2.84.0 - 2.118.1 needs rapidocr >=3.9.1 and conflicts with
transformers on macOS; wants its own PR with a
golden-corpus diff
redis 7.4.0 - 8.x defaults socket_timeout to 5s, silently capping the
blocking reads in the device broker and SSE tail
websockets 16.0 - google-genai caps <17.0
marshmallow - dataclasses-json hard-caps <4; spec tightened to match
langchain block - langchain-community 0.4.2 deletes the Qdrant vectorstore
this repo imports; langchain 1.3.x needs websockets <16
157 lines
6.3 KiB
Python
157 lines
6.3 KiB
Python
from unittest.mock import MagicMock, Mock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestEmbeddingsWrapper:
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_init_success(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_embedding_dimension.return_value = 768
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("test-model")
|
|
|
|
mock_st_cls.assert_called_once()
|
|
assert wrapper.dimension == 768
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_init_falls_back_to_legacy_dimension_api(self, mock_st_cls):
|
|
"""sentence-transformers < 5.4 only exposes get_sentence_embedding_dimension."""
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
del mock_model.get_embedding_dimension
|
|
mock_model.get_sentence_embedding_dimension.return_value = 768
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("test-model")
|
|
|
|
assert wrapper.dimension == 768
|
|
mock_model.get_sentence_embedding_dimension.assert_called_once()
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_init_failure(self, mock_st_cls):
|
|
mock_st_cls.side_effect = Exception("model not found")
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
with pytest.raises(Exception, match="model not found"):
|
|
EmbeddingsWrapper("bad-model")
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_init_none_model(self, mock_st_cls):
|
|
mock_st_cls.return_value = None
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
with pytest.raises((ValueError, AttributeError)):
|
|
EmbeddingsWrapper("bad-model")
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_init_null_first_module(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = None
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
with pytest.raises(ValueError, match="failed to load properly"):
|
|
EmbeddingsWrapper("bad-model")
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_embed_query(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_sentence_embedding_dimension.return_value = 3
|
|
mock_model.encode.return_value = MagicMock(tolist=Mock(return_value=[0.1, 0.2, 0.3]))
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("model")
|
|
result = wrapper.embed_query("hello world")
|
|
|
|
mock_model.encode.assert_called_once_with("hello world")
|
|
assert result == [0.1, 0.2, 0.3]
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_embed_documents(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_sentence_embedding_dimension.return_value = 3
|
|
mock_model.encode.return_value = MagicMock(
|
|
tolist=Mock(return_value=[[0.1, 0.2], [0.3, 0.4]])
|
|
)
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("model")
|
|
result = wrapper.embed_documents(["doc1", "doc2"])
|
|
|
|
mock_model.encode.assert_called_with(["doc1", "doc2"])
|
|
assert result == [[0.1, 0.2], [0.3, 0.4]]
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_call_with_string(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_sentence_embedding_dimension.return_value = 3
|
|
mock_model.encode.return_value = MagicMock(tolist=Mock(return_value=[0.1]))
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("model")
|
|
result = wrapper("hello")
|
|
assert result == [0.1]
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_call_with_list(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_sentence_embedding_dimension.return_value = 3
|
|
mock_model.encode.return_value = MagicMock(
|
|
tolist=Mock(return_value=[[0.1], [0.2]])
|
|
)
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("model")
|
|
result = wrapper(["a", "b"])
|
|
assert result == [[0.1], [0.2]]
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_call_with_invalid_type(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_sentence_embedding_dimension.return_value = 3
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
wrapper = EmbeddingsWrapper("model")
|
|
with pytest.raises(ValueError, match="Input must be a string or a list"):
|
|
wrapper(123)
|
|
|
|
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
|
def test_trust_remote_code_default(self, mock_st_cls):
|
|
mock_model = MagicMock()
|
|
mock_model._first_module.return_value = MagicMock()
|
|
mock_model.get_sentence_embedding_dimension.return_value = 768
|
|
mock_st_cls.return_value = mock_model
|
|
|
|
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
|
|
|
EmbeddingsWrapper("model")
|
|
|
|
call_kwargs = mock_st_cls.call_args[1]
|
|
assert call_kwargs["trust_remote_code"] is True
|