dan92 commited on
Commit
9396dbb
·
verified ·
1 Parent(s): 0261a69

Upload 6 files

Browse files
Files changed (3) hide show
  1. Dockerfile +2 -2
  2. app.py +63 -112
  3. gitattributes +35 -0
Dockerfile CHANGED
@@ -22,5 +22,5 @@ ENV PYTHONUNBUFFERED=1
22
  # 暴露端口
23
  EXPOSE 3000
24
 
25
- # 使用 gunicorn 作为生产级 WSGI 服务器,添加超时和保活设置
26
- CMD ["gunicorn", "--bind", "0.0.0.0:3000", "--workers", "4", "--timeout", "120", "--keep-alive", "5", "--worker-class", "sync", "app:app"]
 
22
  # 暴露端口
23
  EXPOSE 3000
24
 
25
+ # 使用 gunicorn 作为生产级 WSGI 服务器
26
+ CMD ["gunicorn", "--bind", "0.0.0.0:3000", "--workers", "4", "app:app"]
app.py CHANGED
@@ -107,7 +107,7 @@ def create_custom_session():
107
  return session
108
 
109
  # 添加速率限制相关的常量
110
- AUTH_RETRY_DELAY = 60 # 认证���试延迟(秒)
111
  AUTH_BACKOFF_FACTOR = 2 # 退避因子
112
  AUTH_MAX_RETRIES = 3 # 最大重试次数
113
  AUTH_CHECK_INTERVAL = 300 # 健康检查间隔(秒)
@@ -309,84 +309,37 @@ class MultiAuthManager:
309
  self.current_index = 0
310
  self._last_rotation = time.time()
311
  self._rotation_interval = 300 # 5分钟轮转间隔
312
- self.last_successful_index = 0 # 记录上次成功的账号索引
313
- self.last_success_date = datetime.now().date() # 记录上次成功的日期
314
- self.model_usage = {} # 记录每个模型最后使用的成功账号
315
 
316
- def ensure_valid_token(self, model):
317
- """改进的token验证方法,优先使用上次成功的模型特定账号"""
318
- # 首先尝试使用该模型上次成功的账号
319
- if model in self.model_usage:
320
- last_index = self.model_usage[model]
321
- auth_manager = self.auth_managers[last_index]
322
- if auth_manager.is_model_available(model) and auth_manager.ensure_valid_token():
323
- logger.info(f"Using last successful account for model {model}: {auth_manager._email}")
324
- return auth_manager
325
- else:
326
- logger.info(f"Last successful account {auth_manager._email} is no longer available for model {model}")
327
- # 从模型使用记录中移除不可用的账号
328
- del self.model_usage[model]
329
 
330
- # 如果该模型没有成功记录或上次的账号不可用,从头开始尝试所有账号
331
- tried_accounts = set()
332
- start_index = self.current_index
333
- current = start_index
 
334
 
335
- while len(tried_accounts) < len(self.auth_managers):
336
- auth_manager = self.auth_managers[current]
337
-
338
- if auth_manager._email not in tried_accounts:
339
- tried_accounts.add(auth_manager._email)
340
- logger.info(f"Trying account {auth_manager._email} for model {model}")
341
-
342
- if auth_manager.is_model_available(model):
343
- if auth_manager.ensure_valid_token():
344
- # 更新该模型的成功账号记录
345
- self.model_usage[model] = current
346
- self.current_index = (current + 1) % len(self.auth_managers) # 更新当前索引为下一个
347
- logger.info(f"Found available account for model {model}: {auth_manager._email}")
348
- return auth_manager
349
- else:
350
- logger.info(f"Token validation failed for account {auth_manager._email}")
351
- else:
352
- logger.info(f"Model {model} not available for account {auth_manager._email}")
353
-
354
- current = (current + 1) % len(self.auth_managers)
355
- if current == start_index:
356
- break
357
-
358
- # 如果是新的一天,重置所有状态
359
- if datetime.now().date() > self.last_success_date:
360
- logger.info("New day started, resetting all model status")
361
- self.reset_all_model_status()
362
- self.last_success_date = datetime.now().date()
363
- self.model_usage.clear()
364
-
365
- # 重新尝试第一个账号
366
- auth_manager = self.auth_managers[0]
367
- if auth_manager.ensure_valid_token():
368
- self.current_index = 1 # 设置为下一个账号
369
- self.model_usage[model] = 0
370
- logger.info(f"Using first account after reset: {auth_manager._email}")
371
  return auth_manager
372
-
373
- logger.warning(f"No available accounts found for model {model}")
 
374
  return None
375
 
376
- def update_model_success(self, model, index):
377
- """更新模型成功使用的账号索引"""
378
- old_index = self.model_usage.get(model, None)
379
- self.model_usage[model] = index
380
- self.last_success_date = datetime.now().date()
381
- if old_index != index:
382
- logger.info(f"Updated successful account for model {model}: {self.auth_managers[index]._email}")
383
 
384
  def reset_all_model_status(self):
385
- """重置所有账号的状态和模型使用记录"""
386
  for auth_manager in self.auth_managers:
387
  auth_manager.reset_model_status()
388
- self.model_usage.clear()
389
- logger.info("Reset all model status and usage records")
390
 
391
  def require_auth(func: Callable) -> Callable:
392
  """装饰器,确保在调用API之前有有效的token。"""
@@ -593,7 +546,7 @@ def handle_non_stream_response(response, model, prompt_tokens):
593
  raise
594
 
595
  def generate_stream_response(response, model, prompt_tokens):
596
- """生流式 HTTP 响应。"""
597
  total_completion_tokens = 0
598
 
599
  for chunk in stream_notdiamond_response(response, model):
@@ -724,17 +677,17 @@ def handle_request():
724
  request_data = request.get_json()
725
  model_id = request_data.get('model', '')
726
 
727
- # 不直接使用传入的 auth_manager,而是让 make_request 函数自己选择账号
 
 
 
728
  stream = request_data.get('stream', False)
729
  prompt_tokens = count_message_tokens(
730
  request_data.get('messages', []),
731
  model_id
732
  )
733
  payload = build_payload(request_data, model_id)
734
-
735
- # 传入 None 作为 auth_manager,让 make_request 自己选择账号
736
- response = make_request(payload, None, model_id)
737
-
738
  if stream:
739
  return Response(
740
  stream_with_context(generate_stream_response(response, model_id, prompt_tokens)),
@@ -754,6 +707,17 @@ def handle_request():
754
  'details': str(e)
755
  }
756
  }), 503
 
 
 
 
 
 
 
 
 
 
 
757
  except Exception as e:
758
  logger.error("Unexpected error: %s", str(e), exc_info=True)
759
  return jsonify({
@@ -767,7 +731,7 @@ def handle_request():
767
  }), 500
768
 
769
  def build_payload(request_data, model_id):
770
- """构建请求有负载,确保保持完的上下文。"""
771
  messages = request_data.get('messages', [])
772
 
773
  # 检查是否已经存在系统消息
@@ -815,11 +779,10 @@ def build_payload(request_data, model_id):
815
  return payload
816
 
817
  def make_request(payload, auth_manager, model_id):
818
- """改进的请求处理,添加超时控制"""
819
  global multi_auth_manager
820
  max_retries = 3
821
  retry_delay = 1
822
- request_timeout = 30 # 设置请求超时时间
823
 
824
  logger.info(f"尝试发送请求,模型:{model_id}")
825
 
@@ -836,14 +799,15 @@ def make_request(payload, auth_manager, model_id):
836
  else:
837
  raise Exception("无法注册新账号")
838
 
839
- # 记录尝试的账号
840
  tried_accounts = set()
841
 
842
  while len(tried_accounts) < len(multi_auth_manager.auth_managers):
843
- auth_manager = multi_auth_manager.ensure_valid_token(model_id)
844
  if not auth_manager:
845
  break
846
 
 
847
  if auth_manager._email in tried_accounts:
848
  continue
849
 
@@ -854,21 +818,16 @@ def make_request(payload, auth_manager, model_id):
854
  try:
855
  url = get_notdiamond_url()
856
  headers = get_notdiamond_headers(auth_manager)
857
-
858
  response = executor.submit(
859
  requests.post,
860
  url,
861
  headers=headers,
862
  json=payload,
863
- stream=True,
864
- timeout=request_timeout
865
- ).result(timeout=request_timeout)
866
 
867
  if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
868
  logger.info(f"请求成功,使用账号 {auth_manager._email}")
869
- current_index = multi_auth_manager.auth_managers.index(auth_manager)
870
- # 更新模型特定的成功记录
871
- multi_auth_manager.update_model_success(model_id, current_index)
872
  return response
873
 
874
  headers_cache.clear()
@@ -885,9 +844,6 @@ def make_request(payload, auth_manager, model_id):
885
 
886
  logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
887
 
888
- except (requests.Timeout, concurrent.futures.TimeoutError) as e:
889
- logger.error(f"Request timeout for account {auth_manager._email}: {e}")
890
- break
891
  except Exception as e:
892
  logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
893
  if attempt < max_retries - 1:
@@ -907,40 +863,34 @@ def make_request(payload, auth_manager, model_id):
907
  raise Exception("所有账号均不可用,且注册新账号失败")
908
 
909
  def health_check():
910
- """改进的健康检查函数,每60秒只检查一个账号"""
911
- check_index = 0
912
- last_check_date = datetime.now().date()
913
 
914
  while True:
915
  try:
916
  if multi_auth_manager:
917
- current_date = datetime.now().date()
918
 
919
- # 如果是新的一天,重置检查索引
920
- if current_date > last_check_date:
921
- check_index = 0
922
- last_check_date = current_date
923
- logger.info("New day started, resetting health check index")
924
- continue
925
-
926
- # 只检查一个账号
927
- if check_index < len(multi_auth_manager.auth_managers):
928
- auth_manager = multi_auth_manager.auth_managers[check_index]
929
  email = auth_manager._email
930
 
931
- if auth_manager._should_attempt_auth():
 
 
 
 
 
 
 
932
  if not auth_manager.ensure_valid_token():
933
  logger.warning(f"Auth token validation failed during health check for {email}")
934
  auth_manager.clear_auth()
935
  else:
936
  logger.info(f"Health check passed for {email}")
937
- else:
938
- logger.info(f"Skipping health check for {email} due to rate limiting")
939
-
940
- # 更新检查索引
941
- check_index = (check_index + 1) % len(multi_auth_manager.auth_managers)
942
 
943
- # 在每天午夜重置所有账号的模型使用状态
944
  current_time_local = time.localtime()
945
  if current_time_local.tm_hour == 0 and current_time_local.tm_min == 0:
946
  multi_auth_manager.reset_all_model_status()
@@ -949,7 +899,7 @@ def health_check():
949
  except Exception as e:
950
  logger.error(f"Health check error: {e}")
951
 
952
- sleep(60) # 每60秒检查一个账号
953
 
954
  # 为了兼容 Flask CLI 和 Gunicorn,修改启动逻辑
955
  if __name__ != "__main__":
@@ -962,3 +912,4 @@ if __name__ == "__main__":
962
 
963
  port = int(os.environ.get("PORT", 3000))
964
  app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
 
 
107
  return session
108
 
109
  # 添加速率限制相关的常量
110
+ AUTH_RETRY_DELAY = 60 # 认证重试延迟(秒)
111
  AUTH_BACKOFF_FACTOR = 2 # 退避因子
112
  AUTH_MAX_RETRIES = 3 # 最大重试次数
113
  AUTH_CHECK_INTERVAL = 300 # 健康检查间隔(秒)
 
309
  self.current_index = 0
310
  self._last_rotation = time.time()
311
  self._rotation_interval = 300 # 5分钟轮转间隔
 
 
 
312
 
313
+ def _should_rotate(self) -> bool:
314
+ """检查是否应该轮转到下一个账号"""
315
+ return time.time() - self._last_rotation >= self._rotation_interval
 
 
 
 
 
 
 
 
 
 
316
 
317
+ def get_next_auth_manager(self, model):
318
+ """改进的账号选择逻辑"""
319
+ if self._should_rotate():
320
+ self.current_index = (self.current_index + 1) % len(self.auth_managers)
321
+ self._last_rotation = time.time()
322
 
323
+ start_index = self.current_index
324
+ for _ in range(len(self.auth_managers)):
325
+ auth_manager = self.auth_managers[self.current_index]
326
+ if auth_manager.is_model_available(model) and auth_manager._should_attempt_auth():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  return auth_manager
328
+ self.current_index = (self.current_index + 1) % len(self.auth_managers)
329
+ if self.current_index == start_index:
330
+ break
331
  return None
332
 
333
+ def ensure_valid_token(self, model):
334
+ for _ in range(len(self.auth_managers)):
335
+ auth_manager = self.get_next_auth_manager(model)
336
+ if auth_manager and auth_manager.ensure_valid_token():
337
+ return auth_manager
338
+ return None
 
339
 
340
  def reset_all_model_status(self):
 
341
  for auth_manager in self.auth_managers:
342
  auth_manager.reset_model_status()
 
 
343
 
344
  def require_auth(func: Callable) -> Callable:
345
  """装饰器,确保在调用API之前有有效的token。"""
 
546
  raise
547
 
548
  def generate_stream_response(response, model, prompt_tokens):
549
+ """生成流式 HTTP 响应。"""
550
  total_completion_tokens = 0
551
 
552
  for chunk in stream_notdiamond_response(response, model):
 
677
  request_data = request.get_json()
678
  model_id = request_data.get('model', '')
679
 
680
+ auth_manager = multi_auth_manager.ensure_valid_token(model_id)
681
+ if not auth_manager:
682
+ return jsonify({'error': 'No available accounts for this model'}), 403
683
+
684
  stream = request_data.get('stream', False)
685
  prompt_tokens = count_message_tokens(
686
  request_data.get('messages', []),
687
  model_id
688
  )
689
  payload = build_payload(request_data, model_id)
690
+ response = make_request(payload, auth_manager, model_id)
 
 
 
691
  if stream:
692
  return Response(
693
  stream_with_context(generate_stream_response(response, model_id, prompt_tokens)),
 
707
  'details': str(e)
708
  }
709
  }), 503
710
+ except json.JSONDecodeError as e:
711
+ logger.error("JSON decode error: %s", str(e), exc_info=True)
712
+ return jsonify({
713
+ 'error': {
714
+ 'message': 'Invalid JSON in request',
715
+ 'type': 'invalid_request_error',
716
+ 'param': None,
717
+ 'code': None,
718
+ 'details': str(e)
719
+ }
720
+ }), 400
721
  except Exception as e:
722
  logger.error("Unexpected error: %s", str(e), exc_info=True)
723
  return jsonify({
 
731
  }), 500
732
 
733
  def build_payload(request_data, model_id):
734
+ """构建请求有效负载,确保保持完整的上下文。"""
735
  messages = request_data.get('messages', [])
736
 
737
  # 检查是否已经存在系统消息
 
779
  return payload
780
 
781
  def make_request(payload, auth_manager, model_id):
782
+ """发送请求并处理可能的认证刷新和模型特定错误。"""
783
  global multi_auth_manager
784
  max_retries = 3
785
  retry_delay = 1
 
786
 
787
  logger.info(f"尝试发送请求,模型:{model_id}")
788
 
 
799
  else:
800
  raise Exception("无法注册新账号")
801
 
802
+ # 记录已尝试的账号
803
  tried_accounts = set()
804
 
805
  while len(tried_accounts) < len(multi_auth_manager.auth_managers):
806
+ auth_manager = multi_auth_manager.get_next_auth_manager(model_id)
807
  if not auth_manager:
808
  break
809
 
810
+ # 如果这个账号已经尝试过,继续下一个
811
  if auth_manager._email in tried_accounts:
812
  continue
813
 
 
818
  try:
819
  url = get_notdiamond_url()
820
  headers = get_notdiamond_headers(auth_manager)
 
821
  response = executor.submit(
822
  requests.post,
823
  url,
824
  headers=headers,
825
  json=payload,
826
+ stream=True
827
+ ).result()
 
828
 
829
  if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
830
  logger.info(f"请求成功,使用账号 {auth_manager._email}")
 
 
 
831
  return response
832
 
833
  headers_cache.clear()
 
844
 
845
  logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
846
 
 
 
 
847
  except Exception as e:
848
  logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
849
  if attempt < max_retries - 1:
 
863
  raise Exception("所有账号均不可用,且注册新账号失败")
864
 
865
  def health_check():
866
+ """改进的健康检查函数"""
867
+ last_check_time = {} # 用于跟踪每个账号的最后检查时间
 
868
 
869
  while True:
870
  try:
871
  if multi_auth_manager:
872
+ current_time = time.time()
873
 
874
+ for auth_manager in multi_auth_manager.auth_managers:
 
 
 
 
 
 
 
 
 
875
  email = auth_manager._email
876
 
877
+ # 检查是否需要进行健康检查
878
+ if email not in last_check_time or \
879
+ current_time - last_check_time[email] >= AUTH_CHECK_INTERVAL:
880
+
881
+ if not auth_manager._should_attempt_auth():
882
+ logger.info(f"Skipping health check for {email} due to rate limiting")
883
+ continue
884
+
885
  if not auth_manager.ensure_valid_token():
886
  logger.warning(f"Auth token validation failed during health check for {email}")
887
  auth_manager.clear_auth()
888
  else:
889
  logger.info(f"Health check passed for {email}")
890
+
891
+ last_check_time[email] = current_time
 
 
 
892
 
893
+ # 每天重置所有账号的模型使用状态
894
  current_time_local = time.localtime()
895
  if current_time_local.tm_hour == 0 and current_time_local.tm_min == 0:
896
  multi_auth_manager.reset_all_model_status()
 
899
  except Exception as e:
900
  logger.error(f"Health check error: {e}")
901
 
902
+ sleep(60) # 主循环每分钟运行一次
903
 
904
  # 为了兼容 Flask CLI 和 Gunicorn,修改启动逻辑
905
  if __name__ != "__main__":
 
912
 
913
  port = int(os.environ.get("PORT", 3000))
914
  app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
915
+
gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text