chore: Review updates
This commit is contained in:
parent
854b72e931
commit
54ce79a0c9
|
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Qdrant vector store backend (`VECTOR_STORE_TYPE=qdrant`) as an optional `qdrant` extra (#683)
|
||||
|
||||
## [3.1.0] - 2026-08-25
|
||||
|
||||
### Added
|
||||
|
|
@ -380,7 +386,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
- Prometheus token tracking for deriver and dialectic operations
|
||||
- n8n integration
|
||||
- Cloud Events for auditable telemetry
|
||||
- External Vector Store support for turbopuffer, lancedb, and qdrant with reconciliation flow
|
||||
- External Vector Store support for turbopuffer and lancedb with reconciliation flow
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
|||
- Prometheus token tracking for deriver and dialectic operations
|
||||
- n8n integration
|
||||
- Cloud Events for auditable telemetry
|
||||
- External Vector Store support for turbopuffer, lancedb, and qdrant with reconciliation flow
|
||||
- External Vector Store support for turbopuffer and lancedb with reconciliation flow
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ dependencies = [
|
|||
"typing-extensions>=4.11.0",
|
||||
"json-repair>=0.49.0",
|
||||
"turbopuffer>=1.8.1",
|
||||
"qdrant-client>=1.18.0",
|
||||
"redis>=7.0.0,<8.0.0",
|
||||
"cashews[redis]==7.5.0",
|
||||
"scikit-learn>=1.6.0",
|
||||
|
|
@ -44,6 +43,9 @@ lancedb = [
|
|||
"lancedb>=0.25.3; sys_platform != \"darwin\" or platform_machine != \"x86_64\"",
|
||||
"pyarrow>=19.0.0",
|
||||
]
|
||||
qdrant = [
|
||||
"qdrant-client>=1.18.0",
|
||||
]
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8.2.2",
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ async def validate_embedding_schema(
|
|||
dims = await _introspect_pgvector_dims_with_retry(engine, schema)
|
||||
_assert_pgvector_dims_match(dims, schema=schema, target_dim=target_dim)
|
||||
|
||||
if s.VECTOR_STORE.TYPE in ("turbopuffer", "lancedb"):
|
||||
if s.VECTOR_STORE.TYPE in ("turbopuffer", "lancedb", "qdrant"):
|
||||
await _sample_external_namespaces(engine, target_dim=target_dim)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -215,7 +215,15 @@ def _create_store_by_type(store_type: str) -> VectorStore:
|
|||
|
||||
return LanceDBVectorStore()
|
||||
elif store_type == "qdrant":
|
||||
from src.vector_store.qdrant import QdrantVectorStore
|
||||
try:
|
||||
from src.vector_store.qdrant import QdrantVectorStore
|
||||
except ImportError as exc:
|
||||
raise RuntimeError(
|
||||
"VECTOR_STORE.TYPE is set to 'qdrant', but the 'qdrant-client' "
|
||||
+ "package could not be imported. Install Honcho's 'qdrant' extra "
|
||||
+ "(for example, `uv sync --extra qdrant`)"
|
||||
+ f"Original import error: {exc}"
|
||||
) from exc
|
||||
|
||||
return QdrantVectorStore()
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ class QdrantVectorStore(VectorStore):
|
|||
conditions.append(
|
||||
models.FieldCondition(key=k, match=models.MatchAny(any=v["in"])) # pyright: ignore[reportUnknownArgumentType]
|
||||
)
|
||||
elif isinstance(v, list):
|
||||
conditions.append(
|
||||
models.FieldCondition(key=k, match=models.MatchAny(any=v)) # pyright: ignore[reportUnknownArgumentType]
|
||||
)
|
||||
else:
|
||||
conditions.append(
|
||||
models.FieldCondition(key=k, match=models.MatchValue(value=v)) # pyright: ignore[reportArgumentType]
|
||||
|
|
@ -71,7 +75,7 @@ class QdrantVectorStore(VectorStore):
|
|||
models.PointStruct(
|
||||
id=_point_id(v.id),
|
||||
vector=v.embedding,
|
||||
payload={"_id": v.id, **v.metadata},
|
||||
payload={**v.metadata, "_id": v.id},
|
||||
)
|
||||
for v in vectors
|
||||
]
|
||||
|
|
@ -93,16 +97,24 @@ class QdrantVectorStore(VectorStore):
|
|||
top_k: int = 10,
|
||||
filters: dict[str, Any] | None = None,
|
||||
max_distance: float | None = None,
|
||||
include_attributes: bool | list[str] = True,
|
||||
) -> list[VectorQueryResult]:
|
||||
if not await self._client.collection_exists(namespace):
|
||||
return []
|
||||
|
||||
if include_attributes is False:
|
||||
with_payload: bool | list[str] = ["_id"]
|
||||
elif isinstance(include_attributes, list):
|
||||
with_payload = ["_id", *include_attributes]
|
||||
else:
|
||||
with_payload = True
|
||||
|
||||
response = await self._client.query_points(
|
||||
collection_name=namespace,
|
||||
query=embedding,
|
||||
limit=top_k,
|
||||
query_filter=self._build_filter(filters) if filters else None,
|
||||
with_payload=True,
|
||||
with_payload=with_payload,
|
||||
)
|
||||
|
||||
results: list[VectorQueryResult] = []
|
||||
|
|
@ -136,3 +148,20 @@ class QdrantVectorStore(VectorStore):
|
|||
|
||||
async def close(self) -> None:
|
||||
await self._client.close()
|
||||
|
||||
async def probe_namespace_dim(self, namespace: str) -> int | None:
|
||||
if not await self._client.collection_exists(namespace):
|
||||
return None
|
||||
|
||||
vectors = (await self._client.get_collection(namespace)).config.params.vectors
|
||||
if vectors is None:
|
||||
raise VectorStoreError(
|
||||
f"Qdrant collection {namespace!r} has no vector configuration"
|
||||
)
|
||||
if isinstance(vectors, dict):
|
||||
if not vectors:
|
||||
raise VectorStoreError(
|
||||
f"Qdrant collection {namespace!r} has empty named-vector configuration"
|
||||
)
|
||||
vectors = next(iter(vectors.values()))
|
||||
return int(vectors.size)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
from qdrant_client import models
|
||||
|
||||
from src.exceptions import VectorStoreError
|
||||
from src.vector_store.qdrant import (
|
||||
QdrantVectorStore,
|
||||
_point_id, # pyright: ignore[reportPrivateUsage]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def store(monkeypatch: pytest.MonkeyPatch) -> QdrantVectorStore:
|
||||
monkeypatch.setattr("src.vector_store.qdrant.AsyncQdrantClient", MagicMock())
|
||||
return QdrantVectorStore()
|
||||
|
||||
|
||||
def _mock_client(store: QdrantVectorStore) -> MagicMock:
|
||||
client = MagicMock()
|
||||
client.collection_exists = AsyncMock(return_value=True)
|
||||
client.query_points = AsyncMock(return_value=SimpleNamespace(points=[]))
|
||||
client.get_collection = AsyncMock()
|
||||
store._client = client # pyright: ignore[reportPrivateUsage]
|
||||
return client
|
||||
|
||||
|
||||
def _hit(*, id: str, score: float, payload: dict[str, Any]) -> SimpleNamespace:
|
||||
return SimpleNamespace(id=id, score=score, payload=payload)
|
||||
|
||||
|
||||
def test_point_id_is_deterministic_and_a_valid_uuid() -> None:
|
||||
assert _point_id("user_123") == _point_id("user_123")
|
||||
assert _point_id("user_123") != _point_id("user_456")
|
||||
uuid.UUID(_point_id("user_123"))
|
||||
|
||||
|
||||
def test_build_filter_membership(store: QdrantVectorStore) -> None:
|
||||
f = store._build_filter({"session_name": {"in": ["s1", "s2"]}}) # pyright: ignore[reportPrivateUsage]
|
||||
assert f is not None and f.must is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_returns_empty_when_collection_missing(
|
||||
store: QdrantVectorStore,
|
||||
) -> None:
|
||||
client = _mock_client(store)
|
||||
client.collection_exists = AsyncMock(return_value=False)
|
||||
|
||||
results = await store.query("honcho.msg.missing", [0.1, 0.2, 0.3, 0.4])
|
||||
|
||||
assert results == []
|
||||
client.query_points.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_include_attributes_false_still_recovers_id(
|
||||
store: QdrantVectorStore,
|
||||
) -> None:
|
||||
client = _mock_client(store)
|
||||
|
||||
await store.query("honcho.msg.test", [0.1, 0.2, 0.3, 0.4], include_attributes=False)
|
||||
|
||||
assert client.query_points.await_args.kwargs["with_payload"] == ["_id"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_attribute_list_projects_id_plus_listed(
|
||||
store: QdrantVectorStore,
|
||||
) -> None:
|
||||
client = _mock_client(store)
|
||||
|
||||
await store.query(
|
||||
"honcho.msg.test",
|
||||
[0.1, 0.2, 0.3, 0.4],
|
||||
include_attributes=["message_id"],
|
||||
)
|
||||
|
||||
assert client.query_points.await_args.kwargs["with_payload"] == [
|
||||
"_id",
|
||||
"message_id",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_converts_hits_to_results_with_distance_and_metadata(
|
||||
store: QdrantVectorStore,
|
||||
) -> None:
|
||||
client = _mock_client(store)
|
||||
client.query_points = AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
points=[
|
||||
_hit(
|
||||
id="<uuid-1>",
|
||||
score=0.88,
|
||||
payload={"_id": "vec_1", "message_id": "msg_1"},
|
||||
),
|
||||
_hit(id="<uuid-2>", score=0.66, payload={"_id": "vec_2"}),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
results = await store.query("honcho.msg.test", [0.1, 0.2, 0.3, 0.4])
|
||||
|
||||
assert [r.id for r in results] == ["vec_1", "vec_2"]
|
||||
assert results[0].score == 1.0 - 0.88
|
||||
assert results[1].score == 1.0 - 0.66
|
||||
assert results[0].metadata == {"message_id": "msg_1"}
|
||||
assert results[1].metadata == {}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_filters_by_max_distance(store: QdrantVectorStore) -> None:
|
||||
client = _mock_client(store)
|
||||
client.query_points = AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
points=[
|
||||
_hit(id="<uuid-1>", score=0.95, payload={"_id": "vec_close"}),
|
||||
_hit(id="<uuid-2>", score=0.1, payload={"_id": "vec_far"}),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
results = await store.query(
|
||||
"honcho.msg.test",
|
||||
[0.1, 0.2, 0.3, 0.4],
|
||||
max_distance=0.5,
|
||||
)
|
||||
|
||||
assert [r.id for r in results] == ["vec_close"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_probe_returns_none_for_missing_collection(
|
||||
store: QdrantVectorStore,
|
||||
) -> None:
|
||||
client = _mock_client(store)
|
||||
client.collection_exists = AsyncMock(return_value=False)
|
||||
|
||||
assert await store.probe_namespace_dim("does_not_exist") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_probe_returns_declared_dim(store: QdrantVectorStore) -> None:
|
||||
client = _mock_client(store)
|
||||
client.get_collection = AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
config=SimpleNamespace(
|
||||
params=SimpleNamespace(
|
||||
vectors=models.VectorParams(
|
||||
size=768, distance=models.Distance.COSINE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assert await store.probe_namespace_dim("probe_test") == 768
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_probe_raises_when_vector_config_missing(
|
||||
store: QdrantVectorStore,
|
||||
) -> None:
|
||||
client = _mock_client(store)
|
||||
client.get_collection = AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
config=SimpleNamespace(params=SimpleNamespace(vectors=None))
|
||||
)
|
||||
)
|
||||
|
||||
with pytest.raises(VectorStoreError):
|
||||
await store.probe_namespace_dim("corrupt")
|
||||
38
uv.lock
38
uv.lock
|
|
@ -1,6 +1,10 @@
|
|||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.13"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14'",
|
||||
"python_full_version < '3.14'",
|
||||
]
|
||||
|
||||
[options]
|
||||
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
|
||||
|
|
@ -822,26 +826,6 @@ dependencies = [
|
|||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/db/1d56e5f5823257b291962d6c0ce106146c6447f405b60b234c4f222a7cde/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a", size = 6055009, upload-time = "2026-03-30T08:46:46.265Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/18/c83f3cad64c5ca63bca7e91e5e46b0d026afc5af9d0a9972472ceba294b3/grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060", size = 12035295, upload-time = "2026-03-30T08:46:49.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/8e/e14966b435be2dda99fbe89db9525ea436edc79780431a1c2875a3582644/grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2", size = 6610297, upload-time = "2026-03-30T08:46:52.123Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/26/d5eb38f42ce0e3fdc8174ea4d52036ef8d58cc4426cb800f2610f625dd75/grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21", size = 7300208, upload-time = "2026-03-30T08:46:54.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/51/bd267c989f85a17a5b3eea65a6feb4ff672af41ca614e5a0279cc0ea381c/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab", size = 6813442, upload-time = "2026-03-30T08:46:57.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/d9/d80eef735b19e9169e30164bbf889b46f9df9127598a83d174eb13a48b26/grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1", size = 7414743, upload-time = "2026-03-30T08:46:59.682Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/f2/567f5bd5054398ed6b0509b9a30900376dcf2786bd936812098808b49d8d/grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106", size = 8426046, upload-time = "2026-03-30T08:47:02.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/29/73ef0141b4732ff5eacd68430ff2512a65c004696997f70476a83e548e7e/grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6", size = 7851641, upload-time = "2026-03-30T08:47:05.462Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/69/abbfa360eb229a8623bab5f5a4f8105e445bd38ce81a89514ba55d281ad0/grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440", size = 4154368, upload-time = "2026-03-30T08:47:08.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/d4/ae92206d01183b08613e846076115f5ac5991bae358d2a749fa864da5699/grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9", size = 4894235, upload-time = "2026-03-30T08:47:10.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" },
|
||||
|
|
@ -911,7 +895,6 @@ dependencies = [
|
|||
{ name = "pydantic-settings" },
|
||||
{ name = "pyjwt" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "qdrant-client" },
|
||||
{ name = "redis" },
|
||||
{ name = "rich" },
|
||||
{ name = "scikit-learn" },
|
||||
|
|
@ -928,6 +911,9 @@ lancedb = [
|
|||
{ name = "lancedb", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" },
|
||||
{ name = "pyarrow" },
|
||||
]
|
||||
qdrant = [
|
||||
{ name = "qdrant-client" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
|
|
@ -971,7 +957,7 @@ requires-dist = [
|
|||
{ name = "pydantic-settings", specifier = ">=2.10.1" },
|
||||
{ name = "pyjwt", specifier = ">=2.10.0" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||
{ name = "qdrant-client", specifier = ">=1.18.0" },
|
||||
{ name = "qdrant-client", marker = "extra == 'qdrant'", specifier = ">=1.18.0" },
|
||||
{ name = "redis", specifier = ">=7.0.0,<8.0.0" },
|
||||
{ name = "rich", specifier = ">=13.7.1" },
|
||||
{ name = "scikit-learn", specifier = ">=1.6.0" },
|
||||
|
|
@ -982,7 +968,7 @@ requires-dist = [
|
|||
{ name = "turbopuffer", specifier = ">=1.8.1" },
|
||||
{ name = "typing-extensions", specifier = ">=4.11.0" },
|
||||
]
|
||||
provides-extras = ["lancedb"]
|
||||
provides-extras = ["lancedb", "qdrant"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
|
|
@ -2484,12 +2470,6 @@ name = "pywin32"
|
|||
version = "311"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue