dan92 commited on
Commit
c305717
·
verified ·
1 Parent(s): 9c2cf37

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -112
app.py CHANGED
@@ -307,34 +307,39 @@ class MultiAuthManager:
307
  def __init__(self, credentials):
308
  self.auth_managers = [AuthManager(email, password) for email, password in credentials]
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):
@@ -445,7 +450,7 @@ def create_openai_chunk(content, model, finish_reason=None, usage=None):
445
  "delta": {"content": content} if content else {},
446
  "logprobs": None,
447
  "finish_reason": finish_reason,
448
- # 添加上下文相关信息
449
  "context_preserved": True
450
  }
451
  ]
@@ -472,22 +477,30 @@ def stream_notdiamond_response(response, model):
472
  buffer = ""
473
  full_content = ""
474
 
475
- for chunk in response.iter_content(chunk_size=1024):
476
  if chunk:
477
  try:
478
- new_content = chunk.decode('utf-8')
479
- buffer += new_content
480
- full_content += new_content
481
-
482
- # 创建完整的响应块
483
- chunk_data = create_openai_chunk(new_content, model)
484
-
485
- # 确保响应块包含完整的上下文
486
- if 'choices' in chunk_data and chunk_data['choices']:
487
- chunk_data['choices'][0]['delta']['content'] = new_content
488
- chunk_data['choices'][0]['context'] = full_content # 添加完整上下文
489
-
490
- yield chunk_data
 
 
 
 
 
 
 
 
491
 
492
  except Exception as e:
493
  logger.error(f"Error processing chunk: {e}")
@@ -496,20 +509,71 @@ def stream_notdiamond_response(response, model):
496
  # 发送完成标记
497
  final_chunk = create_openai_chunk('', model, 'stop')
498
  if 'choices' in final_chunk and final_chunk['choices']:
499
- final_chunk['choices'][0]['context'] = full_content # 在最终块中包含完整上下文
500
  yield final_chunk
501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  def handle_non_stream_response(response, model, prompt_tokens):
503
- """改进的非流式响应处理,确保保持完整上下文。"""
504
  full_content = ""
505
- context_buffer = []
506
 
507
  try:
508
- for chunk in response.iter_content(chunk_size=1024):
509
  if chunk:
510
- content = chunk.decode('utf-8')
511
- full_content += content
512
- context_buffer.append(content)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
 
514
  completion_tokens = count_tokens(full_content, model)
515
  total_tokens = prompt_tokens + completion_tokens
@@ -526,8 +590,7 @@ def handle_non_stream_response(response, model, prompt_tokens):
526
  "index": 0,
527
  "message": {
528
  "role": "assistant",
529
- "content": full_content,
530
- "context": ''.join(context_buffer) # 包含完整上下文
531
  },
532
  "finish_reason": "stop"
533
  }
@@ -545,24 +608,6 @@ def handle_non_stream_response(response, model, prompt_tokens):
545
  logger.error(f"Error processing non-stream response: {e}")
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):
553
- content = chunk['choices'][0]['delta'].get('content', '')
554
- total_completion_tokens += count_tokens(content, model)
555
-
556
- chunk['usage'] = {
557
- "prompt_tokens": prompt_tokens,
558
- "completion_tokens": total_completion_tokens,
559
- "total_tokens": prompt_tokens + total_completion_tokens
560
- }
561
-
562
- yield f"data: {json.dumps(chunk)}\n\n"
563
-
564
- yield "data: [DONE]\n\n"
565
-
566
  def get_auth_credentials():
567
  """从API获取认证凭据"""
568
  try:
@@ -862,54 +907,8 @@ def make_request(payload, auth_manager, model_id):
862
 
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()
897
- logger.info("Reset model status for all accounts")
898
-
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__":
906
- health_check_thread = threading.Thread(target=health_check, daemon=True)
907
- health_check_thread.start()
908
-
909
  if __name__ == "__main__":
910
- health_check_thread = threading.Thread(target=health_check, daemon=True)
911
- health_check_thread.start()
912
-
913
  port = int(os.environ.get("PORT", 3000))
914
  app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
915
 
 
307
  def __init__(self, credentials):
308
  self.auth_managers = [AuthManager(email, password) for email, password in credentials]
309
  self.current_index = 0
310
+ self.last_success_index = 0 # 记录上一次成功的账号索引
311
+ self.last_rotation_date = datetime.now().date() # 记录上次轮询的日期
 
 
 
 
312
 
313
  def get_next_auth_manager(self, model):
314
+ """改进的账号选择逻辑,从上次成功的账号开始尝试"""
315
+ current_date = datetime.now().date()
316
+
317
+ # 如果是新的一天,重置为从第一个账号开始
318
+ if current_date > self.last_rotation_date:
319
+ self.current_index = 0
320
+ self.last_success_index = 0
321
+ self.last_rotation_date = current_date
322
+ # 重置所有账号的模型状态
323
+ for auth_manager in self.auth_managers:
324
+ auth_manager.reset_model_status()
325
+ return self.auth_managers[0] if self.auth_managers else None
326
+
327
+ # 从上次成功的账号开始尝试
328
+ self.current_index = self.last_success_index
329
+ auth_manager = self.auth_managers[self.current_index]
330
+
331
+ # 如果当前账号可用,直接返回
332
+ if auth_manager.is_model_available(model) and auth_manager._should_attempt_auth():
333
+ return auth_manager
334
+
335
+ # 如果当前账号不可用,等待到第二天
336
  return None
337
 
338
  def ensure_valid_token(self, model):
339
+ auth_manager = self.get_next_auth_manager(model)
340
+ if auth_manager and auth_manager.ensure_valid_token():
341
+ self.last_success_index = self.current_index # 更新最后成功的账号索引
342
+ return auth_manager
343
  return None
344
 
345
  def reset_all_model_status(self):
 
450
  "delta": {"content": content} if content else {},
451
  "logprobs": None,
452
  "finish_reason": finish_reason,
453
+ # 添加上下文��关信息
454
  "context_preserved": True
455
  }
456
  ]
 
477
  buffer = ""
478
  full_content = ""
479
 
480
+ for chunk in response.iter_lines():
481
  if chunk:
482
  try:
483
+ chunk_str = chunk.decode('utf-8')
484
+ # 跳过SSE前缀
485
+ if chunk_str.startswith('data: '):
486
+ chunk_str = chunk_str[6:]
487
+ elif chunk_str == 'data: [DONE]':
488
+ continue
489
+
490
+ # 尝试解析JSON
491
+ try:
492
+ chunk_data = json.loads(chunk_str)
493
+ content = chunk_data.get('choices', [{}])[0].get('delta', {}).get('content', '')
494
+ if content:
495
+ full_content += content
496
+ chunk_data = create_openai_chunk(content, model)
497
+ yield chunk_data
498
+ except json.JSONDecodeError:
499
+ # 如果不是JSON格式��直接作为内容处理
500
+ if chunk_str.strip():
501
+ full_content += chunk_str
502
+ chunk_data = create_openai_chunk(chunk_str, model)
503
+ yield chunk_data
504
 
505
  except Exception as e:
506
  logger.error(f"Error processing chunk: {e}")
 
509
  # 发送完成标记
510
  final_chunk = create_openai_chunk('', model, 'stop')
511
  if 'choices' in final_chunk and final_chunk['choices']:
512
+ final_chunk['choices'][0]['context'] = full_content
513
  yield final_chunk
514
 
515
+ def generate_stream_response(response, model, prompt_tokens):
516
+ """生成流式 HTTP 响应,确保完整的上下文。"""
517
+ total_completion_tokens = 0
518
+ full_content = ""
519
+
520
+ for chunk in stream_notdiamond_response(response, model):
521
+ content = chunk['choices'][0]['delta'].get('content', '')
522
+ if content:
523
+ full_content += content
524
+ total_completion_tokens = count_tokens(full_content, model)
525
+
526
+ chunk['usage'] = {
527
+ "prompt_tokens": prompt_tokens,
528
+ "completion_tokens": total_completion_tokens,
529
+ "total_tokens": prompt_tokens + total_completion_tokens
530
+ }
531
+
532
+ # 确保每个块都包含完整的上下文
533
+ chunk['choices'][0]['context'] = full_content
534
+
535
+ yield f"data: {json.dumps(chunk)}\n\n"
536
+
537
+ # 发送最终的完成标记
538
+ final_chunk = create_openai_chunk('', model, 'stop')
539
+ final_chunk['choices'][0]['context'] = full_content
540
+ final_chunk['usage'] = {
541
+ "prompt_tokens": prompt_tokens,
542
+ "completion_tokens": total_completion_tokens,
543
+ "total_tokens": prompt_tokens + total_completion_tokens
544
+ }
545
+ yield f"data: {json.dumps(final_chunk)}\n\n"
546
+ yield "data: [DONE]\n\n"
547
+
548
  def handle_non_stream_response(response, model, prompt_tokens):
549
+ """改进的非流式响应处理,确保完整的上下文。"""
550
  full_content = ""
 
551
 
552
  try:
553
+ for chunk in response.iter_lines():
554
  if chunk:
555
+ try:
556
+ chunk_str = chunk.decode('utf-8')
557
+ # 跳过SSE前缀
558
+ if chunk_str.startswith('data: '):
559
+ chunk_str = chunk_str[6:]
560
+ elif chunk_str == 'data: [DONE]':
561
+ continue
562
+
563
+ # 尝试解析JSON
564
+ try:
565
+ chunk_data = json.loads(chunk_str)
566
+ content = chunk_data.get('choices', [{}])[0].get('delta', {}).get('content', '')
567
+ if content:
568
+ full_content += content
569
+ except json.JSONDecodeError:
570
+ # 如果不是JSON格式,直接作为内容处理
571
+ if chunk_str.strip():
572
+ full_content += chunk_str
573
+
574
+ except Exception as e:
575
+ logger.error(f"Error processing chunk in non-stream response: {e}")
576
+ continue
577
 
578
  completion_tokens = count_tokens(full_content, model)
579
  total_tokens = prompt_tokens + completion_tokens
 
590
  "index": 0,
591
  "message": {
592
  "role": "assistant",
593
+ "content": full_content
 
594
  },
595
  "finish_reason": "stop"
596
  }
 
608
  logger.error(f"Error processing non-stream response: {e}")
609
  raise
610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  def get_auth_credentials():
612
  """从API获取认证凭据"""
613
  try:
 
907
 
908
  raise Exception("所有账号均不可用,且注册新账号失败")
909
 
910
+ # 删除 health_check 函数和相关的线程启动代码
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
911
  if __name__ == "__main__":
 
 
 
912
  port = int(os.environ.get("PORT", 3000))
913
  app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
914