dan92 commited on
Commit
ccf139b
·
verified ·
1 Parent(s): 1e77c72

Upload 2 files

Browse files
Files changed (1) hide show
  1. app.py +191 -95
app.py CHANGED
@@ -129,16 +129,28 @@ class AuthManager:
129
  self._session: requests.Session = create_custom_session()
130
  self._logger: logging.Logger = logging.getLogger(__name__)
131
  self.model_status = {model: True for model in MODEL_INFO.keys()}
132
- # 添加新的属性来跟踪认证请求
133
  self._last_auth_attempt = 0
134
  self._auth_attempts = 0
135
  self._auth_window_start = time.time()
136
  self._backoff_delay = AUTH_RETRY_DELAY
 
 
 
137
 
138
  def _should_attempt_auth(self) -> bool:
139
  """检查是否应该尝试认证请求"""
140
  current_time = time.time()
141
 
 
 
 
 
 
 
 
 
 
 
142
  # 检查是否在退避期内
143
  if current_time - self._last_auth_attempt < self._backoff_delay:
144
  return False
@@ -149,6 +161,7 @@ class AuthManager:
149
  self._auth_window_start = current_time
150
  self._auth_attempts = 0
151
  self._backoff_delay = AUTH_RETRY_DELAY
 
152
 
153
  # 检查请求数量
154
  if self._auth_attempts >= AUTH_MAX_REQUESTS:
@@ -156,10 +169,39 @@ class AuthManager:
156
 
157
  return True
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  def login(self) -> bool:
160
- """改进的登录方法,包含速率限制和退避机制"""
161
  if not self._should_attempt_auth():
162
- logger.warning(f"Rate limit reached for {self._email}, waiting {self._backoff_delay}s")
163
  return False
164
 
165
  try:
@@ -178,29 +220,55 @@ class AuthManager:
178
 
179
  if response.status_code == 429:
180
  self._backoff_delay *= AUTH_BACKOFF_FACTOR
 
 
181
  logger.warning(f"Rate limit hit, increasing backoff to {self._backoff_delay}s")
182
  return False
183
 
184
- response.raise_for_status()
 
 
 
 
 
 
 
185
  self._user_info = response.json()
186
  self._refresh_token = self._user_info.get('refresh_token', '')
187
  self._access_token = self._user_info.get('access_token', '')
188
  self._token_expiry = time.time() + self._user_info.get('expires_in', 3600)
189
 
190
- # 重置退避延迟
 
191
  self._backoff_delay = AUTH_RETRY_DELAY
192
  self._log_values()
193
  return True
194
 
195
  except requests.RequestException as e:
196
  logger.error(f"\033[91m登录请求错误: {e}\033[0m")
 
 
197
  self._backoff_delay *= AUTH_BACKOFF_FACTOR
198
  return False
199
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  def refresh_user_token(self) -> bool:
 
201
  url = f"{_API_BASE_URL}/auth/v1/token?grant_type=refresh_token"
202
  headers = self._get_headers(with_content_type=True)
203
  data = {"refresh_token": self._refresh_token}
 
204
  try:
205
  response = self._make_request('POST', url, headers=headers, json=data)
206
  self._user_info = response.json()
@@ -285,16 +353,6 @@ class AuthManager:
285
  headers['Authorization'] = f'Bearer {self._access_token}'
286
  return headers
287
 
288
- def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
289
- """发送HTTP请求并处理异常。"""
290
- try:
291
- response = self._session.request(method, url, **kwargs)
292
- response.raise_for_status()
293
- return response
294
- except requests.RequestException as e:
295
- self._logger.error(f"请求错误 ({method} {url}): {e}")
296
- raise
297
-
298
  def is_model_available(self, model):
299
  return self.model_status.get(model, True)
300
 
@@ -324,7 +382,9 @@ class MultiAuthManager:
324
  start_index = self.current_index
325
  for _ in range(len(self.auth_managers)):
326
  auth_manager = self.auth_managers[self.current_index]
327
- if auth_manager.is_model_available(model) and auth_manager._should_attempt_auth():
 
 
328
  return auth_manager
329
  self.current_index = (self.current_index + 1) % len(self.auth_managers)
330
  if self.current_index == start_index:
@@ -338,12 +398,13 @@ class MultiAuthManager:
338
  return auth_manager
339
  return None
340
 
341
- def reset_all_model_status(self):
 
342
  for auth_manager in self.auth_managers:
343
- auth_manager.reset_model_status()
344
 
345
  def require_auth(func: Callable) -> Callable:
346
- """���饰器,确保在调用API之前有有效的token。"""
347
  @wraps(func)
348
  def wrapper(self, *args, **kwargs):
349
  if not self.ensure_valid_token():
@@ -788,6 +849,7 @@ def make_request(payload, auth_manager, model_id):
788
  global multi_auth_manager
789
  max_retries = 3
790
  retry_delay = 1
 
791
 
792
  logger.info(f"尝试发送请求,模型:{model_id}")
793
 
@@ -819,53 +881,64 @@ def make_request(payload, auth_manager, model_id):
819
  logger.info(f"尝试使用账号 {auth_manager._email}")
820
 
821
  for attempt in range(max_retries):
822
- try:
823
- url = get_notdiamond_url()
824
- headers = get_notdiamond_headers(auth_manager)
825
-
826
- # 获取代理
827
- proxy = proxy_pool.get_proxy()
828
-
829
- response = executor.submit(
830
- requests.post,
831
- url,
832
- headers=headers,
833
- json=payload,
834
- stream=True,
835
- proxies=proxy if proxy else None,
836
- timeout=30 # 添加超时设置
837
- ).result()
838
-
839
- if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
840
- logger.info(f"请求成功,使用账号 {auth_manager._email}")
841
- return response
842
-
843
- # 如果请求失败且使用了代理,移除该代理
844
- if proxy:
845
- proxy_pool.remove_proxy(proxy)
846
-
847
- headers_cache.clear()
848
-
849
- if response.status_code == 401:
850
- logger.info(f"Token expired for account {auth_manager._email}, attempting refresh")
851
- if auth_manager.ensure_valid_token():
852
  continue
853
-
854
- if response.status_code == 403:
855
- logger.warning(f"Model {model_id} usage limit reached for account {auth_manager._email}")
856
- auth_manager.set_model_unavailable(model_id)
857
- break
858
-
859
- logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
860
-
861
- except Exception as e:
862
- logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
863
- # 如果是代理导致的错误,移除该代理
864
- if proxy:
865
- proxy_pool.remove_proxy(proxy)
866
- if attempt < max_retries - 1:
867
- time.sleep(retry_delay)
868
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
 
870
  # 所有账号都尝试过且失败后,尝试注册新账号
871
  if len(tried_accounts) == len(multi_auth_manager.auth_managers):
@@ -879,43 +952,66 @@ def make_request(payload, auth_manager, model_id):
879
  raise Exception("所有账号均不可用,且注册新账号失败")
880
 
881
  def health_check():
882
- """改进的健康检查函数"""
883
- last_check_time = {} # 用于跟踪每个账号的最后检查时间
 
 
884
 
885
- while True:
886
- try:
887
- if multi_auth_manager:
888
- current_time = time.time()
889
-
890
- for auth_manager in multi_auth_manager.auth_managers:
891
- email = auth_manager._email
892
 
893
- # 检查是否需要进行健康检查
894
- if email not in last_check_time or \
895
- current_time - last_check_time[email] >= AUTH_CHECK_INTERVAL:
896
 
897
- if not auth_manager._should_attempt_auth():
898
- logger.info(f"Skipping health check for {email} due to rate limiting")
899
- continue
900
 
901
- if not auth_manager.ensure_valid_token():
902
- logger.warning(f"Auth token validation failed during health check for {email}")
903
- auth_manager.clear_auth()
904
- else:
905
- logger.info(f"Health check passed for {email}")
906
 
907
- last_check_time[email] = current_time
908
-
909
- # 每天重置所有账号的模型使用状态
910
- current_time_local = time.localtime()
911
- if current_time_local.tm_hour == 0 and current_time_local.tm_min == 0:
912
- multi_auth_manager.reset_all_model_status()
913
- logger.info("Reset model status for all accounts")
914
 
915
- except Exception as e:
916
- logger.error(f"Health check error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
 
918
- sleep(60) # 主循环每分钟运行一次
 
 
 
 
 
 
 
 
 
 
 
919
 
920
  # 为了兼容 Flask CLI 和 Gunicorn,修改启动逻辑
921
  if __name__ != "__main__":
 
129
  self._session: requests.Session = create_custom_session()
130
  self._logger: logging.Logger = logging.getLogger(__name__)
131
  self.model_status = {model: True for model in MODEL_INFO.keys()}
 
132
  self._last_auth_attempt = 0
133
  self._auth_attempts = 0
134
  self._auth_window_start = time.time()
135
  self._backoff_delay = AUTH_RETRY_DELAY
136
+ self._failed_attempts = 0 # 添加失败尝试计数
137
+ self._last_failure_time = 0 # 添加最后失败时间
138
+ self._is_valid = True # 添加账号有效性标志
139
 
140
  def _should_attempt_auth(self) -> bool:
141
  """检查是否应该尝试认证请求"""
142
  current_time = time.time()
143
 
144
+ # 如果账号已被标记为无效,直接返回False
145
+ if not self._is_valid:
146
+ return False
147
+
148
+ # 如果连续失败次数过多,标记账号为无效
149
+ if self._failed_attempts >= 5:
150
+ self._is_valid = False
151
+ self._logger.warning(f"Account {self._email} marked as invalid due to too many failures")
152
+ return False
153
+
154
  # 检查是否在退避期内
155
  if current_time - self._last_auth_attempt < self._backoff_delay:
156
  return False
 
161
  self._auth_window_start = current_time
162
  self._auth_attempts = 0
163
  self._backoff_delay = AUTH_RETRY_DELAY
164
+ self._failed_attempts = 0 # 重置失败计数
165
 
166
  # 检查请求数量
167
  if self._auth_attempts >= AUTH_MAX_REQUESTS:
 
169
 
170
  return True
171
 
172
+ def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
173
+ """改进的请求方法,支持代理"""
174
+ global proxy_pool
175
+ max_retries = 3
176
+
177
+ for attempt in range(max_retries):
178
+ try:
179
+ # 获取代理
180
+ proxy = proxy_pool.get_proxy() if proxy_pool else None
181
+
182
+ if proxy:
183
+ kwargs['proxies'] = proxy
184
+ kwargs['timeout'] = 10 # 使用代理时设置较短的超时
185
+
186
+ response = self._session.request(method, url, **kwargs)
187
+ response.raise_for_status()
188
+ return response
189
+
190
+ except requests.RequestException as e:
191
+ if proxy:
192
+ proxy_pool.remove_proxy(proxy)
193
+
194
+ if attempt == max_retries - 1:
195
+ self._logger.error(f"请求错误 ({method} {url}): {e}")
196
+ raise
197
+
198
+ time.sleep(1) # 短暂等待后重试
199
+
200
+ raise requests.RequestException("Max retries exceeded")
201
+
202
  def login(self) -> bool:
203
+ """改进的登录方法,使用代理"""
204
  if not self._should_attempt_auth():
 
205
  return False
206
 
207
  try:
 
220
 
221
  if response.status_code == 429:
222
  self._backoff_delay *= AUTH_BACKOFF_FACTOR
223
+ self._failed_attempts += 1
224
+ self._last_failure_time = time.time()
225
  logger.warning(f"Rate limit hit, increasing backoff to {self._backoff_delay}s")
226
  return False
227
 
228
+ if response.status_code == 400:
229
+ self._failed_attempts += 1
230
+ self._last_failure_time = time.time()
231
+ if self._failed_attempts >= 5:
232
+ self._is_valid = False
233
+ logger.error(f"Account {self._email} marked as invalid due to authentication failures")
234
+ return False
235
+
236
  self._user_info = response.json()
237
  self._refresh_token = self._user_info.get('refresh_token', '')
238
  self._access_token = self._user_info.get('access_token', '')
239
  self._token_expiry = time.time() + self._user_info.get('expires_in', 3600)
240
 
241
+ # 重置失败相关计数
242
+ self._failed_attempts = 0
243
  self._backoff_delay = AUTH_RETRY_DELAY
244
  self._log_values()
245
  return True
246
 
247
  except requests.RequestException as e:
248
  logger.error(f"\033[91m登录请求错误: {e}\033[0m")
249
+ self._failed_attempts += 1
250
+ self._last_failure_time = time.time()
251
  self._backoff_delay *= AUTH_BACKOFF_FACTOR
252
  return False
253
 
254
+ def is_valid(self) -> bool:
255
+ """检查账号是否有效"""
256
+ return self._is_valid
257
+
258
+ def reset_status(self) -> None:
259
+ """重置账号状态"""
260
+ self._is_valid = True
261
+ self._failed_attempts = 0
262
+ self._backoff_delay = AUTH_RETRY_DELAY
263
+ self._auth_attempts = 0
264
+ self._auth_window_start = time.time()
265
+
266
  def refresh_user_token(self) -> bool:
267
+ """改进的令牌刷新方法,使用代理"""
268
  url = f"{_API_BASE_URL}/auth/v1/token?grant_type=refresh_token"
269
  headers = self._get_headers(with_content_type=True)
270
  data = {"refresh_token": self._refresh_token}
271
+
272
  try:
273
  response = self._make_request('POST', url, headers=headers, json=data)
274
  self._user_info = response.json()
 
353
  headers['Authorization'] = f'Bearer {self._access_token}'
354
  return headers
355
 
 
 
 
 
 
 
 
 
 
 
356
  def is_model_available(self, model):
357
  return self.model_status.get(model, True)
358
 
 
382
  start_index = self.current_index
383
  for _ in range(len(self.auth_managers)):
384
  auth_manager = self.auth_managers[self.current_index]
385
+ if (auth_manager.is_valid() and
386
+ auth_manager.is_model_available(model) and
387
+ auth_manager._should_attempt_auth()):
388
  return auth_manager
389
  self.current_index = (self.current_index + 1) % len(self.auth_managers)
390
  if self.current_index == start_index:
 
398
  return auth_manager
399
  return None
400
 
401
+ def reset_all_accounts(self):
402
+ """重置所有账号状态"""
403
  for auth_manager in self.auth_managers:
404
+ auth_manager.reset_status()
405
 
406
  def require_auth(func: Callable) -> Callable:
407
+ """饰器,确保在调用API之前有有效的token。"""
408
  @wraps(func)
409
  def wrapper(self, *args, **kwargs):
410
  if not self.ensure_valid_token():
 
849
  global multi_auth_manager
850
  max_retries = 3
851
  retry_delay = 1
852
+ proxy_retries = 2 # 每个账号最多尝试2次代理
853
 
854
  logger.info(f"尝试发送请求,模型:{model_id}")
855
 
 
881
  logger.info(f"尝试使用账号 {auth_manager._email}")
882
 
883
  for attempt in range(max_retries):
884
+ for proxy_attempt in range(proxy_retries):
885
+ try:
886
+ url = get_notdiamond_url()
887
+ headers = get_notdiamond_headers(auth_manager)
888
+
889
+ # 获取代理
890
+ proxy = proxy_pool.get_proxy()
891
+ if not proxy and proxy_attempt == 0:
892
+ # 如果第一次获取代理失败,等待一下再试
893
+ time.sleep(1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
  continue
895
+
896
+ response = executor.submit(
897
+ requests.post,
898
+ url,
899
+ headers=headers,
900
+ json=payload,
901
+ stream=True,
902
+ proxies=proxy if proxy else None,
903
+ timeout=30
904
+ ).result()
905
+
906
+ if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
907
+ logger.info(f"请求成功,使用账号 {auth_manager._email}")
908
+ return response
909
+
910
+ # 如果请求失败且使用了代理,移除该代理
911
+ if proxy:
912
+ proxy_pool.remove_proxy(proxy)
913
+ if proxy_attempt < proxy_retries - 1:
914
+ # 如果还有代理重试机会,继续下一次尝试
915
+ continue
916
+
917
+ headers_cache.clear()
918
+
919
+ if response.status_code == 401:
920
+ logger.info(f"Token expired for account {auth_manager._email}, attempting refresh")
921
+ if auth_manager.ensure_valid_token():
922
+ break # 跳出代理重试循环,使用新token重试
923
+
924
+ if response.status_code == 403:
925
+ logger.warning(f"Model {model_id} usage limit reached for account {auth_manager._email}")
926
+ auth_manager.set_model_unavailable(model_id)
927
+ break # 跳出代理重试循环,尝试下一个账号
928
+
929
+ logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
930
+ break # 跳出代理重试循环
931
+
932
+ except Exception as e:
933
+ logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
934
+ if proxy:
935
+ proxy_pool.remove_proxy(proxy)
936
+ if proxy_attempt < proxy_retries - 1:
937
+ continue
938
+ break # 跳出代理重试循环
939
+
940
+ if attempt < max_retries - 1:
941
+ time.sleep(retry_delay)
942
 
943
  # 所有账号都尝试过且失败后,尝试注册新账号
944
  if len(tried_accounts) == len(multi_auth_manager.auth_managers):
 
952
  raise Exception("所有账号均不可用,且注册新账号失败")
953
 
954
  def health_check():
955
+ """改进的健康检查函数,使用线程池控制并发"""
956
+ last_check_time = {}
957
+ check_interval = 60 # 每个账号的检查间隔(秒)
958
+ max_concurrent_checks = 3 # 最大并发检查数量
959
 
960
+ with ThreadPoolExecutor(max_workers=max_concurrent_checks) as executor:
961
+ while True:
962
+ try:
963
+ if multi_auth_manager:
964
+ current_time = time.time()
965
+ check_futures = []
 
966
 
967
+ for auth_manager in multi_auth_manager.auth_managers:
968
+ email = auth_manager._email
 
969
 
970
+ # 检查是否需要进行健康检查
971
+ if email not in last_check_time or \
972
+ current_time - last_check_time[email] >= check_interval:
973
 
974
+ if not auth_manager._should_attempt_auth():
975
+ logger.info(f"Skipping health check for {email} due to rate limiting")
976
+ continue
 
 
977
 
978
+ # 提交健康检查任务
979
+ future = executor.submit(perform_health_check, auth_manager)
980
+ check_futures.append((email, future))
981
+ last_check_time[email] = current_time
 
 
 
982
 
983
+ # 等待所有检查完成
984
+ for email, future in check_futures:
985
+ try:
986
+ result = future.result(timeout=30)
987
+ if result:
988
+ logger.info(f"Health check passed for {email}")
989
+ else:
990
+ logger.warning(f"Health check failed for {email}")
991
+ except Exception as e:
992
+ logger.error(f"Health check error for {email}: {e}")
993
+
994
+ # 每天重置所有账号的模型使用状态
995
+ current_time_local = time.localtime()
996
+ if current_time_local.tm_hour == 0 and current_time_local.tm_min == 0:
997
+ multi_auth_manager.reset_all_accounts()
998
+ logger.info("Reset model status for all accounts")
999
+
1000
+ except Exception as e:
1001
+ logger.error(f"Health check error: {e}")
1002
 
1003
+ time.sleep(10) # 主循环每10秒运行一次
1004
+
1005
+ def perform_health_check(auth_manager):
1006
+ """执行单个账号的健康检查"""
1007
+ try:
1008
+ if auth_manager.ensure_valid_token():
1009
+ return True
1010
+ auth_manager.clear_auth()
1011
+ return False
1012
+ except Exception as e:
1013
+ logger.error(f"Health check error for {auth_manager._email}: {e}")
1014
+ return False
1015
 
1016
  # 为了兼容 Flask CLI 和 Gunicorn,修改启动逻辑
1017
  if __name__ != "__main__":