vectorvein 0.1.40__py3-none-any.whl → 0.1.41__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.
@@ -128,6 +128,8 @@ def get_token_counts(text: str | dict, model: str = "") -> int:
128
128
  if len(model_setting.endpoints) == 0:
129
129
  return int(len(text) / 1.33)
130
130
  endpoint_id = model_setting.endpoints[0]
131
+ if isinstance(endpoint_id, dict):
132
+ endpoint_id = endpoint_id["endpoint_id"]
131
133
  endpoint = settings.get_endpoint(endpoint_id)
132
134
  tokenize_url = "https://api.minimax.chat/v1/tokenize"
133
135
  headers = {"Authorization": f"Bearer {endpoint.api_key}", "Content-Type": "application/json"}
@@ -156,6 +158,8 @@ def get_token_counts(text: str | dict, model: str = "") -> int:
156
158
  if len(model_setting.endpoints) == 0:
157
159
  return len(get_gpt_35_encoding().encode(text))
158
160
  endpoint_id = model_setting.endpoints[0]
161
+ if isinstance(endpoint_id, dict):
162
+ endpoint_id = endpoint_id["endpoint_id"]
159
163
  endpoint = settings.get_endpoint(endpoint_id)
160
164
  tokenize_url = "https://api.moonshot.cn/v1/tokenizers/estimate-token-count"
161
165
  headers = {"Content-Type": "application/json", "Authorization": f"Bearer {endpoint.api_key}"}
@@ -181,6 +185,8 @@ def get_token_counts(text: str | dict, model: str = "") -> int:
181
185
  if len(model_setting.endpoints) == 0:
182
186
  return len(get_gpt_35_encoding().encode(text))
183
187
  endpoint_id = model_setting.endpoints[0]
188
+ if isinstance(endpoint_id, dict):
189
+ endpoint_id = endpoint_id["endpoint_id"]
184
190
  endpoint = settings.get_endpoint(endpoint_id)
185
191
  url = f"{endpoint.api_base}/models/{model_setting.id}:countTokens"
186
192
  params = {"key": endpoint.api_key}
@@ -215,6 +221,8 @@ def get_token_counts(text: str | dict, model: str = "") -> int:
215
221
  if len(model_setting.endpoints) == 0:
216
222
  return len(get_gpt_35_encoding().encode(text))
217
223
  endpoint_id = model_setting.endpoints[0]
224
+ if isinstance(endpoint_id, dict):
225
+ endpoint_id = endpoint_id["endpoint_id"]
218
226
  endpoint = settings.get_endpoint(endpoint_id)
219
227
  tokenize_url = "https://api.stepfun.com/v1/token/count"
220
228
  headers = {"Content-Type": "application/json", "Authorization": f"Bearer {endpoint.api_key}"}
@@ -7,64 +7,142 @@ from functools import cached_property
7
7
 
8
8
  import httpx
9
9
  from PIL import Image
10
-
10
+ from PIL.ImageFile import ImageFile
11
11
 
12
12
 
13
13
  class ImageProcessor:
14
- def __init__(self, image_source: Image.Image | str | Path, max_size: int | None = 5 * 1024 * 1024):
14
+ def __init__(
15
+ self,
16
+ image_source: Image.Image | str | Path,
17
+ max_size: int | None = 5 * 1024 * 1024,
18
+ max_width: int | None = None,
19
+ max_height: int | None = None,
20
+ ):
15
21
  self.image_source = image_source
16
22
  if isinstance(image_source, (Image.Image, Path)):
17
23
  self.is_local = True
18
24
  else:
19
25
  self.is_local = not image_source.startswith("http")
20
26
  self.max_size = max_size
27
+ self.max_width = max_width
28
+ self.max_height = max_height
21
29
  self._image = self._load_image()
30
+ self._image_format = self._image.format or "JPEG"
31
+ self._cached_bytes = None
32
+ self._cached_base64_image = None
22
33
 
23
34
  def _load_image(self):
24
- if not self.is_local:
35
+ if not self.is_local and isinstance(self.image_source, str):
25
36
  image_url = self.image_source
26
37
  response = httpx.get(image_url)
27
38
  return Image.open(BytesIO(response.content))
28
- else:
39
+ elif isinstance(self.image_source, Path):
29
40
  return Image.open(self.image_source)
41
+ elif isinstance(self.image_source, Image.Image):
42
+ return self.image_source
43
+ else:
44
+ raise ValueError(f"Unsupported image source type: {type(self.image_source)}")
45
+
46
+ def _resize_image(
47
+ self,
48
+ img: ImageFile | Image.Image,
49
+ max_size: int | None = None,
50
+ max_width: int | None = None,
51
+ max_height: int | None = None,
52
+ ):
53
+ img_bytes = BytesIO()
54
+ image_format = img.format or "JPEG"
55
+ _img = img.copy()
56
+ _img.save(img_bytes, format=image_format, optimize=True)
57
+
58
+ if max_width is not None and _img.width > max_width:
59
+ new_size = (max_width, int(max_width * _img.height / _img.width))
60
+ _img = _img.resize(new_size, Image.Resampling.LANCZOS)
61
+
62
+ if max_height is not None and _img.height > max_height:
63
+ new_size = (int(max_height * _img.width / _img.height), max_height)
64
+ _img = _img.resize(new_size, Image.Resampling.LANCZOS)
30
65
 
31
- def _resize_image(self, img, max_size):
32
66
  img_bytes = BytesIO()
33
- img.save(img_bytes, format=img.format, optimize=True)
67
+ _img.save(img_bytes, format=image_format, optimize=True)
34
68
 
35
- if img_bytes.getbuffer().nbytes <= max_size:
69
+ if max_size is not None and img_bytes.getbuffer().nbytes <= max_size:
36
70
  return img_bytes
37
71
 
38
- original_size = img.size
72
+ original_size = _img.size
39
73
  scale_factor = 0.9
40
74
 
41
75
  while True:
42
76
  new_size = (int(original_size[0] * scale_factor), int(original_size[1] * scale_factor))
43
- img_resized = img.resize(new_size, Image.Resampling.LANCZOS)
77
+ img_resized = _img.resize(new_size, Image.Resampling.LANCZOS)
44
78
 
45
79
  img_bytes_resized = BytesIO()
46
- img_resized.save(img_bytes_resized, format=img.format, optimize=True)
80
+ img_resized.save(img_bytes_resized, format=image_format, optimize=True)
47
81
 
48
- if img_bytes_resized.getbuffer().nbytes <= max_size:
82
+ if max_size is not None and img_bytes_resized.getbuffer().nbytes <= max_size:
49
83
  return img_bytes_resized
50
84
 
51
85
  scale_factor -= 0.1
52
86
  if scale_factor < 0.1:
53
87
  return img_bytes_resized
54
88
 
55
- @cached_property
89
+ @property
90
+ def bytes(self):
91
+ if self._cached_bytes is not None:
92
+ return self._cached_bytes
93
+ if self.max_size is None and self.max_width is None and self.max_height is None:
94
+ if isinstance(self._image, Image.Image):
95
+ img_bytes = BytesIO()
96
+
97
+ # 检查图像是否有透明通道
98
+ has_transparency = self._image.mode in ("RGBA", "LA") or (
99
+ self._image.mode == "P" and "transparency" in self._image.info
100
+ )
101
+
102
+ if has_transparency:
103
+ # 如果有透明通道,使用PNG格式
104
+ save_format = "PNG"
105
+ self._image_format = "PNG"
106
+ else:
107
+ # 如果没有透明通道,使用原始格式或默认为JPEG
108
+ save_format = self._image.format or self._image_format or "JPEG"
109
+
110
+ # 如果图像模式不是RGB(例如RGBA),转换为RGB
111
+ if self._image.mode != "RGB":
112
+ self._image = self._image.convert("RGB")
113
+
114
+ self._image.save(img_bytes, format=save_format, optimize=True)
115
+ self._cached_bytes = img_bytes.getvalue()
116
+ return self._cached_bytes
117
+ elif isinstance(self._image, BytesIO):
118
+ self._cached_bytes = self._image.getvalue()
119
+ return self._cached_bytes
120
+ elif isinstance(self._image, ImageFile):
121
+ if self._image.fp is None:
122
+ raise ValueError("Image file is not open")
123
+ self._cached_bytes = self._image.fp.read()
124
+ return self._cached_bytes
125
+
126
+ self._cached_bytes = self._image.getvalue()
127
+ return self._cached_bytes
128
+
129
+ img_bytes_resized = self._resize_image(self._image, self.max_size, self.max_width, self.max_height)
130
+ return img_bytes_resized.getvalue()
131
+
132
+ @property
56
133
  def base64_image(self):
57
- if self.max_size is None:
58
- return base64.b64encode(self._image.getvalue()).decode()
134
+ if self.max_size is None and self.max_width is None and self.max_height is None:
135
+ self._cached_base64_image = base64.b64encode(self.bytes).decode()
136
+ return self._cached_base64_image
59
137
 
60
- img_bytes_resized = self._resize_image(self._image, self.max_size)
61
- return base64.b64encode(img_bytes_resized.getvalue()).decode()
138
+ img_bytes_resized = self._resize_image(self._image, self.max_size, self.max_width, self.max_height)
139
+ self._cached_base64_image = base64.b64encode(img_bytes_resized.getvalue()).decode()
140
+ return self._cached_base64_image
62
141
 
63
- @cached_property
142
+ @property
64
143
  def mime_type(self):
65
- return Image.MIME[self._image.format]
144
+ return Image.MIME[self._image_format]
66
145
 
67
146
  @cached_property
68
147
  def data_url(self):
69
148
  return f"data:{self.mime_type};base64,{self.base64_image}"
70
-
@@ -1,36 +1,62 @@
1
1
  # @Author: Bi Ying
2
- # @Date: 2024-08-14 13:03:10
2
+ # @Date: 2024-06-07 16:16:49
3
3
  import time
4
+ from typing import Optional, Any, Callable, Tuple, Union, TypeVar, Generic
4
5
 
5
6
 
6
- class Retry:
7
- def __init__(self, function):
8
- self.function = function
9
- self.__retry_times = 3
10
- self.__sleep_time = 1
11
- self.pargs = []
12
- self.kwargs = {}
7
+ ResultType = TypeVar("ResultType")
13
8
 
14
- def args(self, *args, **kwargs):
15
- self.pargs = args
9
+
10
+ class Retry(Generic[ResultType]):
11
+ def __init__(self, function: Callable[..., ResultType]):
12
+ self.function: Callable[..., ResultType] = function
13
+ self.__retry_times: int = 3
14
+ self.__sleep_time: Union[int, float] = 1
15
+ self.__timeout: int = 180
16
+ self.__result_check: Optional[Callable[[ResultType], bool]] = None
17
+ self.pargs: list = []
18
+ self.kwargs: dict = {}
19
+
20
+ def args(self, *args: Any, **kwargs: Any) -> "Retry[ResultType]":
21
+ self.pargs = list(args)
16
22
  self.kwargs = kwargs
17
23
  return self
18
24
 
19
- def retry_times(self, retry_times: int):
25
+ def retry_times(self, retry_times: int) -> "Retry[ResultType]":
20
26
  self.__retry_times = retry_times
21
27
  return self
22
28
 
23
- def sleep_time(self, sleep_time):
29
+ def sleep_time(self, sleep_time: Union[int, float]) -> "Retry[ResultType]":
24
30
  self.__sleep_time = sleep_time
25
31
  return self
26
32
 
27
- def run(self):
33
+ def result_check(self, check_function: Callable[[ResultType], bool]) -> "Retry[ResultType]":
34
+ self.__result_check = check_function
35
+ return self
36
+
37
+ def _check_result(self, result: ResultType) -> bool:
38
+ try:
39
+ if self.__result_check is None:
40
+ return True
41
+ return self.__result_check(result)
42
+ except Exception as e:
43
+ print(f"Retry result check error: {e}")
44
+ return False
45
+
46
+ def run(self) -> Tuple[bool, Optional[ResultType]]:
28
47
  try_times = 0
29
- while try_times < self.__retry_times:
48
+ start_time = time.time()
49
+
50
+ while try_times <= self.__retry_times and time.time() - start_time < self.__timeout:
30
51
  try:
31
- return True, self.function(*self.pargs, **self.kwargs)
52
+ result: ResultType = self.function(*self.pargs, **self.kwargs)
53
+ if self._check_result(result):
54
+ return True, result
55
+ try_times += 1
56
+ time.sleep(self.__sleep_time)
32
57
  except Exception as e:
33
- print(f"{self.function.__name__} 函数出错:{e}")
58
+ print(f"{self.function.__name__} function error: {e}")
34
59
  try_times += 1
35
60
  time.sleep(self.__sleep_time)
61
+
36
62
  return False, None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vectorvein
3
- Version: 0.1.40
3
+ Version: 0.1.41
4
4
  Summary: Default template for PDM package
5
5
  Author-Email: Anderson <andersonby@163.com>
6
6
  License: MIT
@@ -13,6 +13,7 @@ Requires-Dist: pydantic>=2.8.2
13
13
  Requires-Dist: Pillow>=10.4.0
14
14
  Requires-Dist: deepseek-tokenizer>=0.1.0
15
15
  Requires-Dist: qwen-tokenizer>=0.2.0
16
+ Requires-Dist: google-auth>=2.35.0
16
17
  Description-Content-Type: text/markdown
17
18
 
18
19
  # vectorvein
@@ -1,6 +1,6 @@
1
- vectorvein-0.1.40.dist-info/METADATA,sha256=Pb_5WD1aE0Y1CDJOPMvCFohJq0h_CcDd_tehqOpsEbk,502
2
- vectorvein-0.1.40.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- vectorvein-0.1.40.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1
+ vectorvein-0.1.41.dist-info/METADATA,sha256=ynJyDUbCKV8Uo4z_bRF18BemQWskfhV1WZoegG9QZJw,537
2
+ vectorvein-0.1.41.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ vectorvein-0.1.41.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
4
  vectorvein/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  vectorvein/chat_clients/__init__.py,sha256=dW169oK1n3v8Z0uD8itghzlCP72rxiaS-XYn6fvI2xM,16788
6
6
  vectorvein/chat_clients/anthropic_client.py,sha256=jF9pDlnkhjM6-OLPCQQxkh27xjzbTRaEY53olRd3_aY,32413
@@ -18,7 +18,7 @@ vectorvein/chat_clients/openai_compatible_client.py,sha256=FVm_ZYL9UP6t6hTUNxPyo
18
18
  vectorvein/chat_clients/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  vectorvein/chat_clients/qwen_client.py,sha256=-ryh-m9PgsO0fc4ulcCmPTy1155J8YUy15uPoJQOHA0,513
20
20
  vectorvein/chat_clients/stepfun_client.py,sha256=zsD2W5ahmR4DD9cqQTXmJr3txrGuvxbRWhFlRdwNijI,519
21
- vectorvein/chat_clients/utils.py,sha256=7aSukIJrjFr50o160tgV7t2gUdZUdeNJnJaeJSwlBtQ,24383
21
+ vectorvein/chat_clients/utils.py,sha256=zEYT9EBLVtUeL-bxFnan3Ey9c6QMVvwI-koSzU4GmSU,24763
22
22
  vectorvein/chat_clients/yi_client.py,sha256=RNf4CRuPJfixrwLZ3-DEc3t25QDe1mvZeb9sku2f8Bc,484
23
23
  vectorvein/chat_clients/zhipuai_client.py,sha256=Ys5DSeLCuedaDXr3PfG1EW2zKXopt-awO2IylWSwY0s,519
24
24
  vectorvein/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,6 +29,6 @@ vectorvein/types/enums.py,sha256=x_S0IJiEWijOAEiMNdiGDGEWGtmt7TwMriJVDqrDmTo,163
29
29
  vectorvein/types/exception.py,sha256=gnW4GnJ76jND6UGnodk9xmqkcbeS7Cz2rvncA2HpD5E,69
30
30
  vectorvein/types/llm_parameters.py,sha256=vhleSgCHzDl7EULYJ3dUYlu9KLbfs9y6dcPD0BkaRdg,5114
31
31
  vectorvein/types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- vectorvein/utilities/media_processing.py,sha256=BujciRmw1GMmc3ELRvafL8STcy6r5b2rVnh27-uA7so,2256
33
- vectorvein/utilities/retry.py,sha256=9ePuJdeUUGx-qMWfaFxmlOvG_lQPwCQ4UB1z3Edlo34,993
34
- vectorvein-0.1.40.dist-info/RECORD,,
32
+ vectorvein/utilities/media_processing.py,sha256=cnzLrU1OaJvSv87IOnc36FrDXtmGMDStPbxtIJ33YN4,5880
33
+ vectorvein/utilities/retry.py,sha256=6KFS9R2HdhqM3_9jkjD4F36ZSpEx2YNFGOVlpOsUetM,2208
34
+ vectorvein-0.1.41.dist-info/RECORD,,