yangtb24 commited on
Commit
af14b51
·
verified ·
1 Parent(s): 0cfc696

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -660
app.py CHANGED
@@ -95,84 +95,6 @@ def get_credit_summary(api_key):
95
  logging.error(f"获取额度信息失败,API Key:{api_key},错误信息:{e}")
96
  return None
97
 
98
- FREE_IMAGE_LIST = [
99
- "stabilityai/stable-diffusion-3-5-large",
100
- "black-forest-labs/FLUX.1-schnell",
101
- "stabilityai/stable-diffusion-3-medium",
102
- "stabilityai/stable-diffusion-xl-base-1.0",
103
- "stabilityai/stable-diffusion-2-1"
104
- ]
105
-
106
- def test_model_availability(api_key, model_name, model_type="chat"):
107
- headers = {
108
- "Authorization": f"Bearer {api_key}",
109
- "Content-Type": "application/json"
110
- }
111
-
112
- if model_type == "image":
113
- return model_name in FREE_IMAGE_LIST
114
-
115
- try:
116
- endpoint = EMBEDDINGS_ENDPOINT if model_type == "embedding" else TEST_MODEL_ENDPOINT
117
- payload = (
118
- {"model": model_name, "input": ["hi"]}
119
- if model_type == "embedding"
120
- else {"model": model_name, "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5, "stream": False}
121
- )
122
- timeout = 10 if model_type == "embedding" else 5
123
-
124
- response = session.post(
125
- endpoint,
126
- headers=headers,
127
- json=payload,
128
- timeout=timeout
129
- )
130
- return response.status_code in [200, 429]
131
- except requests.exceptions.RequestException as e:
132
- logging.error(
133
- f"测试{model_type}模型 {model_name} 可用性失败,"
134
- f"API Key:{api_key},错误信息:{e}"
135
- )
136
- return False
137
-
138
- def process_image_url(image_url, response_format=None):
139
- if not image_url:
140
- return {"url": ""}
141
-
142
- if response_format == "b64_json":
143
- try:
144
- response = session.get(image_url, stream=True)
145
- response.raise_for_status()
146
- image = Image.open(response.raw)
147
- buffered = io.BytesIO()
148
- image.save(buffered, format="PNG")
149
- img_str = base64.b64encode(buffered.getvalue()).decode()
150
- return {"b64_json": img_str}
151
- except Exception as e:
152
- logging.error(f"图片转base64失败: {e}")
153
- return {"url": image_url}
154
- return {"url": image_url}
155
-
156
- def create_base64_markdown_image(image_url):
157
- try:
158
- response = session.get(image_url, stream=True)
159
- response.raise_for_status()
160
- image = Image.open(BytesIO(response.content))
161
-
162
- new_size = tuple(dim // 4 for dim in image.size)
163
- resized_image = image.resize(new_size, Image.LANCZOS)
164
-
165
- buffered = BytesIO()
166
- resized_image.save(buffered, format="PNG")
167
- base64_encoded = base64.b64encode(buffered.getvalue()).decode('utf-8')
168
-
169
- markdown_image_link = f"![](data:image/png;base64,{base64_encoded})"
170
- logging.info("Created base64 markdown image link.")
171
- return markdown_image_link
172
- except Exception as e:
173
- logging.error(f"Error creating markdown image: {e}")
174
- return None
175
-
176
  def extract_user_content(messages):
177
  user_content = ""
178
  for message in messages:
@@ -247,6 +169,9 @@ def load_keys():
247
  key_status[status] = []
248
 
249
  keys_str = os.environ.get("KEYS")
 
 
 
250
  if not keys_str:
251
  logging.warning("环境变量 KEYS 未设置。")
252
  return
@@ -440,244 +365,6 @@ def list_models():
440
  "data": detailed_models
441
  })
442
 
443
- @app.route('/handsome/v1/dashboard/billing/usage', methods=['GET'])
444
- def billing_usage():
445
- if not check_authorization(request):
446
- return jsonify({"error": "Unauthorized"}), 401
447
-
448
- daily_usage = []
449
-
450
- return jsonify({
451
- "object": "list",
452
- "data": daily_usage,
453
- "total_usage": 0
454
- })
455
-
456
- @app.route('/handsome/v1/dashboard/billing/subscription', methods=['GET'])
457
- def billing_subscription():
458
- if not check_authorization(request):
459
- return jsonify({"error": "Unauthorized"}), 401
460
-
461
- keys = valid_keys_global + unverified_keys_global
462
- total_balance = 0
463
-
464
- with concurrent.futures.ThreadPoolExecutor(
465
- max_workers=10000
466
- ) as executor:
467
- futures = [
468
- executor.submit(get_credit_summary, key) for key in keys
469
- ]
470
-
471
- for future in concurrent.futures.as_completed(futures):
472
- try:
473
- credit_summary = future.result()
474
- if credit_summary:
475
- total_balance += credit_summary.get("total_balance", 0)
476
- except Exception as exc:
477
- logging.error(f"获取额度信息生成异常: {exc}")
478
-
479
- return jsonify({
480
- "object": "billing_subscription",
481
- "access_until": int(datetime(9999, 12, 31).timestamp()),
482
- "soft_limit": 0,
483
- "hard_limit": total_balance,
484
- "system_hard_limit": total_balance,
485
- "soft_limit_usd": 0,
486
- "hard_limit_usd": total_balance,
487
- "system_hard_limit_usd": total_balance
488
- })
489
-
490
- @app.route('/handsome/v1/embeddings', methods=['POST'])
491
- def handsome_embeddings():
492
- if not check_authorization(request):
493
- return jsonify({"error": "Unauthorized"}), 401
494
-
495
- data = request.get_json()
496
- if not data or 'model' not in data:
497
- return jsonify({"error": "Invalid request data"}), 400
498
- if data['model'] not in models["embedding"]:
499
- return jsonify({"error": "Invalid model"}), 400
500
-
501
- model_name = data['model']
502
- request_type = determine_request_type(
503
- model_name,
504
- models["embedding"],
505
- models["free_embedding"]
506
- )
507
- api_key = select_key(request_type, model_name)
508
-
509
- if not api_key:
510
- return jsonify({"error": ("No available API key for this request type or all keys have reached their limits")}), 429
511
-
512
- headers = {
513
- "Authorization": f"Bearer {api_key}",
514
- "Content-Type": "application/json"
515
- }
516
-
517
- try:
518
- start_time = time.time()
519
- response = requests.post(
520
- EMBEDDINGS_ENDPOINT,
521
- headers=headers,
522
- json=data,
523
- timeout=120
524
- )
525
-
526
- if response.status_code == 429:
527
- return jsonify(response.json()), 429
528
-
529
- response.raise_for_status()
530
- end_time = time.time()
531
- response_json = response.json()
532
- total_time = end_time - start_time
533
-
534
- try:
535
- prompt_tokens = response_json["usage"]["prompt_tokens"]
536
- embedding_data = response_json["data"]
537
- except (KeyError, ValueError, IndexError) as e:
538
- logging.error(
539
- f"解析响应 JSON 失败: {e}, "
540
- f"完整内容: {response_json}"
541
- )
542
- prompt_tokens = 0
543
- embedding_data = []
544
-
545
- logging.info(
546
- f"使用的key: {api_key}, "
547
- f"提示token: {prompt_tokens}, "
548
- f"总共用时: {total_time:.4f}秒, "
549
- f"使用的模型: {model_name}"
550
- )
551
-
552
- with data_lock:
553
- request_timestamps.append(time.time())
554
- token_counts.append(prompt_tokens)
555
- request_timestamps_day.append(time.time())
556
- token_counts_day.append(prompt_tokens)
557
-
558
- return jsonify({
559
- "object": "list",
560
- "data": embedding_data,
561
- "model": model_name,
562
- "usage": {
563
- "prompt_tokens": prompt_tokens,
564
- "total_tokens": prompt_tokens
565
- }
566
- })
567
-
568
- except requests.exceptions.RequestException as e:
569
- return jsonify({"error": str(e)}), 500
570
-
571
- @app.route('/handsome/v1/images/generations', methods=['POST'])
572
- def handsome_images_generations():
573
- if not check_authorization(request):
574
- return jsonify({"error": "Unauthorized"}), 401
575
-
576
- data = request.get_json()
577
- if not data or 'model' not in data:
578
- return jsonify({"error": "Invalid request data"}), 400
579
- if data['model'] not in models["image"]:
580
- return jsonify({"error": "Invalid model"}), 400
581
-
582
- model_name = data.get('model')
583
-
584
- request_type = determine_request_type(
585
- model_name,
586
- models["image"],
587
- models["free_image"]
588
- )
589
-
590
- api_key = select_key(request_type, model_name)
591
-
592
- if not api_key:
593
- return jsonify({"error": ("No available API key for this request type or all keys have reached their limits")}), 429
594
-
595
- headers = {
596
- "Authorization": f"Bearer {api_key}",
597
- "Content-Type": "application/json"
598
- }
599
-
600
- response_data = {}
601
-
602
- if "stable-diffusion" in model_name or model_name in ["black-forest-labs/FLUX.1-schnell", "Pro/black-forest-labs/FLUX.1-schnell","black-forest-labs/FLUX.1-dev", "black-forest-labs/FLUX.1-pro"]:
603
- siliconflow_data = get_siliconflow_data(model_name, data)
604
-
605
- try:
606
- start_time = time.time()
607
- response = requests.post(
608
- IMAGE_ENDPOINT,
609
- headers=headers,
610
- json=siliconflow_data,
611
- timeout=120
612
- )
613
-
614
- if response.status_code == 429:
615
- return jsonify(response.json()), 429
616
-
617
- response.raise_for_status()
618
- end_time = time.time()
619
- response_json = response.json()
620
- total_time = end_time - start_time
621
-
622
- try:
623
- images = response_json.get("images", [])
624
- openai_images = []
625
- for item in images:
626
- if isinstance(item, dict) and "url" in item:
627
- image_url = item["url"]
628
- print(f"image_url: {image_url}")
629
- if data.get("response_format") == "b64_json":
630
- try:
631
- image_data = session.get(image_url, stream=True).raw
632
- image = Image.open(image_data)
633
- buffered = io.BytesIO()
634
- image.save(buffered, format="PNG")
635
- img_str = base64.b64encode(buffered.getvalue()).decode()
636
- openai_images.append({"b64_json": img_str})
637
- except Exception as e:
638
- logging.error(f"图片转base64失败: {e}")
639
- openai_images.append({"url": image_url})
640
- else:
641
- openai_images.append({"url": image_url})
642
- else:
643
- logging.error(f"无效的图片数据: {item}")
644
- openai_images.append({"url": item})
645
-
646
-
647
- response_data = {
648
- "created": int(time.time()),
649
- "data": openai_images
650
- }
651
- except (KeyError, ValueError, IndexError) as e:
652
- logging.error(
653
- f"解析响应 JSON 失败: {e}, "
654
- f"完整内容: {response_json}"
655
- )
656
- response_data = {
657
- "created": int(time.time()),
658
- "data": []
659
- }
660
-
661
- logging.info(
662
- f"使用的key: {api_key}, "
663
- f"总共用时: {total_time:.4f}秒, "
664
- f"使用的模型: {model_name}"
665
- )
666
-
667
- with data_lock:
668
- request_timestamps.append(time.time())
669
- token_counts.append(0)
670
- request_timestamps_day.append(time.time())
671
- token_counts_day.append(0)
672
-
673
- return jsonify(response_data)
674
-
675
- except requests.exceptions.RequestException as e:
676
- logging.error(f"请求转发异常: {e}")
677
- return jsonify({"error": str(e)}), 500
678
- else:
679
- return jsonify({"error": "Unsupported model"}), 400
680
-
681
  @app.route('/handsome/v1/chat/completions', methods=['POST'])
682
  def handsome_chat_completions():
683
  if not check_authorization(request):
@@ -715,343 +402,84 @@ def handsome_chat_completions():
715
  "Content-Type": "application/json"
716
  }
717
 
718
- if model_name in models["image"]:
719
- if isinstance(data.get("messages"), list):
720
- data = data.copy()
721
- data["prompt"] = extract_user_content(data["messages"])
722
- siliconflow_data = get_siliconflow_data(model_name, data)
723
-
724
- try:
725
- start_time = time.time()
726
- response = requests.post(
727
- IMAGE_ENDPOINT,
728
- headers=headers,
729
- json=siliconflow_data,
730
- stream=data.get("stream", False)
731
- )
732
-
733
- if response.status_code == 429:
734
- return jsonify(response.json()), 429
735
-
736
- if data.get("stream", False):
737
- def generate():
738
- try:
739
- response.raise_for_status()
740
- response_json = response.json()
741
-
742
- images = response_json.get("images", [])
743
-
744
- image_url = ""
745
- if images and isinstance(images[0], dict) and "url" in images[0]:
746
- image_url = images[0]["url"]
747
- logging.info(f"Extracted image URL: {image_url}")
748
- elif images and isinstance(images[0], str):
749
- image_url = images[0]
750
- logging.info(f"Extracted image URL: {image_url}")
751
-
752
- markdown_image_link = create_base64_markdown_image(image_url)
753
- if image_url:
754
- chunk_size = 8192
755
- for i in range(0, len(markdown_image_link), chunk_size):
756
- chunk = markdown_image_link[i:i + chunk_size]
757
- chunk_data = {
758
- "id": f"chatcmpl-{uuid.uuid4()}",
759
- "object": "chat.completion.chunk",
760
- "created": int(time.time()),
761
- "model": model_name,
762
- "choices": [
763
- {
764
- "index": 0,
765
- "delta": {
766
- "role": "assistant",
767
- "content": chunk
768
- },
769
- "finish_reason": None
770
- }
771
- ]
772
- }
773
- yield f"data: {json.dumps(chunk_data)}\n\n".encode('utf-8')
774
- else:
775
- chunk_data = {
776
- "id": f"chatcmpl-{uuid.uuid4()}",
777
- "object": "chat.completion.chunk",
778
- "created": int(time.time()),
779
- "model": model_name,
780
- "choices": [
781
- {
782
- "index": 0,
783
- "delta": {
784
- "role": "assistant",
785
- "content": "Failed to generate image"
786
- },
787
- "finish_reason": None
788
- }
789
- ]
790
- }
791
- yield f"data: {json.dumps(chunk_data)}\n\n".encode('utf-8')
792
-
793
- end_chunk_data = {
794
- "id": f"chatcmpl-{uuid.uuid4()}",
795
- "object": "chat.completion.chunk",
796
- "created": int(time.time()),
797
- "model": model_name,
798
- "choices": [
799
- {
800
- "index": 0,
801
- "delta": {},
802
- "finish_reason": "stop"
803
- }
804
- ]
805
- }
806
- yield f"data: {json.dumps(end_chunk_data)}\n\n".encode('utf-8')
807
- with data_lock:
808
- request_timestamps.append(time.time())
809
- token_counts.append(0)
810
- request_timestamps_day.append(time.time())
811
- token_counts_day.append(0)
812
- except requests.exceptions.RequestException as e:
813
- logging.error(f"请求转发异常: {e}")
814
- error_chunk_data = {
815
- "id": f"chatcmpl-{uuid.uuid4()}",
816
- "object": "chat.completion.chunk",
817
- "created": int(time.time()),
818
- "model": model_name,
819
- "choices": [
820
- {
821
- "index": 0,
822
- "delta": {
823
- "role": "assistant",
824
- "content": f"Error: {str(e)}"
825
- },
826
- "finish_reason": None
827
- }
828
- ]
829
- }
830
- yield f"data: {json.dumps(error_chunk_data)}\n\n".encode('utf-8')
831
- end_chunk_data = {
832
- "id": f"chatcmpl-{uuid.uuid4()}",
833
- "object": "chat.completion.chunk",
834
- "created": int(time.time()),
835
- "model": model_name,
836
- "choices": [
837
- {
838
- "index": 0,
839
- "delta": {},
840
- "finish_reason": "stop"
841
- }
842
- ]
843
- }
844
- yield f"data: {json.dumps(end_chunk_data)}\n\n".encode('utf-8')
845
- logging.info(
846
- f"使用的key: {api_key}, "
847
- f"使用的模型: {model_name}"
848
- )
849
- yield "data: [DONE]\n\n".encode('utf-8')
850
- return Response(stream_with_context(generate()), content_type='text/event-stream')
851
-
852
- else:
853
- response.raise_for_status()
854
- end_time = time.time()
855
- response_json = response.json()
856
- total_time = end_time - start_time
857
-
858
- try:
859
- images = response_json.get("images", [])
860
-
861
- image_url = ""
862
- if images and isinstance(images[0], dict) and "url" in images[0]:
863
- image_url = images[0]["url"]
864
- logging.info(f"Extracted image URL: {image_url}")
865
- elif images and isinstance(images[0], str):
866
- image_url = images[0]
867
- logging.info(f"Extracted image URL: {image_url}")
868
-
869
- markdown_image_link = f"![image]({image_url})"
870
- response_data = {
871
- "id": f"chatcmpl-{uuid.uuid4()}",
872
- "object": "chat.completion",
873
- "created": int(time.time()),
874
- "model": model_name,
875
- "choices": [
876
- {
877
- "index": 0,
878
- "message": {
879
- "role": "assistant",
880
- "content": markdown_image_link if image_url else "Failed to generate image",
881
- },
882
- "finish_reason": "stop",
883
- }
884
- ],
885
- }
886
- except (KeyError, ValueError, IndexError) as e:
887
- logging.error(
888
- f"解析响应 JSON 失败: {e}, "
889
- f"完整内容: {response_json}"
890
- )
891
- response_data = {
892
- "id": f"chatcmpl-{uuid.uuid4()}",
893
- "object": "chat.completion",
894
- "created": int(time.time()),
895
- "model": model_name,
896
- "choices": [
897
- {
898
- "index": 0,
899
- "message": {
900
- "role": "assistant",
901
- "content": "Failed to process image data",
902
- },
903
- "finish_reason": "stop",
904
- }
905
- ],
906
- }
907
 
908
- logging.info(
909
- f"使用的key: {api_key}, "
910
- f"总共用时: {total_time:.4f}秒, "
911
- f"使用的模型: {model_name}"
912
- )
913
- with data_lock:
914
- request_timestamps.append(time.time())
915
- token_counts.append(0)
916
- request_timestamps_day.append(time.time())
917
- token_counts_day.append(0)
918
- return jsonify(response_data)
919
 
920
- except requests.exceptions.RequestException as e:
921
- logging.error(f"请求转发异常: {e}")
922
- return jsonify({"error": str(e)}), 500
923
- else:
924
- try:
925
- start_time = time.time()
926
- response = requests.post(
927
- TEST_MODEL_ENDPOINT,
928
- headers=headers,
929
- json=data,
930
- stream=data.get("stream", False)
931
- )
932
 
933
- if response.status_code == 429:
934
- return jsonify(response.json()), 429
935
-
936
- if data.get("stream", False):
937
- def generate():
938
- first_chunk_time = None
939
- full_response_content = ""
940
- for chunk in response.iter_content(chunk_size=2048):
941
- if chunk:
942
- if first_chunk_time is None:
943
- first_chunk_time = time.time()
944
- full_response_content += chunk.decode("utf-8")
945
- yield chunk
946
-
947
- end_time = time.time()
948
- first_token_time = (
949
- first_chunk_time - start_time
950
- if first_chunk_time else 0
951
- )
952
- total_time = end_time - start_time
953
-
954
- prompt_tokens = 0
955
- completion_tokens = 0
956
- response_content = ""
957
- for line in full_response_content.splitlines():
958
- if line.startswith("data:"):
959
- line = line[5:].strip()
960
- if line == "[DONE]":
961
- continue
962
- try:
963
- response_json = json.loads(line)
964
-
965
- if (
966
- "usage" in response_json and
967
- "completion_tokens" in response_json["usage"]
968
- ):
969
- completion_tokens = response_json[
970
- "usage"
971
- ]["completion_tokens"]
972
-
973
- if (
974
- "choices" in response_json and
975
- len(response_json["choices"]) > 0 and
976
- "delta" in response_json["choices"][0] and
977
- "content" in response_json[
978
- "choices"
979
- ][0]["delta"]
980
- ):
981
- response_content += response_json[
982
- "choices"
983
- ][0]["delta"]["content"]
984
-
985
- if (
986
- "usage" in response_json and
987
- "prompt_tokens" in response_json["usage"]
988
- ):
989
- prompt_tokens = response_json[
990
- "usage"
991
- ]["prompt_tokens"]
992
-
993
- except (
994
- KeyError,
995
- ValueError,
996
- IndexError
997
- ) as e:
998
- logging.error(
999
- f"解析流式响应单行 JSON 失败: {e}, "
1000
- f"行内容: {line}"
1001
- )
1002
-
1003
- user_content = extract_user_content(data.get("messages", []))
1004
-
1005
- user_content_replaced = user_content.replace(
1006
- '\n', '\\n'
1007
- ).replace('\r', '\\n')
1008
- response_content_replaced = response_content.replace(
1009
- '\n', '\\n'
1010
- ).replace('\r', '\\n')
1011
-
1012
- logging.info(
1013
- f"使用的key: {api_key}, "
1014
- f"提示token: {prompt_tokens}, "
1015
- f"输出token: {completion_tokens}, "
1016
- f"首字用时: {first_token_time:.4f}秒, "
1017
- f"总共用时: {total_time:.4f}秒, "
1018
- f"使用的模型: {model_name}, "
1019
- f"用户的内容: {user_content_replaced}, "
1020
- f"输出的内容: {response_content_replaced}"
1021
- )
1022
-
1023
- with data_lock:
1024
- request_timestamps.append(time.time())
1025
- token_counts.append(prompt_tokens+completion_tokens)
1026
- request_timestamps_day.append(time.time())
1027
- token_counts_day.append(prompt_tokens+completion_tokens)
1028
-
1029
- return Response(
1030
- stream_with_context(generate()),
1031
- content_type=response.headers['Content-Type']
1032
- )
1033
- else:
1034
- response.raise_for_status()
1035
  end_time = time.time()
1036
- response_json = response.json()
 
 
 
1037
  total_time = end_time - start_time
1038
 
1039
- try:
1040
- prompt_tokens = response_json["usage"]["prompt_tokens"]
1041
- completion_tokens = response_json[
1042
- "usage"
1043
- ]["completion_tokens"]
1044
- response_content = response_json[
1045
- "choices"
1046
- ][0]["message"]["content"]
1047
- except (KeyError, ValueError, IndexError) as e:
1048
- logging.error(
1049
- f"解析非流式响应 JSON 失败: {e}, "
1050
- f"完整内容: {response_json}"
1051
- )
1052
- prompt_tokens = 0
1053
- completion_tokens = 0
1054
- response_content = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1055
 
1056
  user_content = extract_user_content(data.get("messages", []))
1057
 
@@ -1066,29 +494,82 @@ def handsome_chat_completions():
1066
  f"使用的key: {api_key}, "
1067
  f"提示token: {prompt_tokens}, "
1068
  f"输出token: {completion_tokens}, "
1069
- f"首字用时: 0, "
1070
  f"总共用时: {total_time:.4f}秒, "
1071
  f"使用的模型: {model_name}, "
1072
  f"用户的内容: {user_content_replaced}, "
1073
  f"输出的内容: {response_content_replaced}"
1074
  )
 
1075
  with data_lock:
1076
  request_timestamps.append(time.time())
1077
- if "prompt_tokens" in response_json["usage"] and "completion_tokens" in response_json["usage"]:
1078
- token_counts.append(response_json["usage"]["prompt_tokens"] + response_json["usage"]["completion_tokens"])
1079
- else:
1080
- token_counts.append(0)
1081
  request_timestamps_day.append(time.time())
1082
- if "prompt_tokens" in response_json["usage"] and "completion_tokens" in response_json["usage"]:
1083
- token_counts_day.append(response_json["usage"]["prompt_tokens"] + response_json["usage"]["completion_tokens"])
1084
- else:
1085
- token_counts_day.append(0)
 
 
 
 
 
 
 
1086
 
1087
- return jsonify(response_json)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1088
 
1089
- except requests.exceptions.RequestException as e:
1090
- logging.error(f"请求转发异常: {e}")
1091
- return jsonify({"error": str(e)}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1092
 
1093
  if __name__ == '__main__':
1094
  logging.info(f"环境变量:{os.environ}")
 
95
  logging.error(f"获取额度信息失败,API Key:{api_key},错误信息:{e}")
96
  return None
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  def extract_user_content(messages):
99
  user_content = ""
100
  for message in messages:
 
169
  key_status[status] = []
170
 
171
  keys_str = os.environ.get("KEYS")
172
+
173
+ logging.info(f"The value of KEYS environment variable is: {keys_str}")
174
+
175
  if not keys_str:
176
  logging.warning("环境变量 KEYS 未设置。")
177
  return
 
365
  "data": detailed_models
366
  })
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  @app.route('/handsome/v1/chat/completions', methods=['POST'])
369
  def handsome_chat_completions():
370
  if not check_authorization(request):
 
402
  "Content-Type": "application/json"
403
  }
404
 
405
+ try:
406
+ start_time = time.time()
407
+ response = requests.post(
408
+ TEST_MODEL_ENDPOINT,
409
+ headers=headers,
410
+ json=data,
411
+ stream=data.get("stream", False)
412
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
414
+ if response.status_code == 429:
415
+ return jsonify(response.json()), 429
 
 
 
 
 
 
 
 
 
416
 
417
+ if data.get("stream", False):
418
+ def generate():
419
+ first_chunk_time = None
420
+ full_response_content = ""
421
+ for chunk in response.iter_content(chunk_size=2048):
422
+ if chunk:
423
+ if first_chunk_time is None:
424
+ first_chunk_time = time.time()
425
+ full_response_content += chunk.decode("utf-8")
426
+ yield chunk
 
 
427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  end_time = time.time()
429
+ first_token_time = (
430
+ first_chunk_time - start_time
431
+ if first_chunk_time else 0
432
+ )
433
  total_time = end_time - start_time
434
 
435
+ prompt_tokens = 0
436
+ completion_tokens = 0
437
+ response_content = ""
438
+ for line in full_response_content.splitlines():
439
+ if line.startswith("data:"):
440
+ line = line[5:].strip()
441
+ if line == "[DONE]":
442
+ continue
443
+ try:
444
+ response_json = json.loads(line)
445
+
446
+ if (
447
+ "usage" in response_json and
448
+ "completion_tokens" in response_json["usage"]
449
+ ):
450
+ completion_tokens = response_json[
451
+ "usage"
452
+ ]["completion_tokens"]
453
+
454
+ if (
455
+ "choices" in response_json and
456
+ len(response_json["choices"]) > 0 and
457
+ "delta" in response_json["choices"][0] and
458
+ "content" in response_json[
459
+ "choices"
460
+ ][0]["delta"]
461
+ ):
462
+ response_content += response_json[
463
+ "choices"
464
+ ][0]["delta"]["content"]
465
+
466
+ if (
467
+ "usage" in response_json and
468
+ "prompt_tokens" in response_json["usage"]
469
+ ):
470
+ prompt_tokens = response_json[
471
+ "usage"
472
+ ]["prompt_tokens"]
473
+
474
+ except (
475
+ KeyError,
476
+ ValueError,
477
+ IndexError
478
+ ) as e:
479
+ logging.error(
480
+ f"解析流式响应单行 JSON 失败: {e}, "
481
+ f"行内容: {line}"
482
+ )
483
 
484
  user_content = extract_user_content(data.get("messages", []))
485
 
 
494
  f"使用的key: {api_key}, "
495
  f"提示token: {prompt_tokens}, "
496
  f"输出token: {completion_tokens}, "
497
+ f"首字用时: {first_token_time:.4f}秒, "
498
  f"总共用时: {total_time:.4f}秒, "
499
  f"使用的模型: {model_name}, "
500
  f"用户的内容: {user_content_replaced}, "
501
  f"输出的内容: {response_content_replaced}"
502
  )
503
+
504
  with data_lock:
505
  request_timestamps.append(time.time())
506
+ token_counts.append(prompt_tokens+completion_tokens)
 
 
 
507
  request_timestamps_day.append(time.time())
508
+ token_counts_day.append(prompt_tokens+completion_tokens)
509
+
510
+ return Response(
511
+ stream_with_context(generate()),
512
+ content_type=response.headers['Content-Type']
513
+ )
514
+ else:
515
+ response.raise_for_status()
516
+ end_time = time.time()
517
+ response_json = response.json()
518
+ total_time = end_time - start_time
519
 
520
+ try:
521
+ prompt_tokens = response_json["usage"]["prompt_tokens"]
522
+ completion_tokens = response_json[
523
+ "usage"
524
+ ]["completion_tokens"]
525
+ response_content = response_json[
526
+ "choices"
527
+ ][0]["message"]["content"]
528
+ except (KeyError, ValueError, IndexError) as e:
529
+ logging.error(
530
+ f"解析非流式响应 JSON 失败: {e}, "
531
+ f"完整内容: {response_json}"
532
+ )
533
+ prompt_tokens = 0
534
+ completion_tokens = 0
535
+ response_content = ""
536
 
537
+ user_content = extract_user_content(data.get("messages", []))
538
+
539
+ user_content_replaced = user_content.replace(
540
+ '\n', '\\n'
541
+ ).replace('\r', '\\n')
542
+ response_content_replaced = response_content.replace(
543
+ '\n', '\\n'
544
+ ).replace('\r', '\\n')
545
+
546
+ logging.info(
547
+ f"使用的key: {api_key}, "
548
+ f"提示token: {prompt_tokens}, "
549
+ f"输出token: {completion_tokens}, "
550
+ f"首字用时: 0, "
551
+ f"总共用时: {total_time:.4f}秒, "
552
+ f"使用的模型: {model_name}, "
553
+ f"用户的内容: {user_content_replaced}, "
554
+ f"输出的内容: {response_content_replaced}"
555
+ )
556
+ with data_lock:
557
+ request_timestamps.append(time.time())
558
+ if "prompt_tokens" in response_json["usage"] and "completion_tokens" in response_json["usage"]:
559
+ token_counts.append(response_json["usage"]["prompt_tokens"] + response_json["usage"]["completion_tokens"])
560
+ else:
561
+ token_counts.append(0)
562
+ request_timestamps_day.append(time.time())
563
+ if "prompt_tokens" in response_json["usage"] and "completion_tokens" in response_json["usage"]:
564
+ token_counts_day.append(response_json["usage"]["prompt_tokens"] + response_json["usage"]["completion_tokens"])
565
+ else:
566
+ token_counts_day.append(0)
567
+
568
+ return jsonify(response_json)
569
+
570
+ except requests.exceptions.RequestException as e:
571
+ logging.error(f"请求转发异常: {e}")
572
+ return jsonify({"error": str(e)}), 500
573
 
574
  if __name__ == '__main__':
575
  logging.info(f"环境变量:{os.environ}")