embed-client 0.0.1__py3-none-any.whl → 1.0.0.1__py3-none-any.whl
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.
- embed_client/async_client.py +101 -15
- embed_client/example_async_usage.py +28 -53
- {embed_client-0.0.1.dist-info → embed_client-1.0.0.1.dist-info}/METADATA +1 -1
- embed_client-1.0.0.1.dist-info/RECORD +8 -0
- {embed_client-0.0.1.dist-info → embed_client-1.0.0.1.dist-info}/WHEEL +1 -1
- embed_client-0.0.1.dist-info/RECORD +0 -8
- {embed_client-0.0.1.dist-info → embed_client-1.0.0.1.dist-info}/top_level.txt +0 -0
embed_client/async_client.py
CHANGED
@@ -8,6 +8,7 @@ Async client for Embedding Service API (OpenAPI 3.0.2)
|
|
8
8
|
|
9
9
|
from typing import Any, Dict, List, Optional, Union
|
10
10
|
import aiohttp
|
11
|
+
import os
|
11
12
|
|
12
13
|
class EmbeddingServiceError(Exception):
|
13
14
|
"""Base exception for EmbeddingServiceAsyncClient."""
|
@@ -37,13 +38,13 @@ class EmbeddingServiceAsyncClient:
|
|
37
38
|
Raises:
|
38
39
|
ValueError: If base_url or port is not provided.
|
39
40
|
"""
|
40
|
-
def __init__(self, base_url: str, port: int):
|
41
|
-
|
41
|
+
def __init__(self, base_url: Optional[str] = None, port: Optional[int] = None):
|
42
|
+
self.base_url = base_url or os.getenv("EMBEDDING_SERVICE_BASE_URL", "http://localhost")
|
43
|
+
if not self.base_url:
|
42
44
|
raise ValueError("base_url must be provided.")
|
43
|
-
|
45
|
+
self.port = port or int(os.getenv("EMBEDDING_SERVICE_PORT", "8001"))
|
46
|
+
if self.port is None:
|
44
47
|
raise ValueError("port must be provided.")
|
45
|
-
self.base_url = base_url.rstrip("/")
|
46
|
-
self.port = port
|
47
48
|
self._session: Optional[aiohttp.ClientSession] = None
|
48
49
|
|
49
50
|
def _make_url(self, path: str, base_url: Optional[str] = None, port: Optional[int] = None) -> str:
|
@@ -51,6 +52,23 @@ class EmbeddingServiceAsyncClient:
|
|
51
52
|
port_val = port if port is not None else self.port
|
52
53
|
return f"{url}:{port_val}{path}"
|
53
54
|
|
55
|
+
def _format_error_response(self, error: str, lang: Optional[str] = None, text: Optional[str] = None) -> Dict[str, Any]:
|
56
|
+
"""
|
57
|
+
Format error response in a standard way.
|
58
|
+
Args:
|
59
|
+
error (str): Error message
|
60
|
+
lang (str, optional): Language of the text that caused the error
|
61
|
+
text (str, optional): Text that caused the error
|
62
|
+
Returns:
|
63
|
+
dict: Formatted error response
|
64
|
+
"""
|
65
|
+
response = {"error": f"Embedding service error: {error}"}
|
66
|
+
if lang is not None:
|
67
|
+
response["lang"] = lang
|
68
|
+
if text is not None:
|
69
|
+
response["text"] = text
|
70
|
+
return response
|
71
|
+
|
54
72
|
async def __aenter__(self):
|
55
73
|
self._session = aiohttp.ClientSession()
|
56
74
|
return self
|
@@ -135,6 +153,37 @@ class EmbeddingServiceAsyncClient:
|
|
135
153
|
except Exception as e:
|
136
154
|
raise EmbeddingServiceError(f"Unexpected error: {e}") from e
|
137
155
|
|
156
|
+
def _validate_texts(self, texts: List[str]) -> None:
|
157
|
+
"""
|
158
|
+
Validate input texts before sending to the API.
|
159
|
+
Args:
|
160
|
+
texts (List[str]): List of texts to validate
|
161
|
+
Raises:
|
162
|
+
EmbeddingServiceAPIError: If texts are invalid
|
163
|
+
"""
|
164
|
+
if not texts:
|
165
|
+
raise EmbeddingServiceAPIError({
|
166
|
+
"code": -32602,
|
167
|
+
"message": "Empty texts list provided"
|
168
|
+
})
|
169
|
+
|
170
|
+
invalid_texts = []
|
171
|
+
for i, text in enumerate(texts):
|
172
|
+
if not isinstance(text, str):
|
173
|
+
invalid_texts.append(f"Text at index {i} is not a string")
|
174
|
+
continue
|
175
|
+
if not text or not text.strip():
|
176
|
+
invalid_texts.append(f"Text at index {i} is empty or contains only whitespace")
|
177
|
+
elif len(text.strip()) < 2: # Минимальная длина текста
|
178
|
+
invalid_texts.append(f"Text at index {i} is too short (minimum 2 characters)")
|
179
|
+
|
180
|
+
if invalid_texts:
|
181
|
+
raise EmbeddingServiceAPIError({
|
182
|
+
"code": -32602,
|
183
|
+
"message": "Invalid input texts",
|
184
|
+
"details": invalid_texts
|
185
|
+
})
|
186
|
+
|
138
187
|
async def cmd(self, command: str, params: Optional[Dict[str, Any]] = None, base_url: Optional[str] = None, port: Optional[int] = None) -> Dict[str, Any]:
|
139
188
|
"""
|
140
189
|
Execute a command via JSON-RPC protocol.
|
@@ -144,32 +193,69 @@ class EmbeddingServiceAsyncClient:
|
|
144
193
|
base_url (str, optional): Override base URL.
|
145
194
|
port (int, optional): Override port.
|
146
195
|
Returns:
|
147
|
-
dict: Command execution result
|
196
|
+
dict: Command execution result or error response in format:
|
197
|
+
{
|
198
|
+
"error": {
|
199
|
+
"code": <код ошибки>,
|
200
|
+
"message": <сообщение об ошибке>,
|
201
|
+
"details": <опциональные детали ошибки>
|
202
|
+
}
|
203
|
+
}
|
204
|
+
или
|
205
|
+
{
|
206
|
+
"result": {
|
207
|
+
"success": true,
|
208
|
+
"data": {
|
209
|
+
"embeddings": [[...], ...]
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
148
213
|
"""
|
214
|
+
if not command:
|
215
|
+
raise EmbeddingServiceAPIError({
|
216
|
+
"code": -32602,
|
217
|
+
"message": "Command is required"
|
218
|
+
})
|
219
|
+
|
220
|
+
# Валидация текстов для команды embed
|
221
|
+
if command == "embed" and params and "texts" in params:
|
222
|
+
self._validate_texts(params["texts"])
|
223
|
+
|
149
224
|
url = self._make_url("/cmd", base_url, port)
|
150
225
|
payload = {"command": command}
|
151
226
|
if params is not None:
|
152
227
|
payload["params"] = params
|
228
|
+
|
153
229
|
try:
|
154
230
|
async with self._session.post(url, json=payload) as resp:
|
155
231
|
await self._raise_for_status(resp)
|
156
232
|
data = await resp.json()
|
157
|
-
|
233
|
+
|
158
234
|
if "error" in data:
|
159
235
|
raise EmbeddingServiceAPIError(data["error"])
|
236
|
+
|
237
|
+
if "result" in data:
|
238
|
+
res = data["result"]
|
239
|
+
if isinstance(res, dict) and "success" in res and res["success"] is False:
|
240
|
+
if "error" in res:
|
241
|
+
raise EmbeddingServiceAPIError(res["error"])
|
242
|
+
|
160
243
|
return data
|
161
|
-
|
162
|
-
raise
|
163
|
-
except EmbeddingServiceHTTPError:
|
164
|
-
raise
|
165
|
-
except EmbeddingServiceConnectionError:
|
166
|
-
raise
|
244
|
+
|
167
245
|
except aiohttp.ClientConnectionError as e:
|
168
|
-
raise
|
246
|
+
raise EmbeddingServiceAPIError({
|
247
|
+
"code": -32000,
|
248
|
+
"message": f"Connection error: {e}"
|
249
|
+
}) from e
|
169
250
|
except aiohttp.ClientResponseError as e:
|
170
251
|
raise EmbeddingServiceHTTPError(e.status, e.message) from e
|
252
|
+
except EmbeddingServiceHTTPError:
|
253
|
+
raise
|
171
254
|
except Exception as e:
|
172
|
-
raise
|
255
|
+
raise EmbeddingServiceAPIError({
|
256
|
+
"code": -32000,
|
257
|
+
"message": f"Unexpected error: {e}"
|
258
|
+
}) from e
|
173
259
|
|
174
260
|
async def _raise_for_status(self, resp: aiohttp.ClientResponse):
|
175
261
|
try:
|
@@ -2,7 +2,7 @@
|
|
2
2
|
Example usage of EmbeddingServiceAsyncClient.
|
3
3
|
|
4
4
|
This example demonstrates how to use the async client to check the health of the embedding service,
|
5
|
-
request embeddings, and handle all possible
|
5
|
+
request embeddings, and handle all possible errors.
|
6
6
|
|
7
7
|
Run this script with:
|
8
8
|
python -m asyncio embed_client/example_async_usage.py --base-url http://localhost --port 8001
|
@@ -13,13 +13,7 @@ You can also set EMBED_CLIENT_BASE_URL and EMBED_CLIENT_PORT environment variabl
|
|
13
13
|
import asyncio
|
14
14
|
import sys
|
15
15
|
import os
|
16
|
-
from embed_client.async_client import
|
17
|
-
EmbeddingServiceAsyncClient,
|
18
|
-
EmbeddingServiceConnectionError,
|
19
|
-
EmbeddingServiceHTTPError,
|
20
|
-
EmbeddingServiceAPIError,
|
21
|
-
EmbeddingServiceError,
|
22
|
-
)
|
16
|
+
from embed_client.async_client import EmbeddingServiceAsyncClient
|
23
17
|
|
24
18
|
def get_params():
|
25
19
|
base_url = None
|
@@ -41,54 +35,35 @@ def get_params():
|
|
41
35
|
|
42
36
|
async def main():
|
43
37
|
base_url, port = get_params()
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
try:
|
49
|
-
health = await client.health()
|
50
|
-
print("Service health:", health)
|
51
|
-
except EmbeddingServiceConnectionError as e:
|
52
|
-
print("[Connection error]", e)
|
53
|
-
return
|
54
|
-
except EmbeddingServiceHTTPError as e:
|
55
|
-
print(f"[HTTP error] {e.status}: {e.message}")
|
56
|
-
return
|
57
|
-
except EmbeddingServiceError as e:
|
58
|
-
print("[Other error]", e)
|
59
|
-
return
|
38
|
+
async with EmbeddingServiceAsyncClient(base_url=base_url, port=port) as client:
|
39
|
+
# Check health
|
40
|
+
health = await client.health()
|
41
|
+
print("Service health:", health)
|
60
42
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
print("[
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
print("[Other error]", e)
|
43
|
+
# Request embeddings for a list of texts
|
44
|
+
texts = ["hello world", "test embedding"]
|
45
|
+
result = await client.cmd("embed", params={"texts": texts})
|
46
|
+
|
47
|
+
if "error" in result:
|
48
|
+
print(f"Error occurred: {result['error']}")
|
49
|
+
if "lang" in result:
|
50
|
+
print(f"Language: {result['lang']}")
|
51
|
+
if "text" in result:
|
52
|
+
print(f"Text: {result['text']}")
|
53
|
+
else:
|
54
|
+
vectors = result["result"]
|
55
|
+
print(f"Embeddings for {len(texts)} texts:")
|
56
|
+
for i, vec in enumerate(vectors):
|
57
|
+
print(f" Text: {texts[i]!r}\n Vector: {vec[:5]}... (total {len(vec)} dims)")
|
77
58
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
except EmbeddingServiceAPIError as e:
|
82
|
-
print("[API error for invalid command]", e.error)
|
59
|
+
# Example: error handling for invalid command
|
60
|
+
result = await client.cmd("health")
|
61
|
+
print("Health check result:", result)
|
83
62
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
print("[API error for empty texts]", e.error)
|
89
|
-
|
90
|
-
except Exception as e:
|
91
|
-
print("[Unexpected error]", e)
|
63
|
+
# Example: error handling for empty command
|
64
|
+
# result = await client.cmd("")
|
65
|
+
# if "error" in result:
|
66
|
+
# print(f"Error for empty command: {result['error']}")
|
92
67
|
|
93
68
|
if __name__ == "__main__":
|
94
69
|
asyncio.run(main())
|
@@ -0,0 +1,8 @@
|
|
1
|
+
embed_client/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
2
|
+
embed_client/async_client.py,sha256=sppZ8fPr4XNNLE3M6kLFTL6u5HE3nqo87bxAgt0S0zE,10589
|
3
|
+
embed_client/example_async_usage.py,sha256=df0RRwq2FtqVSL2MHVclfVIJj1wyQUuKZXB-lyVb3Kg,2538
|
4
|
+
embed_client/example_async_usage_ru.py,sha256=kZXQcbEFkx9tWXoCq-AoyvvUY4aCuW1XqPVb1ADWeAM,3558
|
5
|
+
embed_client-1.0.0.1.dist-info/METADATA,sha256=nFDbLecEwcLOuqhJe84hSdboS9vCEhmmX-Ajw9LYark,254
|
6
|
+
embed_client-1.0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
embed_client-1.0.0.1.dist-info/top_level.txt,sha256=uG00A4d9o9DFrhiN7goObpeig72Pniby0E7UpDRgyXY,13
|
8
|
+
embed_client-1.0.0.1.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
embed_client/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
2
|
-
embed_client/async_client.py,sha256=HzvJ85Qh8fT-YHuam4YFW5Kbxx2hrbsOV8EzjbQ34Eg,7329
|
3
|
-
embed_client/example_async_usage.py,sha256=7j9Hro7-TjsKVC2vHUX2J1_-Rh3V9FxsOmsCdCV8KYM,3555
|
4
|
-
embed_client/example_async_usage_ru.py,sha256=kZXQcbEFkx9tWXoCq-AoyvvUY4aCuW1XqPVb1ADWeAM,3558
|
5
|
-
embed_client-0.0.1.dist-info/METADATA,sha256=GUuiN2owdcnNa7XqqZeEJo_G9oV8Z7ZoqcNUtn-Cuyo,252
|
6
|
-
embed_client-0.0.1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
7
|
-
embed_client-0.0.1.dist-info/top_level.txt,sha256=uG00A4d9o9DFrhiN7goObpeig72Pniby0E7UpDRgyXY,13
|
8
|
-
embed_client-0.0.1.dist-info/RECORD,,
|
File without changes
|