From d26d0635c53676200b21d5569517905203bb2e9d Mon Sep 17 00:00:00 2001 From: huanghuoguoguo <1051233107@qq.com> Date: Sat, 12 Sep 2026 19:40:30 +0800 Subject: [PATCH] fix(vector): correct SeekDB adapter semantics (#2536) --- pyproject.toml | 3 +- src/langbot/pkg/vector/vdbs/seekdb.py | 51 +++--- tests/integration/vector/test_seekdb.py | 123 +++++++++++++++ .../unit_tests/test_optional_dependencies.py | 5 +- tests/unit_tests/vector/test_seekdb.py | 96 ++++++++++++ uv.lock | 147 +++++++++--------- 6 files changed, 323 insertions(+), 102 deletions(-) create mode 100644 tests/integration/vector/test_seekdb.py create mode 100644 tests/unit_tests/vector/test_seekdb.py diff --git a/pyproject.toml b/pyproject.toml index 74bd2b7fe..1dc5e3467 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,7 +110,8 @@ classifiers = [ [project.optional-dependencies] seekdb = [ - "pyseekdb==1.1.0.post3", + "pyseekdb==1.4.0.post1", + "pylibseekdb==1.4.0; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'arm64')", ] [project.urls] diff --git a/src/langbot/pkg/vector/vdbs/seekdb.py b/src/langbot/pkg/vector/vdbs/seekdb.py index 5be28b458..cb56bd897 100644 --- a/src/langbot/pkg/vector/vdbs/seekdb.py +++ b/src/langbot/pkg/vector/vdbs/seekdb.py @@ -101,18 +101,6 @@ class SeekDBVectorDatabase(VectorDatabase): self._collection_configs: Dict[str, HNSWConfiguration] = {} self._runtime_cache_limit = runtime_cache_limit(ap) - self._escape_table = str.maketrans( - { - '\x00': '', - '\\': '\\\\', - "'": "''", # Standard SQL escaping (OceanBase NO_BACKSLASH_ESCAPES) - '"': '\\"', - '\n': '\\n', - '\r': '\\r', - '\t': '\\t', - } - ) - async def close(self) -> None: self._collections.clear() self._collection_configs.clear() @@ -192,16 +180,22 @@ class SeekDBVectorDatabase(VectorDatabase): return coll def _clean_metadata(self, meta: Dict[str, Any]) -> Dict[str, Any]: - """SeekDB metadata doesn't support \\ and ", insert will error 3104""" - return { - k: v.translate(self._escape_table) - if isinstance(v, str) - else v - if v is None or isinstance(v, (int, float, bool)) - else str(v) - for k, v in meta.items() - if v is not None - } + """Keep supported scalar metadata values without altering strings.""" + return {k: v if isinstance(v, (str, int, float, bool)) else str(v) for k, v in meta.items() if v is not None} + + @staticmethod + def _relevance_scores_to_distances(results: Dict[str, Any]) -> None: + """Convert SeekDB hybrid relevance scores to lower-is-better distances.""" + distances = results.get('distances') + if not isinstance(distances, list): + return + + results['distances'] = [ + [1.0 - float(score) if isinstance(score, (int, float)) else score for score in batch] + if isinstance(batch, list) + else batch + for batch in distances + ] async def get_or_create_collection(self, collection: str): """Get or create collection (without vector size - will use default).""" @@ -236,10 +230,10 @@ class SeekDBVectorDatabase(VectorDatabase): kwargs: Dict[str, Any] = dict(ids=ids, embeddings=embeddings_list, metadatas=cleaned_metadatas) if documents is not None: - kwargs['documents'] = [doc.translate(self._escape_table) for doc in documents] - await asyncio.to_thread(coll.add, **kwargs) + kwargs['documents'] = documents + await asyncio.to_thread(coll.upsert, **kwargs) - self.ap.logger.info(f"Added {len(ids)} embeddings to SeekDB collection '{collection}'") + self.ap.logger.info(f"Upserted {len(ids)} embeddings into SeekDB collection '{collection}'") async def search( self, @@ -287,7 +281,8 @@ class SeekDBVectorDatabase(VectorDatabase): # Route by search type. # pyseekdb's query() always requires embeddings, so full-text and # hybrid modes use hybrid_search() which supports text-only queries - # and returns the same nested-list format with distances. + # and returns relevance scores in the nested ``distances`` field. + returns_relevance_scores = False if search_type == SearchType.FULL_TEXT: if not query_text: return {'ids': [[]], 'metadatas': [[]], 'distances': [[]]} @@ -309,6 +304,7 @@ class SeekDBVectorDatabase(VectorDatabase): n_results=k, include=['documents', 'metadatas'], ) + returns_relevance_scores = True elif search_type == SearchType.HYBRID: if not query_text: @@ -352,6 +348,7 @@ class SeekDBVectorDatabase(VectorDatabase): n_results=k, include=['documents', 'metadatas'], ) + returns_relevance_scores = True self.ap.logger.info( f"SeekDB hybrid search in '{collection}' returned {len(results.get('ids', [[]])[0])} results." ) @@ -363,6 +360,8 @@ class SeekDBVectorDatabase(VectorDatabase): results = await asyncio.to_thread(coll.query, **query_kwargs) results = self._json_safe(results) + if returns_relevance_scores: + self._relevance_scores_to_distances(results) self.ap.logger.info( f"SeekDB {search_type} search in '{collection}' returned {len(results.get('ids', [[]])[0])} results" ) diff --git a/tests/integration/vector/test_seekdb.py b/tests/integration/vector/test_seekdb.py new file mode 100644 index 000000000..668072aba --- /dev/null +++ b/tests/integration/vector/test_seekdb.py @@ -0,0 +1,123 @@ +"""Real embedded SeekDB regression tests. + +Install the optional dependency before running these slow tests:: + + uv sync --dev --extra seekdb + uv run pytest tests/integration/vector/test_seekdb.py -m slow -q +""" + +from __future__ import annotations + +import asyncio +from types import SimpleNamespace +import uuid + +import pytest + +pytest.importorskip('pyseekdb') + +from langbot.pkg.vector.vdbs.seekdb import SeekDBVectorDatabase + + +pytestmark = [pytest.mark.integration, pytest.mark.slow] + + +@pytest.fixture +async def backend(tmp_path): + app = SimpleNamespace( + instance_config=SimpleNamespace( + data={ + 'vdb': { + 'runtime_cache_limit': 16, + 'seekdb': { + 'mode': 'embedded', + 'path': str(tmp_path), + 'database': 'langbot_test', + }, + } + } + ), + logger=SimpleNamespace( + info=lambda *args, **kwargs: None, + warning=lambda *args, **kwargs: None, + ), + ) + database = SeekDBVectorDatabase(app) + collection = f'test_{uuid.uuid4().hex}' + yield database, collection + + await database.delete_collection(collection) + await database.close() + + +@pytest.mark.asyncio +async def test_upsert_and_text_round_trip(backend) -> None: + database, collection = backend + original = 'He said "hello".\nC:\\notes\\file.txt isn\'t empty. 中文' + updated = f'Updated: {original}' + + await database.add_embeddings( + collection, + ['document-a'], + [[1.0, 0.0, 0.0]], + [{'file_id': 'file-a', 'text': original}], + [original], + ) + await database.add_embeddings( + collection, + ['document-a'], + [[0.0, 1.0, 0.0]], + [{'file_id': 'file-a', 'text': updated}], + [updated], + ) + + items, _ = await database.list_by_filter(collection, {'file_id': 'file-a'}) + + assert len(items) == 1 + assert items[0]['id'] == 'document-a' + assert items[0]['document'] == updated + assert items[0]['metadata']['text'] == updated + + +@pytest.mark.asyncio +async def test_full_text_and_hybrid_results_keep_relevance_order(backend) -> None: + database, collection = backend + documents = [ + 'orchid orchid orchid flower', + 'orchid grows in a garden with many other beautiful plants', + 'a completely unrelated topic', + ] + + await database.add_embeddings( + collection, + ['best', 'weak', 'noise'], + [[1.0, 0.0, 0.0], [0.9, 0.1, 0.0], [0.0, 0.0, 1.0]], + [ + {'file_id': item_id, 'document_id': item_id, 'text': document} + for item_id, document in zip(['best', 'weak', 'noise'], documents, strict=True) + ], + documents, + ) + seekdb_collection = await database.get_or_create_collection(collection) + await asyncio.to_thread(seekdb_collection.refresh_index) + + full_text = await database.search( + collection, + [1.0, 0.0, 0.0], + k=3, + search_type='full_text', + query_text='orchid', + ) + hybrid = await database.search( + collection, + [1.0, 0.0, 0.0], + k=3, + search_type='hybrid', + query_text='orchid', + vector_weight=0.65, + ) + + assert full_text['ids'][0][:2] == ['best', 'weak'] + assert full_text['distances'][0] == sorted(full_text['distances'][0]) + assert hybrid['ids'][0] == ['best', 'weak', 'noise'] + assert hybrid['distances'][0] == sorted(hybrid['distances'][0]) diff --git a/tests/unit_tests/test_optional_dependencies.py b/tests/unit_tests/test_optional_dependencies.py index 841344906..7cbeba89f 100644 --- a/tests/unit_tests/test_optional_dependencies.py +++ b/tests/unit_tests/test_optional_dependencies.py @@ -12,4 +12,7 @@ def test_seekdb_is_only_declared_as_an_optional_dependency() -> None: project = pyproject['project'] base_dependencies = project['dependencies'] assert not any(dependency.lower().startswith('pyseekdb') for dependency in base_dependencies) - assert project['optional-dependencies']['seekdb'] == ['pyseekdb==1.1.0.post3'] + assert project['optional-dependencies']['seekdb'] == [ + 'pyseekdb==1.4.0.post1', + "pylibseekdb==1.4.0; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'arm64')", + ] diff --git a/tests/unit_tests/vector/test_seekdb.py b/tests/unit_tests/vector/test_seekdb.py new file mode 100644 index 000000000..74432ab0e --- /dev/null +++ b/tests/unit_tests/vector/test_seekdb.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from langbot.pkg.vector.vdbs.seekdb import SeekDBVectorDatabase + + +def _adapter_with_collection(collection: MagicMock) -> SeekDBVectorDatabase: + adapter = SeekDBVectorDatabase.__new__(SeekDBVectorDatabase) + adapter.ap = SimpleNamespace(logger=MagicMock()) + adapter.client = MagicMock() + adapter.client.has_collection.return_value = True + adapter._collections = {'knowledge_base': collection} + adapter._runtime_cache_limit = 16 + return adapter + + +@pytest.mark.asyncio +async def test_add_embeddings_upserts_and_preserves_text() -> None: + collection = MagicMock() + adapter = _adapter_with_collection(collection) + adapter._get_or_create_collection_internal = AsyncMock(return_value=collection) + original = 'He said "hello".\nC:\\notes\\file.txt isn\'t empty. 中文' + + await adapter.add_embeddings( + collection='knowledge_base', + ids=['document-a'], + embeddings_list=[[1.0, 0.0, 0.0]], + metadatas=[{'text': original}], + documents=[original], + ) + + collection.upsert.assert_called_once_with( + ids=['document-a'], + embeddings=[[1.0, 0.0, 0.0]], + metadatas=[{'text': original}], + documents=[original], + ) + collection.add.assert_not_called() + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ('search_type', 'scores', 'expected_distances'), + [ + ('full_text', [0.4508196721, 0.25], [0.5491803279, 0.75]), + ('hybrid', [0.0328, 0.0323, 0.0159], [0.9672, 0.9677, 0.9841]), + ], +) +async def test_search_converts_relevance_scores_to_distances( + search_type: str, + scores: list[float], + expected_distances: list[float], +) -> None: + collection = MagicMock() + collection.hybrid_search.return_value = { + 'ids': [['best', 'weak', 'noise'][: len(scores)]], + 'metadatas': [[{} for _ in scores]], + 'distances': [scores], + } + adapter = _adapter_with_collection(collection) + + results = await adapter.search( + collection='knowledge_base', + query_embedding=[1.0, 0.0, 0.0], + k=len(scores), + search_type=search_type, + query_text='orchid', + vector_weight=0.65, + ) + + assert results['distances'][0] == pytest.approx(expected_distances) + assert results['distances'][0] == sorted(results['distances'][0]) + + +@pytest.mark.asyncio +async def test_vector_search_keeps_seekdb_cosine_distances() -> None: + collection = MagicMock() + collection.query.return_value = { + 'ids': [['best', 'weak']], + 'metadatas': [[{}, {}]], + 'distances': [[0.1, 0.25]], + } + adapter = _adapter_with_collection(collection) + + results = await adapter.search( + collection='knowledge_base', + query_embedding=[1.0, 0.0, 0.0], + k=2, + search_type='vector', + ) + + assert results['distances'] == [[0.1, 0.25]] diff --git a/uv.lock b/uv.lock index 51b77be61..8d718eb73 100644 --- a/uv.lock +++ b/uv.lock @@ -1066,7 +1066,7 @@ name = "cuda-bindings" version = "13.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder" }, + { name = "cuda-pathfinder", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539, upload-time = "2026-05-29T23:11:43.19Z" }, @@ -1099,34 +1099,34 @@ wheels = [ [package.optional-dependencies] cudart = [ - { name = "nvidia-cuda-runtime" }, + { name = "nvidia-cuda-runtime", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] cufft = [ - { name = "nvidia-cufft" }, + { name = "nvidia-cufft", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] cufile = [ - { name = "nvidia-cufile" }, + { name = "nvidia-cufile", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] cupti = [ - { name = "nvidia-cuda-cupti" }, + { name = "nvidia-cuda-cupti", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] curand = [ - { name = "nvidia-curand" }, + { name = "nvidia-curand", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] cusolver = [ - { name = "nvidia-cusolver" }, + { name = "nvidia-cusolver", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] cusparse = [ - { name = "nvidia-cusparse" }, + { name = "nvidia-cusparse", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] nvjitlink = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] nvrtc = [ - { name = "nvidia-cuda-nvrtc" }, + { name = "nvidia-cuda-nvrtc", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] nvtx = [ - { name = "nvidia-nvtx" }, + { name = "nvidia-nvtx", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, ] [[package]] @@ -2139,6 +2139,7 @@ dependencies = [ [package.optional-dependencies] seekdb = [ + { name = "pylibseekdb", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, { name = "pyseekdb" }, ] @@ -2203,10 +2204,11 @@ requires-dist = [ { name = "pycryptodome", specifier = ">=3.22.0" }, { name = "pydantic", specifier = ">2.0" }, { name = "pyjwt", specifier = ">=2.12.0" }, + { name = "pylibseekdb", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'seekdb') or (sys_platform == 'linux' and extra == 'seekdb')", specifier = "==1.4.0" }, { name = "pymilvus", specifier = ">=2.6.4" }, { name = "pynacl", specifier = ">=1.5.0" }, { name = "pypdf2", specifier = ">=3.0.1" }, - { name = "pyseekdb", marker = "extra == 'seekdb'", specifier = "==1.1.0.post3" }, + { name = "pyseekdb", marker = "extra == 'seekdb'", specifier = "==1.4.0.post1" }, { name = "python-docx", specifier = ">=1.1.0" }, { name = "python-multipart", specifier = ">=0.0.27" }, { name = "python-socks", specifier = ">=2.7.1" }, @@ -3297,7 +3299,7 @@ name = "nvidia-cublas" version = "13.1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cuda-nvrtc" }, + { name = "nvidia-cuda-nvrtc", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, @@ -3336,7 +3338,7 @@ name = "nvidia-cudnn-cu13" version = "9.20.0.48" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, + { name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, @@ -3348,7 +3350,7 @@ name = "nvidia-cufft" version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, @@ -3378,9 +3380,9 @@ name = "nvidia-cusolver" version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, - { name = "nvidia-cusparse" }, - { name = "nvidia-nvjitlink" }, + { name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, @@ -3392,7 +3394,7 @@ name = "nvidia-cusparse" version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, @@ -4482,21 +4484,18 @@ crypto = [ [[package]] name = "pylibseekdb" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pymysql", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] wheels = [ - { url = "https://files.pythonhosted.org/packages/23/1e/5d971387d4bcdcf0f6f3c85d681a207c49f20715cf566a88d2222e5cd4c0/pylibseekdb-1.3.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:1d33cf82f34339bc58ac160688fc7d15ac2f7cbb226338d3887fe8350f65b762", size = 142749176, upload-time = "2026-05-25T08:59:18.118Z" }, - { url = "https://files.pythonhosted.org/packages/4d/9e/47f4a1ebad7e95169cfff1b87433b38623cc68426b3dfaac244c2492e5d4/pylibseekdb-1.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:77ba6786908cd8ab320ed4e5d5ef352759ef8990d72aff913467db5fe32542c4", size = 140878003, upload-time = "2026-05-25T06:11:51.929Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/c772c15444ddec07365c5728624824b7b2137c319398c3cfc44d2e6b09a3/pylibseekdb-1.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:4b127c21ac1178ab903735041b6afe25295731d7bcee9813e5e1576c9d384937", size = 160132660, upload-time = "2026-05-25T06:12:02.817Z" }, - { url = "https://files.pythonhosted.org/packages/60/e8/d53bb80f6ed27f19dfb5b2f996cf9bef0e054442d473493e4f2425265762/pylibseekdb-1.3.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:23cd6ad60a80543dfccb4dc9500401347b82fddb8cef10f5503e5eb816adb39f", size = 142736028, upload-time = "2026-05-25T08:59:41.571Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e6/3811303e0740e45dd475e6cf8ccea2abb706f047e50455ec1834bdeb6068/pylibseekdb-1.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ec2465e206574f5dee7870bde2434a5ab9a03c2001786b1765fcb5dd790d6f98", size = 140881851, upload-time = "2026-05-25T06:12:11.973Z" }, - { url = "https://files.pythonhosted.org/packages/5d/29/856ea807cbe997c9fe2df6257106b2b2924ef9458bf87db7e4bd0b8dec03/pylibseekdb-1.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1b78f26dfbb80157169b81f22ebb80957e3c6ee7b33e5ff35beaa4d628c33915", size = 160133328, upload-time = "2026-05-25T06:12:22.051Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f1/5ec7782810746e9c065a419e8105a5925b3b04f495296b507706da9dc3b3/pylibseekdb-1.3.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:f6f739454aff786beeccfe71b66a0d89d01b5a8a260e0b8c5c30f8e9184bd88a", size = 142743219, upload-time = "2026-05-25T09:00:08.798Z" }, - { url = "https://files.pythonhosted.org/packages/13/8a/4d8150f6ad5f11dca40a6d42df9e2a41ed47125735a49afc7d2528460cd3/pylibseekdb-1.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:89069e1aeeb51f61aeaa0cf5d94bedb918f46c3476d7b30183dde7b2101e5954", size = 140884366, upload-time = "2026-05-25T06:12:31.689Z" }, - { url = "https://files.pythonhosted.org/packages/46/29/0583f2e00dbad80efffd7cb7df6431bd086b01a94d8b69688bae15a52e84/pylibseekdb-1.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:2515ea14bbac59e6f9f90a43bbaf179050ad7f8ab683d1cb9fd7fe225ccdca4e", size = 160137143, upload-time = "2026-05-25T06:12:43.005Z" }, - { url = "https://files.pythonhosted.org/packages/ad/5d/8c9afc77d32adbb1f7af85c3131419bcc9860677c5d6efb2d8d0ae9a7a66/pylibseekdb-1.3.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:a4177a3a6369699c9791cef3a7bfe7b472af301352237ed6e4cea42034fc0047", size = 142739982, upload-time = "2026-05-25T09:00:26.672Z" }, - { url = "https://files.pythonhosted.org/packages/56/91/bd3f9dea464cc22b454bbe384df3423e36e9fcbe7b1779c861f7ca9721e3/pylibseekdb-1.3.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:8651b8e0324fa78a5ed93b9952f4140c968655c344ef11fdb20d754077efeb05", size = 140896377, upload-time = "2026-05-25T06:12:53.468Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f4/fcf930ed8c6d40154f41edfb2054794c786dd66deced3a8cc3fef5898af7/pylibseekdb-1.3.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e6e58bce51e709c46aae3891e723b786132da925b9b6362db4486c07044d99e8", size = 160135373, upload-time = "2026-05-25T06:13:03.535Z" }, + { url = "https://files.pythonhosted.org/packages/ae/a8/7413d33218aff55a14ec9d20532b49243ffd0579e7a92244922c1885444e/pylibseekdb-1.4.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5cb2efab9f1321cdb4b034d3a2bd92e41a402fc95e7dc9579c7473a426f96e24", size = 52173499, upload-time = "2026-08-27T13:05:09.347Z" }, + { url = "https://files.pythonhosted.org/packages/64/93/e9a13b996b5561f89c9a4f1b62796f8a6230a5dce215869e3cfef8adc4f1/pylibseekdb-1.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e37b931417b7fc7fc88d15fd8b9b0dad499cd05e693cc483aa3743840e85f0c0", size = 49442143, upload-time = "2026-08-27T13:03:41.823Z" }, + { url = "https://files.pythonhosted.org/packages/71/cd/e54bb304512042cac0514fd607175f5425bd0924330e3eb74937c2afe827/pylibseekdb-1.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:6aaa3c9e4865d32f533af04eb8eab06d2c10fc581ac38097d618efb44c05dc5b", size = 53975703, upload-time = "2026-08-27T13:04:41.008Z" }, + { url = "https://files.pythonhosted.org/packages/ca/03/4380094699cbd4539971c0b943701f776408c94c28cc3ecdaed7c217bb29/pylibseekdb-1.4.0-cp312-abi3-macosx_15_0_arm64.whl", hash = "sha256:2fee55af299f2992dd5d61c9e239ef8855629f4117ee8b4c21dc877160707004", size = 52171602, upload-time = "2026-08-27T13:05:15.263Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/ea71acbee58925a51cb1a7137afd2d1c4e3fbb5bf2a0144cd53d299a20df/pylibseekdb-1.4.0-cp312-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f606579904a19bcd7ec96bc117251db9d4202485fc7355f2a289804d1b3b2c1b", size = 49438937, upload-time = "2026-08-27T13:03:48.418Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f3/452485e45676a7d738720a7a3f7abbf4d24a3d272cbacabef41ae8b8e52c/pylibseekdb-1.4.0-cp312-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d32d6f0d3b92b0c719b6c4230a24748ce10666f67052c696359e69138d8fbe7", size = 53972507, upload-time = "2026-08-27T13:04:46.946Z" }, ] [[package]] @@ -4621,7 +4620,7 @@ wheels = [ [[package]] name = "pyseekdb" -version = "1.1.0.post3" +version = "1.4.0.post1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx", marker = "python_full_version < '3.14'" }, @@ -4635,7 +4634,7 @@ dependencies = [ { name = "tqdm", marker = "python_full_version < '3.14'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/58/6e/2373239ab80c35a17aa14e8219727f06567e91d3b7f1b8c36d28ce94d04b/pyseekdb-1.1.0.post3-py3-none-any.whl", hash = "sha256:0437c9a4de72be44eb24b070b2b8099086467c08af10a57191498a61257a4bfb", size = 110985, upload-time = "2026-02-12T14:19:05.402Z" }, + { url = "https://files.pythonhosted.org/packages/22/87/d5dd862faa3d4adf3847c1ce19c3ea5ecd0dcfda9c2584a95bfd2b0fac0f/pyseekdb-1.4.0.post1-py3-none-any.whl", hash = "sha256:a3379f6962a0c01aa029d3e5a8f0c0f5a59b27a689b8aae1931d9ce5563f252c", size = 158375, upload-time = "2026-08-03T08:56:59.501Z" }, ] [[package]] @@ -5256,10 +5255,10 @@ name = "scikit-learn" version = "1.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, + { name = "joblib", marker = "python_full_version >= '3.14'" }, + { name = "numpy", marker = "python_full_version >= '3.14'" }, + { name = "scipy", marker = "python_full_version >= '3.14'" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } wheels = [ @@ -5306,7 +5305,7 @@ name = "scipy" version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -5377,14 +5376,14 @@ name = "sentence-transformers" version = "5.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "typing-extensions" }, + { name = "huggingface-hub", marker = "python_full_version >= '3.14'" }, + { name = "numpy", marker = "python_full_version >= '3.14'" }, + { name = "scikit-learn", marker = "python_full_version >= '3.14'" }, + { name = "scipy", marker = "python_full_version >= '3.14'" }, + { name = "torch", marker = "python_full_version >= '3.14'" }, + { name = "tqdm", marker = "python_full_version >= '3.14'" }, + { name = "transformers", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/30/21664028fc0776eb1ca024879480bbbab36f02923a8ff9e4cae5a150fa35/sentence_transformers-5.2.3.tar.gz", hash = "sha256:3cd3044e1f3fe859b6a1b66336aac502eaae5d3dd7d5c8fc237f37fbf58137c7", size = 381623, upload-time = "2026-02-17T14:05:20.238Z" } wheels = [ @@ -5757,21 +5756,21 @@ name = "torch" version = "2.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, + { name = "cuda-bindings", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "filelock", marker = "python_full_version >= '3.14'" }, + { name = "fsspec", marker = "python_full_version >= '3.14'" }, + { name = "jinja2", marker = "python_full_version >= '3.14'" }, + { name = "networkx", marker = "python_full_version >= '3.14'" }, + { name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "python_full_version >= '3.14'" }, + { name = "sympy", marker = "python_full_version >= '3.14'" }, + { name = "triton", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/59/38/7028d3be540f1dcdf41660a2b01d0c51d2cb73915fe370d84e4d277a6d47/torch-2.12.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ef81f503912effea2ce3d9b12a2e3a6ed488943e91271c90c7a829f60baf6aa2", size = 87975425, upload-time = "2026-06-17T21:08:34.094Z" }, @@ -5813,15 +5812,15 @@ name = "transformers" version = "5.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, + { name = "huggingface-hub", marker = "python_full_version >= '3.14'" }, + { name = "numpy", marker = "python_full_version >= '3.14'" }, + { name = "packaging", marker = "python_full_version >= '3.14'" }, + { name = "pyyaml", marker = "python_full_version >= '3.14'" }, + { name = "regex", marker = "python_full_version >= '3.14'" }, + { name = "safetensors", marker = "python_full_version >= '3.14'" }, + { name = "tokenizers", marker = "python_full_version >= '3.14'" }, + { name = "tqdm", marker = "python_full_version >= '3.14'" }, + { name = "typer", marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" } wheels = [ @@ -6082,9 +6081,9 @@ name = "valkey-glide" version = "2.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, - { name = "protobuf" }, - { name = "sniffio" }, + { name = "anyio", marker = "sys_platform != 'win32'" }, + { name = "protobuf", marker = "sys_platform != 'win32'" }, + { name = "sniffio", marker = "sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/72/a2/582b34c6acc8dc857c537f6007459cba48dfa0dc404789a657e5c1a998c0/valkey_glide-2.4.1.tar.gz", hash = "sha256:f1155d84156d11b90488aa67e90102f0bf98a45314f5b99308ac9074c05f7241", size = 898030, upload-time = "2026-05-28T21:41:55.881Z" } wheels = [