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.
- vectorvein/chat_clients/utils.py +8 -0
- vectorvein/utilities/media_processing.py +97 -19
- vectorvein/utilities/retry.py +42 -16
- {vectorvein-0.1.40.dist-info → vectorvein-0.1.41.dist-info}/METADATA +2 -1
- {vectorvein-0.1.40.dist-info → vectorvein-0.1.41.dist-info}/RECORD +7 -7
- {vectorvein-0.1.40.dist-info → vectorvein-0.1.41.dist-info}/WHEEL +0 -0
- {vectorvein-0.1.40.dist-info → vectorvein-0.1.41.dist-info}/entry_points.txt +0 -0
vectorvein/chat_clients/utils.py
CHANGED
@@ -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__(
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
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=
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
@
|
142
|
+
@property
|
64
143
|
def mime_type(self):
|
65
|
-
return Image.MIME[self.
|
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
|
-
|
vectorvein/utilities/retry.py
CHANGED
@@ -1,36 +1,62 @@
|
|
1
1
|
# @Author: Bi Ying
|
2
|
-
# @Date: 2024-
|
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
|
-
|
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
|
-
|
15
|
-
|
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
|
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
|
-
|
48
|
+
start_time = time.time()
|
49
|
+
|
50
|
+
while try_times <= self.__retry_times and time.time() - start_time < self.__timeout:
|
30
51
|
try:
|
31
|
-
|
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__}
|
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.
|
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.
|
2
|
-
vectorvein-0.1.
|
3
|
-
vectorvein-0.1.
|
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=
|
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=
|
33
|
-
vectorvein/utilities/retry.py,sha256=
|
34
|
-
vectorvein-0.1.
|
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,,
|
File without changes
|
File without changes
|