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.
@@ -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
- if not base_url:
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
- if port is None:
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
- except EmbeddingServiceAPIError:
162
- raise
163
- except EmbeddingServiceHTTPError:
164
- raise
165
- except EmbeddingServiceConnectionError:
166
- raise
244
+
167
245
  except aiohttp.ClientConnectionError as e:
168
- raise EmbeddingServiceConnectionError(f"Connection error: {e}") from e
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 EmbeddingServiceError(f"Unexpected error: {e}") from e
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 exceptions.
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
- # Always use try/except to handle all possible errors
45
- try:
46
- async with EmbeddingServiceAsyncClient(base_url=base_url, port=port) as client:
47
- # Check health
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
- # Request embeddings for a list of texts
62
- texts = ["hello world", "test embedding"]
63
- try:
64
- result = await client.cmd("embed", params={"texts": texts})
65
- vectors = result["result"]
66
- print(f"Embeddings for {len(texts)} texts:")
67
- for i, vec in enumerate(vectors):
68
- print(f" Text: {texts[i]!r}\n Vector: {vec[:5]}... (total {len(vec)} dims)")
69
- except EmbeddingServiceAPIError as e:
70
- print("[API error]", e.error)
71
- except EmbeddingServiceHTTPError as e:
72
- print(f"[HTTP error] {e.status}: {e.message}")
73
- except EmbeddingServiceConnectionError as e:
74
- print("[Connection error]", e)
75
- except EmbeddingServiceError as e:
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
- # Example: error handling for invalid command
79
- try:
80
- await client.cmd("not_a_command")
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
- # Example: error handling for empty texts
85
- try:
86
- await client.cmd("embed", params={"texts": []})
87
- except EmbeddingServiceAPIError as e:
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())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: embed-client
3
- Version: 0.0.1
3
+ Version: 1.0.0.1
4
4
  Summary: Async client for Embedding Service API
5
5
  Author: Your Name
6
6
  Requires-Dist: aiohttp
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,