vectorvein 0.1.87__py3-none-any.whl → 0.1.89__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/anthropic_client.py +4 -0
- vectorvein/chat_clients/base_client.py +121 -2
- vectorvein/chat_clients/gemini_client.py +9 -523
- vectorvein/chat_clients/openai_compatible_client.py +16 -12
- vectorvein/chat_clients/utils.py +34 -116
- vectorvein/settings/__init__.py +30 -1
- vectorvein/types/defaults.py +30 -6
- vectorvein/types/llm_parameters.py +4 -1
- vectorvein/utilities/rate_limiter.py +312 -0
- {vectorvein-0.1.87.dist-info → vectorvein-0.1.89.dist-info}/METADATA +6 -1
- {vectorvein-0.1.87.dist-info → vectorvein-0.1.89.dist-info}/RECORD +13 -12
- {vectorvein-0.1.87.dist-info → vectorvein-0.1.89.dist-info}/WHEEL +0 -0
- {vectorvein-0.1.87.dist-info → vectorvein-0.1.89.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,312 @@
|
|
1
|
+
import time
|
2
|
+
import asyncio
|
3
|
+
from typing import Tuple
|
4
|
+
from collections import defaultdict
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
|
7
|
+
|
8
|
+
class AsyncRateLimiterBackend(ABC):
|
9
|
+
"""Rate Limiter Backend Abstract Base Class"""
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
async def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1) -> Tuple[bool, float]:
|
13
|
+
"""Returns (allowed, wait_time)"""
|
14
|
+
pass
|
15
|
+
|
16
|
+
|
17
|
+
class SyncRateLimiterBackend(ABC):
|
18
|
+
"""Rate Limiter Backend Abstract Base Class"""
|
19
|
+
|
20
|
+
@abstractmethod
|
21
|
+
def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1) -> Tuple[bool, float]:
|
22
|
+
"""Returns (allowed, wait_time)"""
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class AsyncMemoryRateLimiter(AsyncRateLimiterBackend):
|
27
|
+
"""Async Memory Rate Limiter"""
|
28
|
+
|
29
|
+
def __init__(self):
|
30
|
+
self.windows = defaultdict(list)
|
31
|
+
self.tokens = defaultdict(int)
|
32
|
+
self.lock = asyncio.Lock()
|
33
|
+
|
34
|
+
def _get_last_reset(self, key):
|
35
|
+
return self.windows[key][0] if self.windows[key] else time.time()
|
36
|
+
|
37
|
+
async def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1):
|
38
|
+
async with self.lock:
|
39
|
+
now = time.time()
|
40
|
+
|
41
|
+
# RPM 检查
|
42
|
+
window = self.windows[key]
|
43
|
+
window = [t for t in window if t > now - 60]
|
44
|
+
if len(window) >= rpm:
|
45
|
+
return False, 60 - (now - window[0])
|
46
|
+
|
47
|
+
# TPM 检查
|
48
|
+
if self.tokens[key] + request_cost > tpm:
|
49
|
+
return False, 60 - (now - self._get_last_reset(key))
|
50
|
+
|
51
|
+
window.append(now)
|
52
|
+
self.tokens[key] += request_cost
|
53
|
+
self.windows[key] = window[-rpm:]
|
54
|
+
return True, 0
|
55
|
+
|
56
|
+
|
57
|
+
class SyncMemoryRateLimiter(SyncRateLimiterBackend):
|
58
|
+
"""Sync Memory Rate Limiter"""
|
59
|
+
|
60
|
+
def __init__(self):
|
61
|
+
self.windows = defaultdict(list)
|
62
|
+
self.tokens = defaultdict(int)
|
63
|
+
self.lock = asyncio.Lock()
|
64
|
+
|
65
|
+
def _get_last_reset(self, key):
|
66
|
+
return self.windows[key][0] if self.windows[key] else time.time()
|
67
|
+
|
68
|
+
def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1) -> Tuple[bool, float]:
|
69
|
+
"""Sync Rate Limiter Check
|
70
|
+
|
71
|
+
Args:
|
72
|
+
key: Rate Limiter Key
|
73
|
+
rpm: Requests per minute limit
|
74
|
+
tpm: Tokens per minute limit
|
75
|
+
request_cost: The number of tokens consumed by this request
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
Tuple[bool, float]: (allowed, wait_time)
|
81
|
+
"""
|
82
|
+
now = time.time()
|
83
|
+
|
84
|
+
# RPM 检查
|
85
|
+
window = self.windows[key]
|
86
|
+
window = [t for t in window if t > now - 60]
|
87
|
+
if len(window) >= rpm:
|
88
|
+
return False, 60 - (now - window[0])
|
89
|
+
|
90
|
+
# TPM 检查
|
91
|
+
if self.tokens[key] + request_cost > tpm:
|
92
|
+
return False, 60 - (now - self._get_last_reset(key))
|
93
|
+
|
94
|
+
window.append(now)
|
95
|
+
self.tokens[key] += request_cost
|
96
|
+
self.windows[key] = window[-rpm:]
|
97
|
+
return True, 0
|
98
|
+
|
99
|
+
|
100
|
+
REDIS_SCRIPT = """
|
101
|
+
local key = KEYS[1]
|
102
|
+
local rpm = tonumber(ARGV[1])
|
103
|
+
local tpm = tonumber(ARGV[2])
|
104
|
+
local cost = tonumber(ARGV[3])
|
105
|
+
|
106
|
+
-- 使用Redis服务器时间(精确到微秒)
|
107
|
+
local server_time = redis.call('TIME')
|
108
|
+
local now = tonumber(server_time[1]) + tonumber(server_time[2])/1000000
|
109
|
+
|
110
|
+
-- RPM限制检查
|
111
|
+
local rpm_key = key..'_rpm'
|
112
|
+
local elements = redis.call('LRANGE', rpm_key, 0, -1)
|
113
|
+
local valid_elements = {}
|
114
|
+
local min_valid_time = now - 60
|
115
|
+
|
116
|
+
-- 过滤过期时间戳
|
117
|
+
for _, ts in ipairs(elements) do
|
118
|
+
local timestamp = tonumber(ts)
|
119
|
+
if timestamp > min_valid_time then
|
120
|
+
table.insert(valid_elements, timestamp)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
local valid_count = #valid_elements
|
124
|
+
|
125
|
+
-- 新增:自动清理过期时间戳
|
126
|
+
if valid_count > 0 then
|
127
|
+
redis.call('DEL', rpm_key)
|
128
|
+
for _, ts in ipairs(valid_elements) do
|
129
|
+
redis.call('RPUSH', rpm_key, ts)
|
130
|
+
end
|
131
|
+
redis.call('EXPIRE', rpm_key, 60)
|
132
|
+
end
|
133
|
+
|
134
|
+
if valid_count >= rpm then
|
135
|
+
local oldest = valid_elements[valid_count] -- 最旧的有效时间戳
|
136
|
+
local remaining = math.max(0.001, 60 - (now - oldest)) -- 保证最小等待时间
|
137
|
+
return {0, math.ceil(remaining * 1000)/1000} -- 保留3位小数
|
138
|
+
end
|
139
|
+
|
140
|
+
-- TPM限制检查(保持不变)
|
141
|
+
local tpm_key = key..'_tpm'
|
142
|
+
local current_tokens = tonumber(redis.call('GET', tpm_key) or 0)
|
143
|
+
if current_tokens + cost > tpm then
|
144
|
+
local ttl = redis.call('TTL', tpm_key)
|
145
|
+
if ttl < 0 then
|
146
|
+
redis.call('SETEX', tpm_key, 60, current_tokens)
|
147
|
+
ttl = 60
|
148
|
+
end
|
149
|
+
return {0, ttl}
|
150
|
+
end
|
151
|
+
|
152
|
+
-- 更新计数(增加时间戳覆盖写入)
|
153
|
+
redis.call('LPUSH', rpm_key, now)
|
154
|
+
redis.call('LTRIM', rpm_key, 0, rpm-1)
|
155
|
+
redis.call('EXPIRE', rpm_key, 60)
|
156
|
+
|
157
|
+
redis.call('INCRBY', tpm_key, cost)
|
158
|
+
redis.call('EXPIRE', tpm_key, 60)
|
159
|
+
|
160
|
+
return {1, 0}
|
161
|
+
"""
|
162
|
+
|
163
|
+
|
164
|
+
class AsyncRedisRateLimiter(AsyncRateLimiterBackend):
|
165
|
+
"""Async Redis Rate Limiter"""
|
166
|
+
|
167
|
+
def __init__(self, host: str = "localhost", port: int = 6379, db: int = 0):
|
168
|
+
import redis.asyncio as redis
|
169
|
+
|
170
|
+
self.redis = redis.Redis(host=host, port=port, db=db)
|
171
|
+
self.script = self.redis.register_script(REDIS_SCRIPT)
|
172
|
+
|
173
|
+
async def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1):
|
174
|
+
result = await self.script(keys=[key], args=[rpm, tpm, request_cost])
|
175
|
+
allowed, wait_time = result
|
176
|
+
return bool(allowed), max(1, float(wait_time))
|
177
|
+
|
178
|
+
|
179
|
+
class SyncRedisRateLimiter(SyncRateLimiterBackend):
|
180
|
+
"""Sync Redis Rate Limiter"""
|
181
|
+
|
182
|
+
def __init__(self, host: str = "localhost", port: int = 6379, db: int = 0):
|
183
|
+
import redis
|
184
|
+
|
185
|
+
self.redis = redis.Redis(host=host, port=port, db=db)
|
186
|
+
self.script = self.redis.register_script(REDIS_SCRIPT)
|
187
|
+
|
188
|
+
def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1):
|
189
|
+
result = self.script(keys=[key], args=[rpm, tpm, request_cost])
|
190
|
+
allowed, wait_time = result
|
191
|
+
return bool(allowed), max(1, float(wait_time))
|
192
|
+
|
193
|
+
|
194
|
+
class AsyncDiskCacheRateLimiter(AsyncRateLimiterBackend):
|
195
|
+
"""基于 diskcache 的异步限流器实现"""
|
196
|
+
|
197
|
+
def __init__(self, cache_dir: str = ".rate_limit_cache"):
|
198
|
+
"""初始化 diskcache 限流器
|
199
|
+
|
200
|
+
Args:
|
201
|
+
cache_dir: 缓存目录路径
|
202
|
+
"""
|
203
|
+
from diskcache import Cache
|
204
|
+
|
205
|
+
self.cache = Cache(cache_dir)
|
206
|
+
self._lock = asyncio.Lock()
|
207
|
+
|
208
|
+
def _get_rpm_key(self, key: str) -> str:
|
209
|
+
return f"{key}_rpm"
|
210
|
+
|
211
|
+
def _get_tpm_key(self, key: str) -> str:
|
212
|
+
return f"{key}_tpm"
|
213
|
+
|
214
|
+
async def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1) -> Tuple[bool, float]:
|
215
|
+
"""检查是否超出限流阈值
|
216
|
+
|
217
|
+
Args:
|
218
|
+
key: 限流键
|
219
|
+
rpm: 每分钟请求数限制
|
220
|
+
tpm: 每分钟令牌数限制
|
221
|
+
request_cost: 本次请求消耗的令牌数
|
222
|
+
|
223
|
+
Returns:
|
224
|
+
Tuple[bool, float]: (是否允许请求, 需要等待的时间)
|
225
|
+
"""
|
226
|
+
async with self._lock:
|
227
|
+
now = time.time()
|
228
|
+
rpm_key = self._get_rpm_key(key)
|
229
|
+
tpm_key = self._get_tpm_key(key)
|
230
|
+
|
231
|
+
# RPM 检查
|
232
|
+
window = self.cache.get(rpm_key, []) or []
|
233
|
+
window = [t for t in window if t > now - 60] # type: ignore 清理过期时间戳
|
234
|
+
|
235
|
+
if len(window) >= rpm:
|
236
|
+
return False, 60 - (now - window[0]) # type: ignore
|
237
|
+
|
238
|
+
# TPM 检查
|
239
|
+
current_tokens = self.cache.get(tpm_key, 0)
|
240
|
+
if current_tokens + request_cost > tpm: # type: ignore
|
241
|
+
# 获取最早的请求时间
|
242
|
+
oldest_time = window[0] if window else now
|
243
|
+
return False, 60 - (now - oldest_time) # type: ignore
|
244
|
+
|
245
|
+
# 更新状态
|
246
|
+
window.append(now) # type: ignore
|
247
|
+
window = window[-rpm:] # type: ignore # 只保留最近的 rpm 个时间戳
|
248
|
+
self.cache.set(rpm_key, window, expire=60)
|
249
|
+
self.cache.set(tpm_key, current_tokens + request_cost, expire=60) # type: ignore
|
250
|
+
|
251
|
+
return True, 0
|
252
|
+
|
253
|
+
|
254
|
+
class SyncDiskCacheRateLimiter(SyncRateLimiterBackend):
|
255
|
+
"""基于 diskcache 的同步限流器实现"""
|
256
|
+
|
257
|
+
def __init__(self, cache_dir: str = ".rate_limit_cache"):
|
258
|
+
"""初始化 diskcache 限流器
|
259
|
+
|
260
|
+
Args:
|
261
|
+
cache_dir: 缓存目录路径
|
262
|
+
"""
|
263
|
+
from diskcache import Cache
|
264
|
+
import threading
|
265
|
+
|
266
|
+
self.cache = Cache(cache_dir)
|
267
|
+
self._lock = threading.Lock()
|
268
|
+
|
269
|
+
def _get_rpm_key(self, key: str) -> str:
|
270
|
+
return f"{key}_rpm"
|
271
|
+
|
272
|
+
def _get_tpm_key(self, key: str) -> str:
|
273
|
+
return f"{key}_tpm"
|
274
|
+
|
275
|
+
def check_limit(self, key: str, rpm: int, tpm: int, request_cost: int = 1) -> Tuple[bool, float]:
|
276
|
+
"""检查是否超出限流阈值
|
277
|
+
|
278
|
+
Args:
|
279
|
+
key: 限流键
|
280
|
+
rpm: 每分钟请求数限制
|
281
|
+
tpm: 每分钟令牌数限制
|
282
|
+
request_cost: 本次请求消耗的令牌数
|
283
|
+
|
284
|
+
Returns:
|
285
|
+
Tuple[bool, float]: (是否允许请求, 需要等待的时间)
|
286
|
+
"""
|
287
|
+
with self._lock:
|
288
|
+
now = time.time()
|
289
|
+
rpm_key = self._get_rpm_key(key)
|
290
|
+
tpm_key = self._get_tpm_key(key)
|
291
|
+
|
292
|
+
# RPM 检查
|
293
|
+
window = self.cache.get(rpm_key, []) or []
|
294
|
+
window = [t for t in window if t > now - 60] # type: ignore 清理过期时间戳
|
295
|
+
|
296
|
+
if len(window) >= rpm:
|
297
|
+
return False, 60 - (now - window[0]) # type: ignore
|
298
|
+
|
299
|
+
# TPM 检查
|
300
|
+
current_tokens = self.cache.get(tpm_key, 0)
|
301
|
+
if current_tokens + request_cost > tpm: # type: ignore
|
302
|
+
# 获取最早的请求时间
|
303
|
+
oldest_time = window[0] if window else now
|
304
|
+
return False, 60 - (now - oldest_time) # type: ignore
|
305
|
+
|
306
|
+
# 更新状态
|
307
|
+
window.append(now)
|
308
|
+
window = window[-rpm:] # 只保留最近的 rpm 个时间戳
|
309
|
+
self.cache.set(rpm_key, window, expire=60)
|
310
|
+
self.cache.set(tpm_key, current_tokens + request_cost, expire=60) # type: ignore
|
311
|
+
|
312
|
+
return True, 0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vectorvein
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.89
|
4
4
|
Summary: VectorVein python SDK
|
5
5
|
Author-Email: Anderson <andersonby@163.com>
|
6
6
|
License: MIT
|
@@ -14,9 +14,14 @@ 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
16
|
Requires-Dist: google-auth>=2.35.0
|
17
|
+
Requires-Dist: diskcache>=5.0.0
|
17
18
|
Provides-Extra: server
|
18
19
|
Requires-Dist: fastapi; extra == "server"
|
19
20
|
Requires-Dist: uvicorn; extra == "server"
|
21
|
+
Provides-Extra: redis
|
22
|
+
Requires-Dist: redis; extra == "redis"
|
23
|
+
Provides-Extra: diskcache
|
24
|
+
Requires-Dist: diskcache; extra == "diskcache"
|
20
25
|
Description-Content-Type: text/markdown
|
21
26
|
|
22
27
|
# vectorvein
|
@@ -1,37 +1,38 @@
|
|
1
|
-
vectorvein-0.1.
|
2
|
-
vectorvein-0.1.
|
3
|
-
vectorvein-0.1.
|
1
|
+
vectorvein-0.1.89.dist-info/METADATA,sha256=qO2cLUOWAPGVGMH_ufs-2-fosZiJNkEM8fIo9npYEaY,807
|
2
|
+
vectorvein-0.1.89.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
+
vectorvein-0.1.89.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=omQuG4PRRPNflSAgtdU--rwsWG6vMpwMEyIGZyFVHVQ,18596
|
6
|
-
vectorvein/chat_clients/anthropic_client.py,sha256=
|
6
|
+
vectorvein/chat_clients/anthropic_client.py,sha256=Zk6X1feIvv7Az5dgyipJXbm9TkgWgpFghSTxLiXKKA8,38405
|
7
7
|
vectorvein/chat_clients/baichuan_client.py,sha256=CVMvpgjdrZGv0BWnTOBD-f2ufZ3wq3496wqukumsAr4,526
|
8
|
-
vectorvein/chat_clients/base_client.py,sha256=
|
8
|
+
vectorvein/chat_clients/base_client.py,sha256=QLvcGhjravPbvha6-spU-w6ugHU1LrsbdFUcs6NwMgE,18842
|
9
9
|
vectorvein/chat_clients/deepseek_client.py,sha256=3qWu01NlJAP2N-Ff62d5-CZXZitlizE1fzb20LNetig,526
|
10
|
-
vectorvein/chat_clients/gemini_client.py,sha256=
|
10
|
+
vectorvein/chat_clients/gemini_client.py,sha256=ufovIZrmAE3RLEe8h5WXombf7bARAZxnkj6ydNK2FQM,475
|
11
11
|
vectorvein/chat_clients/groq_client.py,sha256=Uow4pgdmFi93ZQSoOol2-0PhhqkW-S0XuSldvppz5U4,498
|
12
12
|
vectorvein/chat_clients/local_client.py,sha256=55nOsxzqUf79q3Y14MKROA71zxhsT7p7FsDZ89rts2M,422
|
13
13
|
vectorvein/chat_clients/minimax_client.py,sha256=ooJU92UCACC4TVWKJ-uo8vqQ8qF3K14ziAuSFm8Wj3M,20025
|
14
14
|
vectorvein/chat_clients/mistral_client.py,sha256=1aKSylzBDaLYcFnaBIL4-sXSzWmXfBeON9Q0rq-ziWw,534
|
15
15
|
vectorvein/chat_clients/moonshot_client.py,sha256=gbu-6nGxx8uM_U2WlI4Wus881rFRotzHtMSoYOcruGU,526
|
16
16
|
vectorvein/chat_clients/openai_client.py,sha256=Nz6tV45pWcsOupxjnsRsGTicbQNJWIZyxuJoJ5DGMpg,527
|
17
|
-
vectorvein/chat_clients/openai_compatible_client.py,sha256=
|
17
|
+
vectorvein/chat_clients/openai_compatible_client.py,sha256=F_kHsoCtrqJ7jLsgyIZ2mJSNQ_YnDp9SRNW4ydFDtic,28950
|
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=Nf7EKtKCuWkIi1zkoU-sSjsTT271OvWJsKzxo0WKJX4,24791
|
22
22
|
vectorvein/chat_clients/xai_client.py,sha256=eLFJJrNRJ-ni3DpshODcr3S1EJQLbhVwxyO1E54LaqM,491
|
23
23
|
vectorvein/chat_clients/yi_client.py,sha256=RNf4CRuPJfixrwLZ3-DEc3t25QDe1mvZeb9sku2f8Bc,484
|
24
24
|
vectorvein/chat_clients/zhipuai_client.py,sha256=Ys5DSeLCuedaDXr3PfG1EW2zKXopt-awO2IylWSwY0s,519
|
25
25
|
vectorvein/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
26
|
vectorvein/server/token_server.py,sha256=36F9PKSNOX8ZtYBXY_l-76GQTpUSmQ2Y8EMy1H7wtdQ,1353
|
27
|
-
vectorvein/settings/__init__.py,sha256=
|
27
|
+
vectorvein/settings/__init__.py,sha256=ecGyrE_6YfX9z6Igb1rDCu1Q-qMTcVozWF3WEl_hiKA,4871
|
28
28
|
vectorvein/settings/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
-
vectorvein/types/defaults.py,sha256=
|
29
|
+
vectorvein/types/defaults.py,sha256=MAoxFhtvzPWBl1Zroz6hhKl7AshyHcO90hcE3pXzePQ,27384
|
30
30
|
vectorvein/types/enums.py,sha256=7KTJSVtQueImmbr1fSwv3rQVtc0RyMWXJmoE2tDOaso,1667
|
31
31
|
vectorvein/types/exception.py,sha256=gnW4GnJ76jND6UGnodk9xmqkcbeS7Cz2rvncA2HpD5E,69
|
32
|
-
vectorvein/types/llm_parameters.py,sha256=
|
32
|
+
vectorvein/types/llm_parameters.py,sha256=jXHGR9aORkBrUaG4oQef3zorFzvVX2oTn2lMu57IOQs,5989
|
33
33
|
vectorvein/types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
34
|
vectorvein/utilities/media_processing.py,sha256=CTRq-lGlFkFgP_FSRhNwF_qUgmOrXPf2_1Ok9HY42_g,5887
|
35
|
+
vectorvein/utilities/rate_limiter.py,sha256=dwolIUVw2wP83Odqpx0AAaE77de1GzxkYDGH4tM_u_4,10300
|
35
36
|
vectorvein/utilities/retry.py,sha256=6KFS9R2HdhqM3_9jkjD4F36ZSpEx2YNFGOVlpOsUetM,2208
|
36
37
|
vectorvein/workflow/graph/edge.py,sha256=xLZEJmBjAfVB53cd7CuRcKhgE6QqXv9nz32wJn8cmyk,1064
|
37
38
|
vectorvein/workflow/graph/node.py,sha256=A3M_GghrSju1D3xc_HtPdGyr-7XSkplGPKJveOUiIF4,3256
|
@@ -54,4 +55,4 @@ vectorvein/workflow/nodes/vector_db.py,sha256=t6I17q6iR3yQreiDHpRrksMdWDPIvgqJs0
|
|
54
55
|
vectorvein/workflow/nodes/video_generation.py,sha256=qmdg-t_idpxq1veukd-jv_ChICMOoInKxprV9Z4Vi2w,4118
|
55
56
|
vectorvein/workflow/nodes/web_crawlers.py,sha256=LsqomfXfqrXfHJDO1cl0Ox48f4St7X_SL12DSbAMSOw,5415
|
56
57
|
vectorvein/workflow/utils/json_to_code.py,sha256=F7dhDy8kGc8ndOeihGLRLGFGlquoxVlb02ENtxnQ0C8,5914
|
57
|
-
vectorvein-0.1.
|
58
|
+
vectorvein-0.1.89.dist-info/RECORD,,
|
File without changes
|
File without changes
|