mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-15 12:13:38 +00:00
refactor: rename the application package to docsgpt
The backend import package is now docsgpt, the name it will carry on PyPI; application was far too generic to install into anyone's site-packages. git mv plus a mechanical rewrite of every import, dotted string and path reference: 734 Python files, the compose files, Dockerfile, workflows, docs, setup scripts, devcontainer, k8s manifests, vscode config, pytest and coverage config, .gitignore. Behaviour is unchanged. Kept for one release: - A top-level application package whose meta-path finder resolves application.x.y to the already-imported docsgpt.x.y object, so old imports and entry points (celery -A application.app.celery, uvicorn application.asgi:asgi_app) keep working with a FutureWarning. - Celery registers every application.* task name as an alias of its docsgpt.* task on start-up, so messages queued by the previous release still run. The redbeat key prefix moves to redbeat:docsgpt:v2: so schedule entries the previous release wrote are left unread instead of firing twice. The backend image builds from the repository root (docker build -f docsgpt/Dockerfile .) so it can ship the alias package; a root .dockerignore allow-lists docsgpt/ and application/ and keeps caches, local data, .env files, the sample index files and the Dockerfile out. Compose and the image workflows point at the new context.
This commit is contained in:
1 parent
3e7f1f91b4
commit
574f96341e
984 files changed
+9847
-9682
No files matched your search
@@ -2,7 +2,7 @@ from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore.base import (
|
||||
from docsgpt.vectorstore.base import (
|
||||
BaseVectorStore,
|
||||
EmbeddingsSingleton,
|
||||
RemoteEmbeddings,
|
||||
@@ -30,7 +30,7 @@ class TestRemoteEmbeddings:
|
||||
emb = RemoteEmbeddings(api_url="http://host", model_name="m")
|
||||
assert "Authorization" not in emb.headers
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_sends_correct_payload(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
@@ -48,7 +48,7 @@ class TestRemoteEmbeddings:
|
||||
assert call_kwargs[1]["json"]["model"] == "model-v1"
|
||||
assert result == [[0.1, 0.2]]
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_sorts_by_index(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
@@ -64,7 +64,7 @@ class TestRemoteEmbeddings:
|
||||
result = emb._embed(["a", "b"])
|
||||
assert result == [[0.1, 0.2], [0.3, 0.4]]
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_raises_on_error_response(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {"error": "rate limit exceeded"}
|
||||
@@ -75,7 +75,7 @@ class TestRemoteEmbeddings:
|
||||
with pytest.raises(ValueError, match="rate limit exceeded"):
|
||||
emb._embed("test")
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_raises_on_unexpected_format(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {"unexpected": True}
|
||||
@@ -86,7 +86,7 @@ class TestRemoteEmbeddings:
|
||||
with pytest.raises(ValueError, match="Unexpected response format"):
|
||||
emb._embed("test")
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_raises_on_non_dict_response(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = [1, 2, 3]
|
||||
@@ -97,7 +97,7 @@ class TestRemoteEmbeddings:
|
||||
with pytest.raises(ValueError, match="Unexpected response format"):
|
||||
emb._embed("test")
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_query(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
@@ -112,7 +112,7 @@ class TestRemoteEmbeddings:
|
||||
assert result == [0.1, 0.2, 0.3]
|
||||
assert emb.dimension == 3
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_query_raises_on_bad_structure(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
# Return multiple embeddings for a single query
|
||||
@@ -129,7 +129,7 @@ class TestRemoteEmbeddings:
|
||||
with pytest.raises(ValueError, match="Unexpected result structure"):
|
||||
emb.embed_query("hello")
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_embed_documents(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
@@ -151,7 +151,7 @@ class TestRemoteEmbeddings:
|
||||
emb = RemoteEmbeddings("http://host", "m")
|
||||
assert emb.embed_documents([]) == []
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_call_with_string(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
@@ -164,7 +164,7 @@ class TestRemoteEmbeddings:
|
||||
result = emb("hello")
|
||||
assert result == [0.5]
|
||||
|
||||
@patch("application.vectorstore.base.requests.post")
|
||||
@patch("docsgpt.vectorstore.base.requests.post")
|
||||
def test_call_with_list(self, mock_post):
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
@@ -191,7 +191,7 @@ class TestEmbeddingsSingleton:
|
||||
def setup_method(self):
|
||||
EmbeddingsSingleton._instances = {}
|
||||
|
||||
@patch("application.vectorstore.base.OpenAIEmbeddings")
|
||||
@patch("docsgpt.vectorstore.base.OpenAIEmbeddings")
|
||||
def test_get_instance_openai(self, mock_openai_cls):
|
||||
mock_instance = Mock()
|
||||
mock_openai_cls.return_value = mock_instance
|
||||
@@ -199,7 +199,7 @@ class TestEmbeddingsSingleton:
|
||||
result = EmbeddingsSingleton.get_instance("openai_text-embedding-ada-002")
|
||||
assert result is mock_instance
|
||||
|
||||
@patch("application.vectorstore.base.OpenAIEmbeddings")
|
||||
@patch("docsgpt.vectorstore.base.OpenAIEmbeddings")
|
||||
def test_singleton_returns_same_instance(self, mock_openai_cls):
|
||||
mock_instance = Mock()
|
||||
mock_openai_cls.return_value = mock_instance
|
||||
@@ -209,7 +209,7 @@ class TestEmbeddingsSingleton:
|
||||
assert r1 is r2
|
||||
mock_openai_cls.assert_called_once()
|
||||
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
def test_get_instance_huggingface(self, mock_get_wrapper):
|
||||
mock_wrapper_cls = Mock()
|
||||
mock_instance = Mock()
|
||||
@@ -221,7 +221,7 @@ class TestEmbeddingsSingleton:
|
||||
)
|
||||
assert result is mock_instance
|
||||
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
def test_get_instance_unknown_falls_back_to_wrapper(self, mock_get_wrapper):
|
||||
mock_wrapper_cls = Mock()
|
||||
mock_instance = Mock()
|
||||
@@ -232,7 +232,7 @@ class TestEmbeddingsSingleton:
|
||||
mock_wrapper_cls.assert_called_once_with("custom_model_name")
|
||||
assert result is mock_instance
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
def test_get_instance_uses_remote_when_base_url_set(self, mock_settings):
|
||||
"""Direct callers (GraphRAG, semantic chunking) must route to the
|
||||
remote embeddings API instead of loading a local model."""
|
||||
@@ -246,7 +246,7 @@ class TestEmbeddingsSingleton:
|
||||
assert result.model_name == "embeddinggemma"
|
||||
assert result.headers["Authorization"] == "Bearer sk-remote"
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
def test_get_instance_remote_falls_back_to_settings_key(self, mock_settings):
|
||||
"""When no key is passed, the remote dispatch uses EMBEDDINGS_KEY."""
|
||||
mock_settings.EMBEDDINGS_BASE_URL = "http://remote:8080"
|
||||
@@ -258,8 +258,8 @@ class TestEmbeddingsSingleton:
|
||||
assert result.headers["Authorization"] == "Bearer sk-from-settings"
|
||||
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
def test_get_instance_hf_ignores_positional_key(
|
||||
self, mock_get_wrapper, mock_settings
|
||||
):
|
||||
@@ -281,8 +281,8 @@ class TestEmbeddingsSingleton:
|
||||
# The configured name is passed through; the registry maps it to a repo.
|
||||
mock_wrapper_cls.assert_called_once_with(HF_MPNET)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
def test_get_instance_hf_ignores_keyword_args(
|
||||
self, mock_get_wrapper, mock_settings
|
||||
):
|
||||
@@ -321,7 +321,7 @@ class TestBaseVectorStore:
|
||||
assert store.add_chunk("text") is None
|
||||
assert store.delete_chunk("id") is None
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
def test_is_azure_configured_true(self, mock_settings):
|
||||
mock_settings.OPENAI_API_BASE = "https://azure.openai.com"
|
||||
mock_settings.OPENAI_API_VERSION = "2023-05-15"
|
||||
@@ -330,7 +330,7 @@ class TestBaseVectorStore:
|
||||
store = ConcreteVectorStore()
|
||||
assert store.is_azure_configured()
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
def test_is_azure_configured_false(self, mock_settings):
|
||||
mock_settings.OPENAI_API_BASE = None
|
||||
mock_settings.OPENAI_API_VERSION = None
|
||||
@@ -339,7 +339,7 @@ class TestBaseVectorStore:
|
||||
store = ConcreteVectorStore()
|
||||
assert not store.is_azure_configured()
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
def test_get_embeddings_remote(self, mock_settings):
|
||||
mock_settings.EMBEDDINGS_BASE_URL = "http://remote:8080"
|
||||
|
||||
@@ -349,8 +349,8 @@ class TestBaseVectorStore:
|
||||
assert isinstance(result, RemoteEmbeddings)
|
||||
assert result.api_url == "http://remote:8080"
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_get_embeddings_openai(self, mock_get_instance, mock_settings):
|
||||
mock_settings.EMBEDDINGS_BASE_URL = None
|
||||
mock_settings.OPENAI_API_BASE = None
|
||||
@@ -364,8 +364,8 @@ class TestBaseVectorStore:
|
||||
result = store._get_embeddings("openai_text-embedding-ada-002", "sk-key")
|
||||
assert result is mock_emb
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_get_embeddings_openai_azure(self, mock_get_instance, mock_settings):
|
||||
mock_settings.EMBEDDINGS_BASE_URL = None
|
||||
mock_settings.OPENAI_API_BASE = "https://azure.openai.com"
|
||||
@@ -380,8 +380,8 @@ class TestBaseVectorStore:
|
||||
result = store._get_embeddings("openai_text-embedding-ada-002", "sk-key")
|
||||
assert result is mock_emb
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_get_embeddings_huggingface_no_local_model(
|
||||
self, mock_exists, mock_get_instance, mock_settings
|
||||
@@ -396,8 +396,8 @@ class TestBaseVectorStore:
|
||||
)
|
||||
assert result is mock_emb
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_get_embeddings_registered_model_passes_configured_name(
|
||||
self, mock_get_instance, mock_settings
|
||||
):
|
||||
@@ -419,8 +419,8 @@ class TestBaseVectorStore:
|
||||
"huggingface_sentence-transformers/all-mpnet-base-v2"
|
||||
)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_get_embeddings_generic(self, mock_get_instance, mock_settings):
|
||||
mock_settings.EMBEDDINGS_BASE_URL = None
|
||||
mock_emb = Mock()
|
||||
@@ -437,7 +437,7 @@ class TestSearchWithScoresDefault:
|
||||
def test_pairs_hits_with_none(self):
|
||||
"""A store that reports no score still satisfies the contract, so the
|
||||
retriever never has to special-case it."""
|
||||
from application.vectorstore.base import BaseVectorStore
|
||||
from docsgpt.vectorstore.base import BaseVectorStore
|
||||
|
||||
class _Store(BaseVectorStore):
|
||||
def search(self, question, k=2, *args, **kwargs):
|
||||
@@ -451,7 +451,7 @@ class TestSearchWithScoresDefault:
|
||||
assert store.search_with_scores("q", k=2) == [("a", None), ("b", None)]
|
||||
|
||||
def test_handles_store_returning_none(self):
|
||||
from application.vectorstore.base import BaseVectorStore
|
||||
from docsgpt.vectorstore.base import BaseVectorStore
|
||||
|
||||
class _Store(BaseVectorStore):
|
||||
def search(self, question, k=2, *args, **kwargs):
|
||||
@@ -477,8 +477,8 @@ class TestGetEmbeddingsResolver:
|
||||
def setup_method(self):
|
||||
EmbeddingsSingleton._instances = {}
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_defaults_from_settings_do_not_raise(
|
||||
self, _mock_exists, mock_get_wrapper, mock_settings
|
||||
@@ -497,8 +497,8 @@ class TestGetEmbeddingsResolver:
|
||||
assert result is mock_instance
|
||||
assert set(EmbeddingsSingleton._instances) == {HF_MPNET}
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_shares_cache_entry_with_vectorstore_helper(
|
||||
self, _mock_exists, mock_get_wrapper, mock_settings
|
||||
@@ -518,8 +518,8 @@ class TestGetEmbeddingsResolver:
|
||||
assert set(EmbeddingsSingleton._instances) == {HF_MPNET}
|
||||
mock_wrapper_cls.assert_called_once()
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base._get_embeddings_wrapper")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base._get_embeddings_wrapper")
|
||||
def test_repeated_resolution_loads_one_model(
|
||||
self, mock_get_wrapper, mock_settings
|
||||
):
|
||||
@@ -543,7 +543,7 @@ class TestGetEmbeddingsResolver:
|
||||
assert set(EmbeddingsSingleton._instances) == {HF_MPNET}
|
||||
mock_wrapper_cls.assert_called_once_with(HF_MPNET)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
def test_remote_when_base_url_configured(self, mock_settings):
|
||||
mock_settings.EMBEDDINGS_BASE_URL = "http://remote:8080"
|
||||
mock_settings.EMBEDDINGS_NAME = HF_MPNET
|
||||
@@ -556,8 +556,8 @@ class TestGetEmbeddingsResolver:
|
||||
assert result.model_name == HF_MPNET
|
||||
assert result.headers["Authorization"] == "Bearer sk-remote"
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_openai_passes_key(self, mock_get_instance, mock_settings):
|
||||
mock_settings.EMBEDDINGS_BASE_URL = None
|
||||
mock_settings.OPENAI_API_BASE = None
|
||||
@@ -572,8 +572,8 @@ class TestGetEmbeddingsResolver:
|
||||
"openai_text-embedding-ada-002", openai_api_key="sk-from-settings"
|
||||
)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_openai_azure_uses_deployment_name(
|
||||
self, mock_get_instance, mock_settings
|
||||
):
|
||||
@@ -591,8 +591,8 @@ class TestGetEmbeddingsResolver:
|
||||
"openai_text-embedding-ada-002", model="embed-deploy"
|
||||
)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_openai_alias_also_reaches_the_azure_deployment(
|
||||
self, mock_get_instance, mock_settings
|
||||
):
|
||||
@@ -616,8 +616,8 @@ class TestGetEmbeddingsResolver:
|
||||
"text-embedding-ada-002", model="embed-deploy"
|
||||
)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_openai_name_is_matched_case_insensitively(
|
||||
self, mock_get_instance, mock_settings
|
||||
):
|
||||
@@ -634,8 +634,8 @@ class TestGetEmbeddingsResolver:
|
||||
"OpenAI_Text-Embedding-Ada-002", openai_api_key="sk-from-settings"
|
||||
)
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.EmbeddingsSingleton.get_instance")
|
||||
def test_explicit_arguments_win_over_settings(
|
||||
self, mock_get_instance, mock_settings
|
||||
):
|
||||
@@ -647,8 +647,8 @@ class TestGetEmbeddingsResolver:
|
||||
|
||||
mock_get_instance.assert_called_once_with("some_custom_embedding")
|
||||
|
||||
@patch("application.vectorstore.base.settings")
|
||||
@patch("application.vectorstore.base.get_embeddings")
|
||||
@patch("docsgpt.vectorstore.base.settings")
|
||||
@patch("docsgpt.vectorstore.base.get_embeddings")
|
||||
def test_vectorstore_helper_delegates_to_resolver(
|
||||
self, mock_resolver, _mock_settings
|
||||
):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from application.vectorstore.document_class import Document
|
||||
from docsgpt.vectorstore.document_class import Document
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
@@ -6,12 +6,12 @@ import pytest
|
||||
def _make_es_store(source_id="test-source"):
|
||||
"""Helper to create an ElasticsearchStore with mocked deps."""
|
||||
# Reset class-level connection
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
ElasticsearchStore._es_connection = None
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.elasticsearch.settings"
|
||||
"docsgpt.vectorstore.elasticsearch.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules", {"elasticsearch": MagicMock(), "elasticsearch.helpers": MagicMock()}
|
||||
):
|
||||
@@ -39,7 +39,7 @@ def _make_es_store(source_id="test-source"):
|
||||
@pytest.mark.unit
|
||||
class TestElasticsearchStoreInit:
|
||||
def test_source_id_cleaned(self):
|
||||
store, _, _ = _make_es_store(source_id="application/indexes/abc123/")
|
||||
store, _, _ = _make_es_store(source_id="docsgpt/indexes/abc123/")
|
||||
assert store.source_id == "abc123"
|
||||
|
||||
def test_init_with_url(self):
|
||||
@@ -48,12 +48,12 @@ class TestElasticsearchStoreInit:
|
||||
assert store.index_name == "test_index"
|
||||
|
||||
def test_init_with_cloud_id(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
ElasticsearchStore._es_connection = None
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.elasticsearch.settings"
|
||||
"docsgpt.vectorstore.elasticsearch.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules", {"elasticsearch": MagicMock()}
|
||||
):
|
||||
@@ -70,12 +70,12 @@ class TestElasticsearchStoreInit:
|
||||
assert store.docsearch is not None
|
||||
|
||||
def test_init_no_url_no_cloud_id_raises(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
ElasticsearchStore._es_connection = None
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.elasticsearch.settings"
|
||||
"docsgpt.vectorstore.elasticsearch.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules", {"elasticsearch": MagicMock()}
|
||||
):
|
||||
@@ -88,12 +88,12 @@ class TestElasticsearchStoreInit:
|
||||
ElasticsearchStore(source_id="src", embeddings_key="k")
|
||||
|
||||
def test_reuses_class_connection(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
ElasticsearchStore._es_connection = None
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.elasticsearch.settings"
|
||||
"docsgpt.vectorstore.elasticsearch.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules", {"elasticsearch": MagicMock()}
|
||||
):
|
||||
@@ -245,7 +245,7 @@ class TestElasticsearchStoreIndex:
|
||||
@pytest.mark.unit
|
||||
class TestElasticsearchStoreConnectToElasticsearch:
|
||||
def test_connect_with_url(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
with patch.dict("sys.modules", {"elasticsearch": MagicMock()}):
|
||||
import elasticsearch
|
||||
@@ -261,7 +261,7 @@ class TestElasticsearchStoreConnectToElasticsearch:
|
||||
assert result is mock_es
|
||||
|
||||
def test_connect_with_both_raises(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
with patch.dict("sys.modules", {"elasticsearch": MagicMock()}):
|
||||
with pytest.raises(ValueError, match="Both es_url and cloud_id"):
|
||||
@@ -270,14 +270,14 @@ class TestElasticsearchStoreConnectToElasticsearch:
|
||||
)
|
||||
|
||||
def test_connect_with_neither_raises(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
with patch.dict("sys.modules", {"elasticsearch": MagicMock()}):
|
||||
with pytest.raises(ValueError, match="provide either"):
|
||||
ElasticsearchStore.connect_to_elasticsearch()
|
||||
|
||||
def test_connect_with_api_key(self):
|
||||
from application.vectorstore.elasticsearch import ElasticsearchStore
|
||||
from docsgpt.vectorstore.elasticsearch import ElasticsearchStore
|
||||
|
||||
with patch.dict("sys.modules", {"elasticsearch": MagicMock()}):
|
||||
import elasticsearch
|
||||
|
||||
@@ -6,8 +6,8 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore import base
|
||||
from application.vectorstore.embeddings_delegated import EMBED_TASK, DelegatedEmbeddings
|
||||
from docsgpt.vectorstore import base
|
||||
from docsgpt.vectorstore.embeddings_delegated import EMBED_TASK, DelegatedEmbeddings
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -19,7 +19,7 @@ def _clear_singleton():
|
||||
|
||||
@pytest.fixture
|
||||
def not_in_worker():
|
||||
with patch("application.vectorstore.embeddings_delegated._in_worker", return_value=False):
|
||||
with patch("docsgpt.vectorstore.embeddings_delegated._in_worker", return_value=False):
|
||||
yield
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class TestDispatch:
|
||||
def test_query_is_embedded_on_the_worker(self, not_in_worker):
|
||||
celery = MagicMock()
|
||||
celery.send_task.return_value.get.return_value = [[0.1, 0.2, 0.3]]
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
vector = DelegatedEmbeddings("some/model").embed_query("hello")
|
||||
assert vector == [0.1, 0.2, 0.3]
|
||||
assert celery.send_task.call_args.args[0] == EMBED_TASK
|
||||
@@ -36,7 +36,7 @@ class TestDispatch:
|
||||
def test_routed_to_the_embeddings_queue(self, not_in_worker):
|
||||
celery = MagicMock()
|
||||
celery.send_task.return_value.get.return_value = [[0.0]]
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
with patch.object(base.settings, "EMBEDDINGS_QUEUE", "embeddings"):
|
||||
DelegatedEmbeddings("some/model").embed_query("hi")
|
||||
assert celery.send_task.call_args.kwargs["queue"] == "embeddings"
|
||||
@@ -44,7 +44,7 @@ class TestDispatch:
|
||||
def test_no_worker_gives_an_actionable_error(self, not_in_worker):
|
||||
celery = MagicMock()
|
||||
celery.send_task.return_value.get.side_effect = TimeoutError("no worker")
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
DelegatedEmbeddings("some/model").embed_query("hi")
|
||||
message = str(excinfo.value)
|
||||
@@ -53,7 +53,7 @@ class TestDispatch:
|
||||
|
||||
def test_empty_input_never_reaches_the_broker(self, not_in_worker):
|
||||
celery = MagicMock()
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
assert DelegatedEmbeddings("some/model").embed_documents([]) == []
|
||||
celery.send_task.assert_not_called()
|
||||
|
||||
@@ -65,9 +65,9 @@ class TestInsideAWorker:
|
||||
local = MagicMock()
|
||||
local.embed_documents.return_value = [[1.0, 2.0]]
|
||||
celery = MagicMock()
|
||||
with patch("application.vectorstore.embeddings_delegated._in_worker", return_value=True):
|
||||
with patch("application.vectorstore.base.build_local_embeddings", return_value=local):
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.vectorstore.embeddings_delegated._in_worker", return_value=True):
|
||||
with patch("docsgpt.vectorstore.base.build_local_embeddings", return_value=local):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
vector = DelegatedEmbeddings("some/model").embed_query("hi")
|
||||
assert vector == [1.0, 2.0]
|
||||
celery.send_task.assert_not_called()
|
||||
@@ -77,8 +77,8 @@ class TestInsideAWorker:
|
||||
local.embed_documents.return_value = [[1.0]]
|
||||
builder = MagicMock(return_value=local)
|
||||
client = DelegatedEmbeddings("some/model")
|
||||
with patch("application.vectorstore.embeddings_delegated._in_worker", return_value=True):
|
||||
with patch("application.vectorstore.base.build_local_embeddings", builder):
|
||||
with patch("docsgpt.vectorstore.embeddings_delegated._in_worker", return_value=True):
|
||||
with patch("docsgpt.vectorstore.base.build_local_embeddings", builder):
|
||||
client.embed_query("a")
|
||||
client.embed_query("b")
|
||||
builder.assert_called_once()
|
||||
@@ -87,7 +87,7 @@ class TestInsideAWorker:
|
||||
class TestDimension:
|
||||
def test_registry_width_costs_no_round_trip(self):
|
||||
celery = MagicMock()
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
client = DelegatedEmbeddings("ibm-granite/granite-embedding-311m-multilingual-r2")
|
||||
assert client.dimension == 768
|
||||
celery.send_task.assert_not_called()
|
||||
@@ -95,7 +95,7 @@ class TestDimension:
|
||||
def test_unknown_width_is_probed_once(self, not_in_worker):
|
||||
celery = MagicMock()
|
||||
celery.send_task.return_value.get.return_value = [[0.0] * 1024]
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
client = DelegatedEmbeddings("some/unregistered")
|
||||
assert client.dimension == 1024
|
||||
assert client.dimension == 1024
|
||||
@@ -104,7 +104,7 @@ class TestDimension:
|
||||
def test_an_unreachable_worker_reports_no_width(self, not_in_worker):
|
||||
celery = MagicMock()
|
||||
celery.send_task.return_value.get.side_effect = TimeoutError("down")
|
||||
with patch("application.celery_init.celery", celery):
|
||||
with patch("docsgpt.celery_init.celery", celery):
|
||||
assert DelegatedEmbeddings("some/unregistered").dimension is None
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ class TestFailureCooldown:
|
||||
def test_only_the_first_call_waits_out_the_timeout(self, not_in_worker):
|
||||
celery, _ = self._celery(TimeoutError("no worker"))
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
for _ in range(4):
|
||||
with pytest.raises(RuntimeError):
|
||||
embeddings.embed_query("q")
|
||||
@@ -161,7 +161,7 @@ class TestFailureCooldown:
|
||||
def test_the_fast_failure_still_names_the_remedy(self, not_in_worker):
|
||||
celery, _ = self._celery(TimeoutError("no worker"))
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
with pytest.raises(RuntimeError):
|
||||
embeddings.embed_query("q")
|
||||
with pytest.raises(RuntimeError, match="EMBEDDINGS_DELEGATE_TO_WORKER=false"):
|
||||
@@ -170,7 +170,7 @@ class TestFailureCooldown:
|
||||
def test_the_latch_clears_once_the_worker_answers(self, not_in_worker):
|
||||
celery, result = self._celery(TimeoutError("no worker"))
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
with pytest.raises(RuntimeError):
|
||||
embeddings.embed_query("q")
|
||||
embeddings._failed_at = None # stand in for the cooldown elapsing
|
||||
@@ -183,7 +183,7 @@ class TestFailureCooldown:
|
||||
celery, result = self._celery(None)
|
||||
result.get.return_value = [[0.1, 0.2]]
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
for _ in range(3):
|
||||
assert embeddings.embed_query("q") == [0.1, 0.2]
|
||||
assert celery.send_task.call_count == 3
|
||||
@@ -225,7 +225,7 @@ class TestTheConcurrentFirstWave:
|
||||
except Exception as exc: # noqa: BLE001 -- recorded for the assertions
|
||||
errors.append(exc)
|
||||
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
workers = [threading.Thread(target=call) for _ in range(threads)]
|
||||
for worker in workers:
|
||||
worker.start()
|
||||
@@ -241,7 +241,7 @@ class TestTheConcurrentFirstWave:
|
||||
celery = self._blocking_celery(release, TimeoutError("no worker"))
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch(
|
||||
"application.vectorstore.embeddings_delegated._PROBE_WAIT", 0.05
|
||||
"docsgpt.vectorstore.embeddings_delegated._PROBE_WAIT", 0.05
|
||||
):
|
||||
values, errors = self._race(celery, embeddings, release)
|
||||
|
||||
@@ -255,7 +255,7 @@ class TestTheConcurrentFirstWave:
|
||||
release = threading.Event()
|
||||
celery = self._blocking_celery(release, TimeoutError("no worker"))
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch("application.vectorstore.embeddings_delegated._PROBE_WAIT", 0.05):
|
||||
with patch("docsgpt.vectorstore.embeddings_delegated._PROBE_WAIT", 0.05):
|
||||
_, errors = self._race(celery, embeddings, release, threads=3)
|
||||
assert all("EMBEDDINGS_DELEGATE_TO_WORKER=false" in str(e) for e in errors)
|
||||
|
||||
@@ -277,13 +277,13 @@ class TestTheConcurrentFirstWave:
|
||||
release.set()
|
||||
celery = self._blocking_celery(release, [[0.3]])
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
embeddings.embed_query("warm")
|
||||
assert embeddings._verified is True
|
||||
|
||||
with patch.object(embeddings, "_state_lock") as lock:
|
||||
with patch.dict(
|
||||
"sys.modules", {"application.celery_init": MagicMock(celery=celery)}
|
||||
"sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}
|
||||
):
|
||||
embeddings.embed_query("q")
|
||||
lock.__enter__.assert_not_called()
|
||||
@@ -302,7 +302,7 @@ class TestTheConcurrentFirstWave:
|
||||
healthy = self._blocking_celery(warm, [[0.4]])
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict(
|
||||
"sys.modules", {"application.celery_init": MagicMock(celery=healthy)}
|
||||
"sys.modules", {"docsgpt.celery_init": MagicMock(celery=healthy)}
|
||||
):
|
||||
embeddings.embed_query("warm")
|
||||
assert embeddings._verified is True
|
||||
@@ -310,8 +310,8 @@ class TestTheConcurrentFirstWave:
|
||||
# No cooldown, so anything that gates the second wave can only be the
|
||||
# probe -- which engages only because the failure cleared _verified.
|
||||
with patch(
|
||||
"application.vectorstore.embeddings_delegated._FAILURE_COOLDOWN", 0.0
|
||||
), patch("application.vectorstore.embeddings_delegated._PROBE_WAIT", 0.05):
|
||||
"docsgpt.vectorstore.embeddings_delegated._FAILURE_COOLDOWN", 0.0
|
||||
), patch("docsgpt.vectorstore.embeddings_delegated._PROBE_WAIT", 0.05):
|
||||
dying = threading.Event()
|
||||
died = self._blocking_celery(dying, TimeoutError("worker went away"))
|
||||
self._race(died, embeddings, dying)
|
||||
@@ -352,14 +352,14 @@ class TestTheResultIsForgotten:
|
||||
def test_a_successful_embed_forgets_its_result(self, not_in_worker):
|
||||
celery, result = self._celery(value=[[0.1, 0.2]])
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
assert embeddings.embed_query("q") == [0.1, 0.2]
|
||||
result.forget.assert_called_once()
|
||||
|
||||
def test_a_failed_embed_still_forgets(self, not_in_worker):
|
||||
celery, result = self._celery(side_effect=TimeoutError("no worker"))
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
with pytest.raises(RuntimeError):
|
||||
embeddings.embed_query("q")
|
||||
result.forget.assert_called_once()
|
||||
@@ -368,13 +368,13 @@ class TestTheResultIsForgotten:
|
||||
celery, result = self._celery(value=[[0.3, 0.4]])
|
||||
result.forget.side_effect = ConnectionError("backend down")
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
assert embeddings.embed_query("q") == [0.3, 0.4]
|
||||
|
||||
def test_forgetting_does_not_mask_the_dispatch_failure(self, not_in_worker):
|
||||
celery, result = self._celery(side_effect=TimeoutError("no worker"))
|
||||
result.forget.side_effect = ConnectionError("backend down")
|
||||
embeddings = DelegatedEmbeddings("granite-311m")
|
||||
with patch.dict("sys.modules", {"application.celery_init": MagicMock(celery=celery)}):
|
||||
with patch.dict("sys.modules", {"docsgpt.celery_init": MagicMock(celery=celery)}):
|
||||
with pytest.raises(RuntimeError, match="timed out or failed"):
|
||||
embeddings.embed_query("q")
|
||||
@@ -5,9 +5,9 @@ from unittest.mock import MagicMock, patch
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from application.vectorstore import embeddings_local
|
||||
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
||||
from application.vectorstore.model_registry import GRANITE_97M, MPNET
|
||||
from docsgpt.vectorstore import embeddings_local
|
||||
from docsgpt.vectorstore.embeddings_local import EmbeddingsWrapper
|
||||
from docsgpt.vectorstore.model_registry import GRANITE_97M, MPNET
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -10,8 +10,8 @@ from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.storage.local import LocalStorage
|
||||
from application.vectorstore.faiss import FaissStore
|
||||
from docsgpt.storage.local import LocalStorage
|
||||
from docsgpt.vectorstore.faiss import FaissStore
|
||||
|
||||
|
||||
class _FakeEmbeddings:
|
||||
@@ -52,16 +52,16 @@ def storage(tmp_path):
|
||||
|
||||
@pytest.fixture
|
||||
def make_store(storage):
|
||||
from application.vectorstore.faiss import FaissStore
|
||||
from docsgpt.vectorstore.faiss import FaissStore
|
||||
|
||||
def _make(source_id="src", docs_init=None):
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
return_value=_FakeEmbeddings(),
|
||||
), patch(
|
||||
"application.vectorstore.faiss.StorageCreator.get_storage",
|
||||
"docsgpt.vectorstore.faiss.StorageCreator.get_storage",
|
||||
return_value=storage,
|
||||
), patch("application.vectorstore.faiss.settings") as mock_settings:
|
||||
), patch("docsgpt.vectorstore.faiss.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
return FaissStore(source_id, "key", docs_init=docs_init)
|
||||
|
||||
@@ -180,7 +180,7 @@ class TestFaissPersistence:
|
||||
@pytest.mark.unit
|
||||
class TestFaissStoreAssertEmbeddingDimensions:
|
||||
def test_dimension_mismatch_raises(self, populated):
|
||||
with patch("application.vectorstore.faiss.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.faiss.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = (
|
||||
"huggingface_sentence-transformers/all-mpnet-base-v2"
|
||||
)
|
||||
@@ -194,7 +194,7 @@ class TestFaissStoreAssertEmbeddingDimensions:
|
||||
perfectly valid remote configuration, so an unknown width is deferred,
|
||||
not treated as a mismatch.
|
||||
"""
|
||||
with patch("application.vectorstore.faiss.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.faiss.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = (
|
||||
"huggingface_sentence-transformers/all-mpnet-base-v2"
|
||||
)
|
||||
@@ -203,7 +203,7 @@ class TestFaissStoreAssertEmbeddingDimensions:
|
||||
assert populated.assert_embedding_dimensions(embeddings) is None
|
||||
|
||||
def test_dimension_match_passes(self, populated):
|
||||
with patch("application.vectorstore.faiss.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.faiss.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = (
|
||||
"huggingface_sentence-transformers/all-mpnet-base-v2"
|
||||
)
|
||||
@@ -215,33 +215,33 @@ class TestFaissStoreAssertEmbeddingDimensions:
|
||||
That skipped exactly the case it exists for: an index built with one
|
||||
model being opened under a different one.
|
||||
"""
|
||||
with patch("application.vectorstore.faiss.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.faiss.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = "openai_text-embedding-ada-002"
|
||||
with pytest.raises(ValueError, match="Embedding dimension mismatch"):
|
||||
populated.assert_embedding_dimensions(Mock(dimension=1536))
|
||||
|
||||
def test_mismatch_message_points_at_the_reembed_script(self, populated):
|
||||
with patch("application.vectorstore.faiss.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.faiss.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = "granite-311m"
|
||||
with pytest.raises(ValueError, match="application.scripts.reembed"):
|
||||
with pytest.raises(ValueError, match="docsgpt.scripts.reembed"):
|
||||
populated.assert_embedding_dimensions(Mock(dimension=768))
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetVectorstore:
|
||||
def test_empty_path_returns_base(self):
|
||||
from application.vectorstore.faiss import get_vectorstore
|
||||
from docsgpt.vectorstore.faiss import get_vectorstore
|
||||
|
||||
assert get_vectorstore("") == "indexes"
|
||||
|
||||
def test_normal_path(self):
|
||||
from application.vectorstore.faiss import get_vectorstore
|
||||
from docsgpt.vectorstore.faiss import get_vectorstore
|
||||
|
||||
assert get_vectorstore("abc") == "indexes/abc"
|
||||
|
||||
@pytest.mark.parametrize("bad", ["../etc", "..\\etc", "a/../../b"])
|
||||
def test_traversal_rejected(self, bad):
|
||||
from application.vectorstore.faiss import get_vectorstore
|
||||
from docsgpt.vectorstore.faiss import get_vectorstore
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid source_id path"):
|
||||
get_vectorstore(bad)
|
||||
|
||||
@@ -14,7 +14,7 @@ import pickletools
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore.faiss_docstore import (
|
||||
from docsgpt.vectorstore.faiss_docstore import (
|
||||
CompatUnpickler,
|
||||
LegacyDocstore,
|
||||
LegacyDocument,
|
||||
@@ -61,8 +61,8 @@ class TestPickleSidecar:
|
||||
|
||||
dump_pickle_sidecar(DOCUMENTS, MAPPING)
|
||||
assert not [m for m in sys.modules if m.startswith("langchain")]
|
||||
assert LegacyDocument.__module__.startswith("application.")
|
||||
assert LegacyDocstore.__module__.startswith("application.")
|
||||
assert LegacyDocument.__module__.startswith("docsgpt.")
|
||||
assert LegacyDocstore.__module__.startswith("docsgpt.")
|
||||
|
||||
def test_reads_pydantic_state_shape(self):
|
||||
"""langchain's Document is a pydantic model; that is the state we get."""
|
||||
@@ -174,7 +174,7 @@ class TestRealLegacyFixture:
|
||||
"""The index.pkl checked into the repo was written by langchain in 2025."""
|
||||
|
||||
def test_reads_committed_legacy_index(self):
|
||||
with open("application/index.pkl", "rb") as f:
|
||||
with open("docsgpt/index.pkl", "rb") as f:
|
||||
documents, mapping = load_pickle_sidecar(f.read())
|
||||
assert len(documents) == 3
|
||||
assert len(mapping) == 3
|
||||
@@ -182,7 +182,7 @@ class TestRealLegacyFixture:
|
||||
assert all(d["metadata"].get("title") for d in documents.values())
|
||||
|
||||
def test_legacy_survives_conversion_to_json(self):
|
||||
with open("application/index.pkl", "rb") as f:
|
||||
with open("docsgpt/index.pkl", "rb") as f:
|
||||
documents, mapping = load_pickle_sidecar(f.read())
|
||||
restored, restored_mapping = load_json_sidecar(
|
||||
dump_json_sidecar(documents, mapping)
|
||||
|
||||
@@ -6,13 +6,13 @@ import pytest
|
||||
def _make_lancedb_store(source_id="test-source"):
|
||||
"""Helper to create a LanceDBVectorStore with mocked deps."""
|
||||
with patch(
|
||||
"application.vectorstore.lancedb.settings"
|
||||
"docsgpt.vectorstore.lancedb.settings"
|
||||
) as mock_settings:
|
||||
mock_settings.LANCEDB_PATH = "/tmp/lancedb"
|
||||
mock_settings.LANCEDB_TABLE_NAME = "docs"
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
|
||||
from application.vectorstore.lancedb import LanceDBVectorStore
|
||||
from docsgpt.vectorstore.lancedb import LanceDBVectorStore
|
||||
|
||||
store = LanceDBVectorStore(
|
||||
path="/tmp/lancedb",
|
||||
@@ -31,11 +31,11 @@ class TestLanceDBVectorStoreInit:
|
||||
assert store.table_name == "docs_src1"
|
||||
|
||||
def test_table_name_without_source_id(self):
|
||||
with patch("application.vectorstore.lancedb.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.lancedb.settings") as mock_settings:
|
||||
mock_settings.LANCEDB_PATH = "/tmp"
|
||||
mock_settings.LANCEDB_TABLE_NAME = "docs"
|
||||
|
||||
from application.vectorstore.lancedb import LanceDBVectorStore
|
||||
from docsgpt.vectorstore.lancedb import LanceDBVectorStore
|
||||
|
||||
store = LanceDBVectorStore(
|
||||
path="/tmp", table_name_prefix="docs", source_id=None
|
||||
|
||||
@@ -40,12 +40,12 @@ class _FakeEmbeddings:
|
||||
|
||||
@pytest.fixture
|
||||
def store(tmp_path):
|
||||
from application.vectorstore.milvus import MilvusStore
|
||||
from docsgpt.vectorstore.milvus import MilvusStore
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
return_value=_FakeEmbeddings(),
|
||||
), patch("application.vectorstore.milvus.settings") as mock_settings:
|
||||
), patch("docsgpt.vectorstore.milvus.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
mock_settings.MILVUS_COLLECTION_NAME = "test_collection"
|
||||
mock_settings.MILVUS_URI = str(tmp_path / "milvus.db")
|
||||
@@ -114,12 +114,12 @@ class TestMilvusStore:
|
||||
assert len(populated.get_chunks()) == 2
|
||||
|
||||
def test_delete_index_removes_only_this_source(self, populated):
|
||||
from application.vectorstore.milvus import MilvusStore
|
||||
from docsgpt.vectorstore.milvus import MilvusStore
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
return_value=_FakeEmbeddings(),
|
||||
), patch("application.vectorstore.milvus.settings") as mock_settings:
|
||||
), patch("docsgpt.vectorstore.milvus.settings") as mock_settings:
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
mock_settings.MILVUS_COLLECTION_NAME = "test_collection"
|
||||
mock_settings.MILVUS_URI = ":memory:"
|
||||
@@ -156,7 +156,7 @@ class TestMilvusUriEnvGuard:
|
||||
"""
|
||||
|
||||
def test_env_var_hidden_during_import_and_restored(self):
|
||||
from application.vectorstore.milvus import _without_milvus_uri_env
|
||||
from docsgpt.vectorstore.milvus import _without_milvus_uri_env
|
||||
|
||||
with patch.dict(os.environ, {"MILVUS_URI": "./milvus_local.db"}):
|
||||
with _without_milvus_uri_env():
|
||||
@@ -164,7 +164,7 @@ class TestMilvusUriEnvGuard:
|
||||
assert os.environ["MILVUS_URI"] == "./milvus_local.db"
|
||||
|
||||
def test_absent_env_var_stays_absent(self):
|
||||
from application.vectorstore.milvus import _without_milvus_uri_env
|
||||
from docsgpt.vectorstore.milvus import _without_milvus_uri_env
|
||||
|
||||
env = {k: v for k, v in os.environ.items() if k != "MILVUS_URI"}
|
||||
with patch.dict(os.environ, env, clear=True):
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore import model_registry as reg
|
||||
from docsgpt.vectorstore import model_registry as reg
|
||||
|
||||
|
||||
class TestResolve:
|
||||
@@ -107,7 +107,7 @@ class TestRegistryMatchesTheHub:
|
||||
"model", [m for m in reg.MODELS if m.provider == "fastembed"]
|
||||
)
|
||||
def test_entry_matches_repository_metadata(self, model):
|
||||
from application.vectorstore.embeddings_local import _describe_from_repo
|
||||
from docsgpt.vectorstore.embeddings_local import _describe_from_repo
|
||||
|
||||
described = _describe_from_repo(model.repo)
|
||||
assert described is not None, f"{model.repo} declares no pooling metadata"
|
||||
|
||||
@@ -6,9 +6,9 @@ import pytest
|
||||
def _make_mongodb_store(source_id="test-source"):
|
||||
"""Helper to create a MongoDBVectorStore with all external deps mocked."""
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
) as mock_get_emb, patch(
|
||||
"application.vectorstore.mongodb.settings"
|
||||
"docsgpt.vectorstore.mongodb.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules", {"pymongo": MagicMock()}
|
||||
):
|
||||
@@ -19,7 +19,7 @@ def _make_mongodb_store(source_id="test-source"):
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
mock_settings.MONGO_URI = "mongodb://localhost:27017"
|
||||
|
||||
from application.vectorstore.mongodb import MongoDBVectorStore
|
||||
from docsgpt.vectorstore.mongodb import MongoDBVectorStore
|
||||
|
||||
store = MongoDBVectorStore(
|
||||
source_id=source_id,
|
||||
@@ -37,7 +37,7 @@ def _make_mongodb_store(source_id="test-source"):
|
||||
@pytest.mark.unit
|
||||
class TestMongoDBVectorStoreInit:
|
||||
def test_source_id_cleaned(self):
|
||||
store, _, _ = _make_mongodb_store(source_id="application/indexes/abc123/")
|
||||
store, _, _ = _make_mongodb_store(source_id="docsgpt/indexes/abc123/")
|
||||
assert store._source_id == "abc123"
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ class TestMongoDBVectorStoreDeleteChunk:
|
||||
store, mock_collection, _ = _make_mongodb_store()
|
||||
mock_collection.delete_one.return_value = Mock(deleted_count=1)
|
||||
|
||||
with patch("application.vectorstore.mongodb.ObjectId", create=True):
|
||||
with patch("docsgpt.vectorstore.mongodb.ObjectId", create=True):
|
||||
# We need to mock bson.objectid.ObjectId
|
||||
with patch.dict("sys.modules", {"bson": MagicMock(), "bson.objectid": MagicMock()}):
|
||||
from unittest.mock import MagicMock as MM
|
||||
|
||||
@@ -10,9 +10,9 @@ def _make_store(
|
||||
):
|
||||
"""Helper to create a PGVectorStore with all external deps mocked."""
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
) as mock_get_emb, patch(
|
||||
"application.vectorstore.pgvector.settings"
|
||||
"docsgpt.vectorstore.pgvector.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules",
|
||||
{
|
||||
@@ -29,7 +29,7 @@ def _make_store(
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
mock_settings.PGVECTOR_CONNECTION_STRING = connection_string
|
||||
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
store = PGVectorStore(
|
||||
source_id=source_id,
|
||||
@@ -55,14 +55,14 @@ def _make_store(
|
||||
@pytest.mark.unit
|
||||
class TestPGVectorStoreInit:
|
||||
def test_source_id_cleaned(self):
|
||||
store, _, _, _ = _make_store(source_id="application/indexes/abc123/")
|
||||
store, _, _, _ = _make_store(source_id="docsgpt/indexes/abc123/")
|
||||
assert store._source_id == "abc123"
|
||||
|
||||
def test_missing_connection_string_raises(self):
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
) as mock_get_emb, patch(
|
||||
"application.vectorstore.pgvector.settings"
|
||||
"docsgpt.vectorstore.pgvector.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules",
|
||||
{
|
||||
@@ -76,7 +76,7 @@ class TestPGVectorStoreInit:
|
||||
mock_settings.PGVECTOR_CONNECTION_STRING = None
|
||||
mock_settings.POSTGRES_URI = None
|
||||
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
with pytest.raises(ValueError, match="connection string is required"):
|
||||
PGVectorStore(
|
||||
|
||||
@@ -13,9 +13,9 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.storage.db.bootstrap import ensure_vector_schema
|
||||
from application.vectorstore import pgvector as pgvector_module
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.storage.db.bootstrap import ensure_vector_schema
|
||||
from docsgpt.vectorstore import pgvector as pgvector_module
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
@@ -88,7 +88,7 @@ def live_dsn(postgresql, monkeypatch):
|
||||
pytest.skip(f"pgvector extension unavailable: {exc}")
|
||||
|
||||
dsn = _dsn(postgresql.info)
|
||||
from application.core import settings as settings_module
|
||||
from docsgpt.core import settings as settings_module
|
||||
|
||||
settings = settings_module.settings
|
||||
monkeypatch.setattr(settings, "VECTOR_STORE", "pgvector", raising=False)
|
||||
@@ -111,14 +111,14 @@ def stub_embeddings():
|
||||
"""
|
||||
stub = _StubEmbeddings()
|
||||
with patch(
|
||||
"application.vectorstore.base.get_embeddings", return_value=stub
|
||||
"docsgpt.vectorstore.base.get_embeddings", return_value=stub
|
||||
), patch(
|
||||
"application.vectorstore.base.build_local_embeddings", return_value=stub
|
||||
"docsgpt.vectorstore.base.build_local_embeddings", return_value=stub
|
||||
), patch(
|
||||
"application.vectorstore.model_registry.dimension_for",
|
||||
"docsgpt.vectorstore.model_registry.dimension_for",
|
||||
return_value=STUB_DIM,
|
||||
), patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
return_value=stub,
|
||||
):
|
||||
yield stub
|
||||
@@ -150,9 +150,9 @@ class TestBootHookCreatesTheSchema:
|
||||
|
||||
wide = _WideStubEmbeddings()
|
||||
with patch(
|
||||
"application.vectorstore.base.get_embeddings", return_value=wide
|
||||
"docsgpt.vectorstore.base.get_embeddings", return_value=wide
|
||||
), patch(
|
||||
"application.vectorstore.model_registry.dimension_for",
|
||||
"docsgpt.vectorstore.model_registry.dimension_for",
|
||||
return_value=wide.dimension,
|
||||
):
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
@@ -165,7 +165,7 @@ class TestBootHookCreatesTheSchema:
|
||||
def test_creates_graph_tables_when_graphrag_is_enabled(
|
||||
self, live_dsn, postgresql, stub_embeddings, monkeypatch
|
||||
):
|
||||
from application.core import settings as settings_module
|
||||
from docsgpt.core import settings as settings_module
|
||||
|
||||
monkeypatch.setattr(
|
||||
settings_module.settings, "GRAPHRAG_ENABLED", True, raising=False
|
||||
|
||||
@@ -13,8 +13,8 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore import pgvector as pgvector_module
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore import pgvector as pgvector_module
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -15,7 +15,7 @@ from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore import pgvector as pgvector_module
|
||||
from docsgpt.vectorstore import pgvector as pgvector_module
|
||||
|
||||
CONNECTION_STRING = "postgresql://user:pass@localhost/db"
|
||||
|
||||
@@ -23,9 +23,9 @@ CONNECTION_STRING = "postgresql://user:pass@localhost/db"
|
||||
def _make_store(source_id="test-source", connection_string=CONNECTION_STRING):
|
||||
"""Build a store with every external dependency mocked, no DDL patching."""
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
) as mock_get_emb, patch(
|
||||
"application.vectorstore.pgvector.settings"
|
||||
"docsgpt.vectorstore.pgvector.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules",
|
||||
{
|
||||
@@ -42,7 +42,7 @@ def _make_store(source_id="test-source", connection_string=CONNECTION_STRING):
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
mock_settings.PGVECTOR_CONNECTION_STRING = connection_string
|
||||
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
store = PGVectorStore(
|
||||
source_id=source_id,
|
||||
@@ -64,9 +64,9 @@ def _make_store(source_id="test-source", connection_string=CONNECTION_STRING):
|
||||
class TestConstructionTouchesNothing:
|
||||
def test_init_opens_no_connection_and_runs_no_ddl(self):
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings"
|
||||
) as mock_get_emb, patch(
|
||||
"application.vectorstore.pgvector.settings"
|
||||
"docsgpt.vectorstore.pgvector.settings"
|
||||
) as mock_settings, patch.dict(
|
||||
"sys.modules",
|
||||
{
|
||||
@@ -79,7 +79,7 @@ class TestConstructionTouchesNothing:
|
||||
mock_settings.EMBEDDINGS_NAME = "test_model"
|
||||
mock_settings.PGVECTOR_CONNECTION_STRING = CONNECTION_STRING
|
||||
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
with patch.object(
|
||||
PGVectorStore, "_get_connection"
|
||||
@@ -112,8 +112,8 @@ class TestPoolSizingHasOneHome:
|
||||
"""
|
||||
|
||||
def test_both_stores_share_one_implementation(self):
|
||||
from application.graphrag import store as store_module
|
||||
from application.vectorstore import pgconn
|
||||
from docsgpt.graphrag import store as store_module
|
||||
from docsgpt.vectorstore import pgconn
|
||||
|
||||
assert pgvector_module.DEFAULT_POOL_MAX_SIZE is pgconn.DEFAULT_POOL_MAX_SIZE
|
||||
assert store_module.DEFAULT_POOL_MAX_SIZE is pgconn.DEFAULT_POOL_MAX_SIZE
|
||||
@@ -128,8 +128,8 @@ class TestPoolSizingHasOneHome:
|
||||
[(0, 0), (2, 2), (None, 8), ("4", 8), (True, 8), (-1, 8)],
|
||||
)
|
||||
def test_pool_size_is_resolved_defensively(self, monkeypatch, value, expected):
|
||||
from application.core import settings as settings_module
|
||||
from application.vectorstore import pgconn
|
||||
from docsgpt.core import settings as settings_module
|
||||
from docsgpt.vectorstore import pgconn
|
||||
|
||||
monkeypatch.setattr(
|
||||
settings_module.settings, "PGVECTOR_POOL_MAX_SIZE", value, raising=False
|
||||
@@ -176,7 +176,7 @@ class TestWritePathEnsuresSchemaOnce:
|
||||
@pytest.mark.unit
|
||||
class TestCreateSchema:
|
||||
def test_emits_the_ddl_and_leaves_the_commit_to_the_caller(self):
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
conn, cursor = MagicMock(), MagicMock()
|
||||
conn.cursor.return_value = cursor
|
||||
@@ -213,19 +213,19 @@ class TestTableDimension:
|
||||
return conn
|
||||
|
||||
def test_parses_the_declared_vector_width(self):
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
conn = self._conn([("documents",), ("vector(768)",)])
|
||||
assert PGVectorStore.table_dimension(conn) == 768
|
||||
|
||||
def test_returns_none_when_the_table_is_absent(self):
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
conn = self._conn([(None,)])
|
||||
assert PGVectorStore.table_dimension(conn) is None
|
||||
|
||||
def test_returns_none_when_the_column_type_is_not_a_vector(self):
|
||||
from application.vectorstore.pgvector import PGVectorStore
|
||||
from docsgpt.vectorstore.pgvector import PGVectorStore
|
||||
|
||||
conn = self._conn([("documents",), ("text",)])
|
||||
assert PGVectorStore.table_dimension(conn) is None
|
||||
|
||||
@@ -51,12 +51,12 @@ def _settings(mock_settings, collection="test_collection"):
|
||||
|
||||
@pytest.fixture
|
||||
def store():
|
||||
from application.vectorstore.qdrant import QdrantStore
|
||||
from docsgpt.vectorstore.qdrant import QdrantStore
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
return_value=_FakeEmbeddings(),
|
||||
), patch("application.vectorstore.qdrant.settings") as mock_settings:
|
||||
), patch("docsgpt.vectorstore.qdrant.settings") as mock_settings:
|
||||
_settings(mock_settings)
|
||||
yield QdrantStore(source_id="src-A", embeddings_key="k")
|
||||
|
||||
@@ -135,12 +135,12 @@ class TestQdrantStore:
|
||||
assert len(populated.get_chunks()) == 2
|
||||
|
||||
def test_delete_index_removes_only_this_source(self, populated):
|
||||
from application.vectorstore.qdrant import QdrantStore
|
||||
from docsgpt.vectorstore.qdrant import QdrantStore
|
||||
|
||||
with patch(
|
||||
"application.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
"docsgpt.vectorstore.base.BaseVectorStore._get_embeddings",
|
||||
return_value=_FakeEmbeddings(),
|
||||
), patch("application.vectorstore.qdrant.settings") as mock_settings:
|
||||
), patch("docsgpt.vectorstore.qdrant.settings") as mock_settings:
|
||||
_settings(mock_settings)
|
||||
other = QdrantStore(source_id="src-B", embeddings_key="k")
|
||||
# Share the in-memory backend so both sources live in one collection.
|
||||
@@ -159,9 +159,9 @@ class TestQdrantStore:
|
||||
@pytest.mark.unit
|
||||
class TestQdrantClientKwargs:
|
||||
def test_unset_settings_are_omitted(self):
|
||||
from application.vectorstore.qdrant import QdrantStore
|
||||
from docsgpt.vectorstore.qdrant import QdrantStore
|
||||
|
||||
with patch("application.vectorstore.qdrant.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.qdrant.settings") as mock_settings:
|
||||
_settings(mock_settings)
|
||||
kwargs = QdrantStore._client_kwargs()
|
||||
# location/url/path are mutually exclusive in qdrant-client, so only
|
||||
@@ -170,9 +170,9 @@ class TestQdrantClientKwargs:
|
||||
assert "url" not in kwargs and "path" not in kwargs and "host" not in kwargs
|
||||
|
||||
def test_configured_settings_are_forwarded(self):
|
||||
from application.vectorstore.qdrant import QdrantStore
|
||||
from docsgpt.vectorstore.qdrant import QdrantStore
|
||||
|
||||
with patch("application.vectorstore.qdrant.settings") as mock_settings:
|
||||
with patch("docsgpt.vectorstore.qdrant.settings") as mock_settings:
|
||||
_settings(mock_settings)
|
||||
mock_settings.QDRANT_LOCATION = None
|
||||
mock_settings.QDRANT_URL = "http://qdrant:6333"
|
||||
|
||||
@@ -8,10 +8,10 @@ the request; the overflow is dropped (lossy by design).
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from application.core.settings import settings
|
||||
from application.utils import get_encoding
|
||||
from application.vectorstore import base
|
||||
from application.vectorstore.base import RemoteEmbeddings
|
||||
from docsgpt.core.settings import settings
|
||||
from docsgpt.utils import get_encoding
|
||||
from docsgpt.vectorstore import base
|
||||
from docsgpt.vectorstore.base import RemoteEmbeddings
|
||||
|
||||
|
||||
def _capture_post(monkeypatch):
|
||||
@@ -89,20 +89,20 @@ class TestInputLimitResolution:
|
||||
"""The cap falls back to the model's own context window."""
|
||||
|
||||
def _remote(self, model_name):
|
||||
from application.vectorstore.base import RemoteEmbeddings
|
||||
from docsgpt.vectorstore.base import RemoteEmbeddings
|
||||
|
||||
return RemoteEmbeddings(
|
||||
api_url="http://embeddings", model_name=model_name, api_key=None
|
||||
)
|
||||
|
||||
def test_explicit_setting_wins(self, monkeypatch):
|
||||
from application.vectorstore import base
|
||||
from docsgpt.vectorstore import base
|
||||
|
||||
monkeypatch.setattr(base.settings, "EMBEDDINGS_MAX_INPUT_TOKENS", 123)
|
||||
assert self._remote("granite-311m")._resolve_input_limit() == 123
|
||||
|
||||
def test_registered_model_supplies_its_own_ceiling(self, monkeypatch):
|
||||
from application.vectorstore import base
|
||||
from docsgpt.vectorstore import base
|
||||
|
||||
monkeypatch.setattr(base.settings, "EMBEDDINGS_MAX_INPUT_TOKENS", None)
|
||||
monkeypatch.setattr(base, "_embeddings_name_is_explicit", lambda: True)
|
||||
@@ -117,27 +117,27 @@ class TestInputLimitResolution:
|
||||
settings default contribute mpnet's 384-token window would clip every
|
||||
chunk on a server that may well serve a 32k-context model.
|
||||
"""
|
||||
from application.vectorstore import base
|
||||
from docsgpt.vectorstore import base
|
||||
|
||||
monkeypatch.setattr(base.settings, "EMBEDDINGS_MAX_INPUT_TOKENS", None)
|
||||
monkeypatch.setattr(base, "_embeddings_name_is_explicit", lambda: False)
|
||||
assert self._remote("all-mpnet-base-v2")._resolve_input_limit() is None
|
||||
|
||||
def test_explicit_setting_still_wins_over_an_unset_name(self, monkeypatch):
|
||||
from application.vectorstore import base
|
||||
from docsgpt.vectorstore import base
|
||||
|
||||
monkeypatch.setattr(base.settings, "EMBEDDINGS_MAX_INPUT_TOKENS", 512)
|
||||
monkeypatch.setattr(base, "_embeddings_name_is_explicit", lambda: False)
|
||||
assert self._remote("all-mpnet-base-v2")._resolve_input_limit() == 512
|
||||
|
||||
def test_unknown_model_stays_unlimited(self, monkeypatch):
|
||||
from application.vectorstore import base
|
||||
from docsgpt.vectorstore import base
|
||||
|
||||
monkeypatch.setattr(base.settings, "EMBEDDINGS_MAX_INPUT_TOKENS", None)
|
||||
assert self._remote("some-org/mystery")._resolve_input_limit() is None
|
||||
|
||||
def test_non_positive_setting_falls_through_to_the_registry(self, monkeypatch):
|
||||
from application.vectorstore import base
|
||||
from docsgpt.vectorstore import base
|
||||
|
||||
monkeypatch.setattr(base.settings, "EMBEDDINGS_MAX_INPUT_TOKENS", 0)
|
||||
monkeypatch.setattr(base, "_embeddings_name_is_explicit", lambda: True)
|
||||
@@ -161,7 +161,7 @@ class TestEmbeddingsNameIsExplicit:
|
||||
"""
|
||||
|
||||
def _default(self):
|
||||
from application.core.settings import Settings
|
||||
from docsgpt.core.settings import Settings
|
||||
|
||||
return Settings.model_fields["EMBEDDINGS_NAME"].default
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from application.vectorstore.vector_creator import VectorCreator
|
||||
from docsgpt.vectorstore.vector_creator import VectorCreator
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
Reference in new issue
Block a user