vectorizer-sdk 3.0.0__tar.gz → 3.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {vectorizer_sdk-3.0.0/vectorizer_sdk.egg-info → vectorizer_sdk-3.0.3}/PKG-INFO +1 -1
  2. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/pyproject.toml +110 -110
  3. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/utils/http_client.py +9 -1
  4. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/_base.py +19 -1
  5. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/auth.py +27 -1
  6. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/client.py +10 -1
  7. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/vectors.py +47 -0
  8. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3/vectorizer_sdk.egg-info}/PKG-INFO +1 -1
  9. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/LICENSE +0 -0
  10. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/README.md +0 -0
  11. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/__init__.py +0 -0
  12. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/_codec.py +0 -0
  13. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/async_client.py +0 -0
  14. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/commands.py +0 -0
  15. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/endpoint.py +0 -0
  16. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/pool.py +0 -0
  17. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/sync_client.py +0 -0
  18. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/rpc/types.py +0 -0
  19. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/setup.cfg +0 -0
  20. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_client_integration.py +0 -0
  21. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_discovery.py +0 -0
  22. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_exceptions.py +0 -0
  23. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_file_operations.py +0 -0
  24. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_file_upload.py +0 -0
  25. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_graph.py +0 -0
  26. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_http_client.py +0 -0
  27. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_intelligent_search.py +0 -0
  28. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_mock_transport.py +0 -0
  29. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_models.py +0 -0
  30. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_qdrant_advanced.py +0 -0
  31. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_routing.py +0 -0
  32. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_sdk_comprehensive.py +0 -0
  33. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_simple.py +0 -0
  34. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_umicp.py +0 -0
  35. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/tests/test_validation.py +0 -0
  36. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/utils/__init__.py +0 -0
  37. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/utils/transport.py +0 -0
  38. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/utils/umicp_client.py +0 -0
  39. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/utils/validation.py +0 -0
  40. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/__init__.py +0 -0
  41. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/admin.py +0 -0
  42. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/collections.py +0 -0
  43. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/graph.py +0 -0
  44. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer/search.py +0 -0
  45. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer_sdk.egg-info/SOURCES.txt +0 -0
  46. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer_sdk.egg-info/dependency_links.txt +0 -0
  47. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer_sdk.egg-info/entry_points.txt +0 -0
  48. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer_sdk.egg-info/requires.txt +0 -0
  49. {vectorizer_sdk-3.0.0 → vectorizer_sdk-3.0.3}/vectorizer_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectorizer_sdk
3
- Version: 3.0.0
3
+ Version: 3.0.3
4
4
  Summary: Python SDK for Vectorizer - Semantic search, vector operations, and the VectorizerRPC binary transport (default in v3.x)
5
5
  Author-email: HiveLLM Team <team@hivellm.org>
6
6
  License: Apache-2.0
@@ -1,110 +1,110 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "vectorizer_sdk"
7
- version = "3.0.0"
8
- description = "Python SDK for Vectorizer - Semantic search, vector operations, and the VectorizerRPC binary transport (default in v3.x)"
9
- readme = "README.md"
10
- requires-python = ">=3.8"
11
- license = {text = "Apache-2.0"}
12
- authors = [
13
- {name = "HiveLLM Team", email = "team@hivellm.org"}
14
- ]
15
- keywords = [
16
- "vectorizer",
17
- "semantic-search",
18
- "embeddings",
19
- "machine-learning",
20
- "ai",
21
- "search",
22
- "vectors",
23
- "similarity",
24
- "hivellm",
25
- "umicp"
26
- ]
27
- classifiers = [
28
- "Development Status :: 5 - Production/Stable",
29
- "Intended Audience :: Developers",
30
- "Operating System :: OS Independent",
31
- "Programming Language :: Python :: 3",
32
- "Programming Language :: Python :: 3.8",
33
- "Programming Language :: Python :: 3.9",
34
- "Programming Language :: Python :: 3.10",
35
- "Programming Language :: Python :: 3.11",
36
- "Programming Language :: Python :: 3.12",
37
- "Topic :: Software Development :: Libraries :: Python Modules",
38
- "Topic :: Scientific/Engineering :: Artificial Intelligence",
39
- "Topic :: Text Processing :: Indexing",
40
- "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
41
- ]
42
-
43
- dependencies = [
44
- "aiohttp>=3.10.0",
45
- "msgpack>=1.1.0",
46
- ]
47
-
48
- [project.optional-dependencies]
49
- umicp = [
50
- "umicp-python>=0.1.3",
51
- ]
52
- dev = [
53
- "pytest>=8.0.0",
54
- "pytest-asyncio>=0.24.0",
55
- "pytest-cov>=5.0.0",
56
- "black>=25.0.0",
57
- "isort>=5.13.0",
58
- "flake8>=7.0.0",
59
- "mypy>=1.11.0",
60
- "pre-commit>=4.0.0",
61
- ]
62
- docs = [
63
- "sphinx>=7.0.0",
64
- "sphinx-rtd-theme>=2.0.0",
65
- "myst-parser>=3.0.0",
66
- ]
67
- test = [
68
- "pytest>=8.0.0",
69
- "pytest-asyncio>=0.24.0",
70
- "pytest-cov>=5.0.0",
71
- "httpx>=0.27.0",
72
- ]
73
-
74
- [project.urls]
75
- Homepage = "https://github.com/hivellm/vectorizer"
76
- Documentation = "https://github.com/hivellm/vectorizer/tree/main/docs"
77
- Repository = "https://github.com/hivellm/vectorizer"
78
- Issues = "https://github.com/hivellm/vectorizer/issues"
79
-
80
- [project.scripts]
81
- vectorizer-cli = "cli:main"
82
-
83
- [tool.setuptools.packages.find]
84
- include = ["*"]
85
- exclude = ["tests*", "examples*", "docs*"]
86
-
87
- [tool.setuptools.package-data]
88
- "*" = ["py.typed"]
89
-
90
- [tool.pytest.ini_options]
91
- asyncio_mode = "auto"
92
- testpaths = ["tests"]
93
- python_files = ["test_*.py"]
94
- python_classes = ["Test*"]
95
- python_functions = ["test_*"]
96
-
97
- [tool.black]
98
- line-length = 100
99
- target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
100
-
101
- [tool.isort]
102
- profile = "black"
103
- line_length = 100
104
-
105
- [tool.mypy]
106
- python_version = "3.8"
107
- warn_return_any = true
108
- warn_unused_configs = true
109
- disallow_untyped_defs = false
110
-
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vectorizer_sdk"
7
+ version = "3.0.3"
8
+ description = "Python SDK for Vectorizer - Semantic search, vector operations, and the VectorizerRPC binary transport (default in v3.x)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = {text = "Apache-2.0"}
12
+ authors = [
13
+ {name = "HiveLLM Team", email = "team@hivellm.org"}
14
+ ]
15
+ keywords = [
16
+ "vectorizer",
17
+ "semantic-search",
18
+ "embeddings",
19
+ "machine-learning",
20
+ "ai",
21
+ "search",
22
+ "vectors",
23
+ "similarity",
24
+ "hivellm",
25
+ "umicp"
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 5 - Production/Stable",
29
+ "Intended Audience :: Developers",
30
+ "Operating System :: OS Independent",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.8",
33
+ "Programming Language :: Python :: 3.9",
34
+ "Programming Language :: Python :: 3.10",
35
+ "Programming Language :: Python :: 3.11",
36
+ "Programming Language :: Python :: 3.12",
37
+ "Topic :: Software Development :: Libraries :: Python Modules",
38
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
39
+ "Topic :: Text Processing :: Indexing",
40
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
41
+ ]
42
+
43
+ dependencies = [
44
+ "aiohttp>=3.10.0",
45
+ "msgpack>=1.1.0",
46
+ ]
47
+
48
+ [project.optional-dependencies]
49
+ umicp = [
50
+ "umicp-python>=0.1.3",
51
+ ]
52
+ dev = [
53
+ "pytest>=8.0.0",
54
+ "pytest-asyncio>=0.24.0",
55
+ "pytest-cov>=5.0.0",
56
+ "black>=25.0.0",
57
+ "isort>=5.13.0",
58
+ "flake8>=7.0.0",
59
+ "mypy>=1.11.0",
60
+ "pre-commit>=4.0.0",
61
+ ]
62
+ docs = [
63
+ "sphinx>=7.0.0",
64
+ "sphinx-rtd-theme>=2.0.0",
65
+ "myst-parser>=3.0.0",
66
+ ]
67
+ test = [
68
+ "pytest>=8.0.0",
69
+ "pytest-asyncio>=0.24.0",
70
+ "pytest-cov>=5.0.0",
71
+ "httpx>=0.27.0",
72
+ ]
73
+
74
+ [project.urls]
75
+ Homepage = "https://github.com/hivellm/vectorizer"
76
+ Documentation = "https://github.com/hivellm/vectorizer/tree/main/docs"
77
+ Repository = "https://github.com/hivellm/vectorizer"
78
+ Issues = "https://github.com/hivellm/vectorizer/issues"
79
+
80
+ [project.scripts]
81
+ vectorizer-cli = "cli:main"
82
+
83
+ [tool.setuptools.packages.find]
84
+ include = ["*"]
85
+ exclude = ["tests*", "examples*", "docs*"]
86
+
87
+ [tool.setuptools.package-data]
88
+ "*" = ["py.typed"]
89
+
90
+ [tool.pytest.ini_options]
91
+ asyncio_mode = "auto"
92
+ testpaths = ["tests"]
93
+ python_files = ["test_*.py"]
94
+ python_classes = ["Test*"]
95
+ python_functions = ["test_*"]
96
+
97
+ [tool.black]
98
+ line-length = 100
99
+ target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
100
+
101
+ [tool.isort]
102
+ profile = "black"
103
+ line_length = 100
104
+
105
+ [tool.mypy]
106
+ python_version = "3.8"
107
+ warn_return_any = true
108
+ warn_unused_configs = true
109
+ disallow_untyped_defs = false
110
+
@@ -53,7 +53,15 @@ class HTTPClient:
53
53
  if self._session is None or self._session.closed:
54
54
  headers = {"Content-Type": "application/json"}
55
55
  if self.api_key:
56
- headers["Authorization"] = f"Bearer {self.api_key}"
56
+ # JWT shape → `Authorization: Bearer`; anything else
57
+ # (raw API keys from `POST /auth/keys`) → `X-API-Key`.
58
+ # Server middleware won't fall back Bearer → api_key,
59
+ # so routing has to happen client-side.
60
+ parts = self.api_key.split(".")
61
+ if len(parts) == 3 and all(p for p in parts):
62
+ headers["Authorization"] = f"Bearer {self.api_key}"
63
+ else:
64
+ headers["X-API-Key"] = self.api_key
57
65
 
58
66
  timeout_config = aiohttp.ClientTimeout(total=self.timeout)
59
67
  self._session = aiohttp.ClientSession(
@@ -112,6 +112,22 @@ class RestTransport(Transport):
112
112
  await self._http.close()
113
113
 
114
114
 
115
+ def _looks_like_jwt(token: str) -> bool:
116
+ """Cheap JWT shape sniff: three non-empty base64url-encoded segments
117
+ joined by ``.``. Raw Vectorizer API keys (from ``POST /auth/keys``)
118
+ are a single alphanumeric string and fail this check, so they get
119
+ routed to ``X-API-Key`` rather than ``Authorization: Bearer``.
120
+
121
+ The server's auth middleware treats every ``Bearer`` string as a
122
+ JWT and never falls back to the API-key validator, so sending a
123
+ raw key under ``Bearer`` silently 401s. This sniff keeps the public
124
+ ``api_key`` config field unchanged while routing each credential
125
+ down the header the server actually accepts.
126
+ """
127
+ parts = token.split(".")
128
+ return len(parts) == 3 and all(p for p in parts)
129
+
130
+
115
131
  @dataclass
116
132
  class AuthState:
117
133
  """Bearer-token + API-key state shared across surface clients."""
@@ -121,7 +137,9 @@ class AuthState:
121
137
  def headers(self) -> dict:
122
138
  if not self.api_key:
123
139
  return {}
124
- return {"Authorization": f"Bearer {self.api_key}"}
140
+ if _looks_like_jwt(self.api_key):
141
+ return {"Authorization": f"Bearer {self.api_key}"}
142
+ return {"X-API-Key": self.api_key}
125
143
 
126
144
 
127
145
  @dataclass
@@ -22,7 +22,8 @@ class AuthClient(_ApiBase):
22
22
 
23
23
  from vectorizer import RestTransport, AuthClient
24
24
  auth = AuthClient(RestTransport("http://localhost:15002"), api_key="sk-...")
25
- print(auth.headers()) # -> {"Authorization": "Bearer sk-..."}
25
+ print(auth.headers()) # raw API key → {"X-API-Key": "sk-..."}
26
+ # # JWT → {"Authorization": "Bearer <jwt>"}
26
27
 
27
28
  In the :class:`vectorizer.VectorizerClient` facade this is the
28
29
  surface that owns the API key and surfaces it via :attr:`api_key`
@@ -47,3 +48,28 @@ class AuthClient(_ApiBase):
47
48
  def set_api_key(self, api_key: Optional[str]) -> None:
48
49
  """Replace the current API key — affects future request headers."""
49
50
  self._auth.api_key = api_key
51
+
52
+ async def login(self, username: str, password: str) -> str:
53
+ """Exchange ``(username, password)`` for a JWT via
54
+ ``POST /auth/login`` and return the raw ``access_token``.
55
+
56
+ The token is **not** stored on ``self`` — to use it for
57
+ subsequent requests, call :meth:`set_api_key` with the token
58
+ (``AuthState.headers`` sniffs the JWT shape and routes it onto
59
+ ``Authorization: Bearer …`` automatically).
60
+
61
+ When the server runs with ``auth.enabled: false`` this
62
+ endpoint returns 404 — dev servers without auth don't need
63
+ to call :meth:`login` at all.
64
+ """
65
+ data = await self._transport.post(
66
+ "/auth/login",
67
+ {"username": username, "password": password},
68
+ )
69
+ token = data.get("access_token") if isinstance(data, dict) else None
70
+ if not isinstance(token, str) or not token:
71
+ raise ValueError(
72
+ "login response missing `access_token`; server payload: "
73
+ f"{data!r}"
74
+ )
75
+ return token
@@ -274,7 +274,16 @@ class VectorizerClient:
274
274
  if self._session is None or self._session.closed:
275
275
  headers = {}
276
276
  if self.api_key:
277
- headers["Authorization"] = f"Bearer {self.api_key}"
277
+ # Route JWTs to `Authorization: Bearer` and raw API
278
+ # keys to `X-API-Key`. Server middleware treats
279
+ # every Bearer string as a JWT and won't fall back
280
+ # to the API-key validator — sending a raw key as
281
+ # Bearer silently 401s on every gated route.
282
+ from ._base import _looks_like_jwt
283
+ if _looks_like_jwt(self.api_key):
284
+ headers["Authorization"] = f"Bearer {self.api_key}"
285
+ else:
286
+ headers["X-API-Key"] = self.api_key
278
287
 
279
288
  timeout = aiohttp.ClientTimeout(total=self.timeout)
280
289
  self._session = aiohttp.ClientSession(
@@ -112,6 +112,15 @@ class VectorsClient(_ApiBase):
112
112
  """
113
113
  Get a specific vector by ID.
114
114
 
115
+ .. warning::
116
+ **Server caveat (observed on ``hivehub/vectorizer:3.0.x``):**
117
+ this endpoint currently returns HTTP 200 with a synthetic
118
+ uniform-vector payload (``[0.1, 0.1, ...]``) even for ids
119
+ that don't exist. Callers that need real miss detection
120
+ should probe via :meth:`list_vectors` or search and not
121
+ trust a successful response as proof of existence until
122
+ the server fix ships.
123
+
115
124
  Args:
116
125
  collection: Collection name
117
126
  vector_id: Vector ID
@@ -125,6 +134,44 @@ class VectorsClient(_ApiBase):
125
134
  ServerError: If service returns error
126
135
  """
127
136
  return await self._transport.get(f"/collections/{collection}/vectors/{vector_id}")
137
+
138
+ async def insert_text_batch(
139
+ self,
140
+ collection: str,
141
+ texts: List[Dict[str, Any]],
142
+ ) -> Dict[str, Any]:
143
+ """Insert a batch of **texts** into a collection. The server
144
+ embeds each entry with the collection's configured provider
145
+ (BM25 default, FastEmbed ONNX when selected in ``config.yml``).
146
+
147
+ Wire contract: ``POST /insert_texts`` with
148
+ ``{"collection": ..., "texts": [...]}`` payload. The collection
149
+ is a top-level JSON field, **not** a path segment.
150
+
151
+ Per-entry ``id``: the server **reassigns** every inserted
152
+ vector a server-generated UUID. The original client id
153
+ round-trips on the response as ``client_id``. Callers that
154
+ need idempotency by client id should key off the returned
155
+ ``results[].client_id``, not the server-assigned UUID.
156
+
157
+ Distinct from :meth:`insert_texts` on this class, which takes
158
+ :class:`Vector` objects with pre-computed embeddings and hits
159
+ the raw-vector write path.
160
+
161
+ Args:
162
+ collection: Collection name.
163
+ texts: Entries of shape
164
+ ``{"id": "...", "text": "...", "metadata": {...}}``.
165
+
166
+ Returns:
167
+ Raw ``/insert_texts`` response dict with fields
168
+ ``collection``, ``count``, ``inserted``, ``failed``,
169
+ ``results``.
170
+ """
171
+ if not texts:
172
+ raise ValidationError("texts list cannot be empty")
173
+ payload = {"collection": collection, "texts": texts}
174
+ return await self._transport.post("/insert_texts", data=payload)
128
175
  async def delete_vectors(self, collection: str, vector_ids: List[str]) -> bool:
129
176
  """
130
177
  Delete vectors from a collection.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectorizer_sdk
3
- Version: 3.0.0
3
+ Version: 3.0.3
4
4
  Summary: Python SDK for Vectorizer - Semantic search, vector operations, and the VectorizerRPC binary transport (default in v3.x)
5
5
  Author-email: HiveLLM Team <team@hivellm.org>
6
6
  License: Apache-2.0
File without changes
File without changes
File without changes