yangtb24 commited on
Commit
6740b78
·
verified ·
1 Parent(s): 7191bab

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +769 -778
app.py CHANGED
@@ -7,6 +7,9 @@ import random
7
  import uuid
8
  import concurrent.futures
9
  import threading
 
 
 
10
  from datetime import datetime, timedelta
11
  from apscheduler.schedulers.background import BackgroundScheduler
12
  from flask import Flask, request, jsonify, Response, stream_with_context
@@ -545,9 +548,178 @@ def check_tokens():
545
  )
546
 
547
  return jsonify(results)
 
 
 
 
 
548
 
549
- @app.route('/handsome/v1/chat/completions', methods=['POST'])
550
- def handsome_chat_completions():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
  if not check_authorization(request):
552
  return jsonify({"error": "Unauthorized"}), 401
553
 
@@ -556,13 +728,11 @@ def handsome_chat_completions():
556
  return jsonify({"error": "Invalid request data"}), 400
557
 
558
  model_name = data['model']
559
-
560
  request_type = determine_request_type(
561
  model_name,
562
- text_models + image_models,
563
- free_text_models + free_image_models
564
  )
565
-
566
  api_key = select_key(request_type, model_name)
567
 
568
  if not api_key:
@@ -580,692 +750,23 @@ def handsome_chat_completions():
580
  "Authorization": f"Bearer {api_key}",
581
  "Content-Type": "application/json"
582
  }
583
-
584
- if model_name in image_models:
585
- # Handle image generation
586
- user_content = ""
587
- messages = data.get("messages", [])
588
- for message in messages:
589
- if message["role"] == "user":
590
- if isinstance(message["content"], str):
591
- user_content += message["content"] + " "
592
- elif isinstance(message["content"], list):
593
- for item in message["content"]:
594
- if (
595
- isinstance(item, dict) and
596
- item.get("type") == "text"
597
- ):
598
- user_content += (
599
- item.get("text", "") +
600
- " "
601
- )
602
- user_content = user_content.strip()
603
-
604
- # Map OpenAI-style parameters to SiliconFlow's parameters
605
- siliconflow_data = {
606
- "model": model_name,
607
- "prompt": user_content,
608
- "image_size": "1024x1024", # Default value
609
- "batch_size": 1, # Default value
610
- "num_inference_steps": 20, # Default value
611
- "guidance_scale": 7.5, # Default value
612
- "prompt_enhancement": False, # Default value
613
- }
614
 
615
- # Override with user's params (if provided)
616
- if data.get("size"):
617
- siliconflow_data["image_size"] = data.get("size")
618
- if data.get("n"):
619
- siliconflow_data["batch_size"] = data.get("n")
620
- if data.get("steps"):
621
- siliconflow_data["num_inference_steps"] = data.get("steps")
622
- if data.get("guidance_scale"):
623
- siliconflow_data["guidance_scale"] = data.get("guidance_scale")
624
- if data.get("negative_prompt"):
625
- siliconflow_data["negative_prompt"] = data.get("negative_prompt")
626
- if data.get("seed"):
627
- siliconflow_data["seed"] = data.get("seed")
628
 
629
- # Parameter validation and adjustments
630
- if siliconflow_data["batch_size"] < 1:
631
- siliconflow_data["batch_size"] = 1
632
- if siliconflow_data["batch_size"] > 4:
633
- siliconflow_data["batch_size"] = 4
634
 
635
- if siliconflow_data["num_inference_steps"] < 1:
636
- siliconflow_data["num_inference_steps"] = 1
637
- if siliconflow_data["num_inference_steps"] > 50:
638
- siliconflow_data["num_inference_steps"] = 50
639
-
640
- if siliconflow_data["guidance_scale"] < 0:
641
- siliconflow_data["guidance_scale"] = 0
642
- if siliconflow_data["guidance_scale"] > 100:
643
- siliconflow_data["guidance_scale"] = 100
644
-
645
- if siliconflow_data["image_size"] not in ["1024x1024", "512x1024", "768x512", "768x1024", "1024x576", "576x1024"]:
646
- siliconflow_data["image_size"] = "1024x1024"
647
-
648
- try:
649
- start_time = time.time()
650
- response = requests.post(
651
- "https://api.siliconflow.cn/v1/images/generations",
652
- headers=headers,
653
- json=siliconflow_data,
654
- timeout=120,
655
- stream=data.get("stream", False)
656
- )
657
-
658
- if response.status_code == 429:
659
- return jsonify(response.json()), 429
660
-
661
- if data.get("stream", False):
662
- def generate():
663
- first_chunk_time = None
664
- full_response_content = ""
665
- try:
666
- response.raise_for_status()
667
- end_time = time.time()
668
- response_json = response.json()
669
- total_time = end_time - start_time
670
-
671
- images = response_json.get("images", [])
672
-
673
- # Extract the first URL if available
674
- image_url = ""
675
- if images and isinstance(images[0], dict) and "url" in images[0]:
676
- image_url = images[0]["url"]
677
- logging.info(f"Extracted image URL: {image_url}")
678
- elif images and isinstance(images[0], str):
679
- image_url = images[0]
680
- logging.info(f"Extracted image URL: {image_url}")
681
-
682
- markdown_image_link = f"![image]({image_url})"
683
- if image_url:
684
- chunk_data = {
685
- "id": f"chatcmpl-{uuid.uuid4()}",
686
- "object": "chat.completion.chunk",
687
- "created": int(time.time()),
688
- "model": model_name,
689
- "choices": [
690
- {
691
- "index": 0,
692
- "delta": {
693
- "role": "assistant",
694
- "content": markdown_image_link
695
- },
696
- "finish_reason": None
697
- }
698
- ]
699
- }
700
- yield f"data: {json.dumps(chunk_data)}\n\n".encode('utf-8')
701
- full_response_content = markdown_image_link
702
- else:
703
- chunk_data = {
704
- "id": f"chatcmpl-{uuid.uuid4()}",
705
- "object": "chat.completion.chunk",
706
- "created": int(time.time()),
707
- "model": model_name,
708
- "choices": [
709
- {
710
- "index": 0,
711
- "delta": {
712
- "role": "assistant",
713
- "content": "Failed to generate image"
714
- },
715
- "finish_reason": None
716
- }
717
- ]
718
- }
719
- yield f"data: {json.dumps(chunk_data)}\n\n".encode('utf-8')
720
- full_response_content = "Failed to generate image"
721
-
722
- end_chunk_data = {
723
- "id": f"chatcmpl-{uuid.uuid4()}",
724
- "object": "chat.completion.chunk",
725
- "created": int(time.time()),
726
- "model": model_name,
727
- "choices": [
728
- {
729
- "index": 0,
730
- "delta": {},
731
- "finish_reason": "stop"
732
- }
733
- ]
734
- }
735
- yield f"data: {json.dumps(end_chunk_data)}\n\n".encode('utf-8')
736
-
737
- with data_lock:
738
- request_timestamps.append(time.time())
739
- token_counts.append(0) # Image generation doesn't use tokens
740
- except requests.exceptions.RequestException as e:
741
- logging.error(f"请求转发异常: {e}")
742
- error_chunk_data = {
743
- "id": f"chatcmpl-{uuid.uuid4()}",
744
- "object": "chat.completion.chunk",
745
- "created": int(time.time()),
746
- "model": model_name,
747
- "choices": [
748
- {
749
- "index": 0,
750
- "delta": {
751
- "role": "assistant",
752
- "content": f"Error: {str(e)}"
753
- },
754
- "finish_reason": None
755
- }
756
- ]
757
- }
758
- yield f"data: {json.dumps(error_chunk_data)}\n\n".encode('utf-8')
759
- end_chunk_data = {
760
- "id": f"chatcmpl-{uuid.uuid4()}",
761
- "object": "chat.completion.chunk",
762
- "created": int(time.time()),
763
- "model": model_name,
764
- "choices": [
765
- {
766
- "index": 0,
767
- "delta": {},
768
- "finish_reason": "stop"
769
- }
770
- ]
771
- }
772
- yield f"data: {json.dumps(end_chunk_data)}\n\n".encode('utf-8')
773
-
774
- logging.info(
775
- f"使用的key: {api_key}, "
776
- f"使用的模型: {model_name}"
777
- )
778
- yield "data: [DONE]\n\n".encode('utf-8')
779
- return Response(stream_with_context(generate()), content_type='text/event-stream')
780
- else:
781
- response.raise_for_status()
782
- end_time = time.time()
783
- response_json = response.json()
784
- total_time = end_time - start_time
785
-
786
- try:
787
- images = response_json.get("images", [])
788
-
789
- # Extract the first URL if available
790
- image_url = ""
791
- if images and isinstance(images[0], dict) and "url" in images[0]:
792
- image_url = images[0]["url"]
793
- logging.info(f"Extracted image URL: {image_url}")
794
- elif images and isinstance(images[0], str):
795
- image_url = images[0]
796
- logging.info(f"Extracted image URL: {image_url}")
797
-
798
- markdown_image_link = f"![image]({image_url})"
799
- # Construct the expected JSON output - Mimicking OpenAI
800
- response_data = {
801
- "id": f"chatcmpl-{uuid.uuid4()}",
802
- "object": "chat.completion",
803
- "created": int(time.time()),
804
- "model": model_name,
805
- "choices": [
806
- {
807
- "index": 0,
808
- "message": {
809
- "role": "assistant",
810
- "content": markdown_image_link if image_url else "Failed to generate image", # Directly return the URL in content
811
- },
812
- "finish_reason": "stop",
813
- }
814
- ],
815
- }
816
-
817
- except (KeyError, ValueError, IndexError) as e:
818
- logging.error(
819
- f"解析响应 JSON 失败: {e}, "
820
- f"完整内容: {response_json}"
821
- )
822
- response_data = {
823
- "id": f"chatcmpl-{uuid.uuid4()}",
824
- "object": "chat.completion",
825
- "created": int(time.time()),
826
- "model": model_name,
827
- "choices": [
828
- {
829
- "index": 0,
830
- "message": {
831
- "role": "assistant",
832
- "content": "Failed to process image data",
833
- },
834
- "finish_reason": "stop",
835
- }
836
- ],
837
- }
838
-
839
- logging.info(
840
- f"使用的key: {api_key}, "
841
- f"总共用时: {total_time:.4f}秒, "
842
- f"使用的模型: {model_name}"
843
- )
844
-
845
- with data_lock:
846
- request_timestamps.append(time.time())
847
- token_counts.append(0) # Image generation doesn't use tokens
848
-
849
- return jsonify(response_data)
850
- except requests.exceptions.RequestException as e:
851
- logging.error(f"请求转发异常: {e}")
852
- return jsonify({"error": str(e)}), 500
853
- else:
854
- # Existing text-based model handling logic
855
- try:
856
- start_time = time.time()
857
- response = requests.post(
858
- TEST_MODEL_ENDPOINT,
859
- headers=headers,
860
- json=data,
861
- stream=data.get("stream", False),
862
- timeout=60
863
- )
864
- if response.status_code == 429:
865
- return jsonify(response.json()), 429
866
-
867
- if data.get("stream", False):
868
- def generate():
869
- first_chunk_time = None
870
- full_response_content = ""
871
- for chunk in response.iter_content(chunk_size=1024):
872
- if chunk:
873
- if first_chunk_time is None:
874
- first_chunk_time = time.time()
875
- full_response_content += chunk.decode("utf-8")
876
- yield chunk
877
-
878
- end_time = time.time()
879
- first_token_time = (
880
- first_chunk_time - start_time
881
- if first_chunk_time else 0
882
- )
883
- total_time = end_time - start_time
884
-
885
- prompt_tokens = 0
886
- completion_tokens = 0
887
- response_content = ""
888
- for line in full_response_content.splitlines():
889
- if line.startswith("data:"):
890
- line = line[5:].strip()
891
- if line == "[DONE]":
892
- continue
893
- try:
894
- response_json = json.loads(line)
895
-
896
- if (
897
- "usage" in response_json and
898
- "completion_tokens" in response_json["usage"]
899
- ):
900
- completion_tokens = response_json[
901
- "usage"
902
- ]["completion_tokens"]
903
-
904
- if (
905
- "choices" in response_json and
906
- len(response_json["choices"]) > 0 and
907
- "delta" in response_json["choices"][0] and
908
- "content" in response_json[
909
- "choices"
910
- ][0]["delta"]
911
- ):
912
- response_content += response_json[
913
- "choices"
914
- ][0]["delta"]["content"]
915
-
916
- if (
917
- "usage" in response_json and
918
- "prompt_tokens" in response_json["usage"]
919
- ):
920
- prompt_tokens = response_json[
921
- "usage"
922
- ]["prompt_tokens"]
923
-
924
- except (
925
- KeyError,
926
- ValueError,
927
- IndexError
928
- ) as e:
929
- logging.error(
930
- f"解析流式响应单行 JSON 失败: {e}, "
931
- f"行内容: {line}"
932
- )
933
-
934
- user_content = ""
935
- messages = data.get("messages", [])
936
- for message in messages:
937
- if message["role"] == "user":
938
- if isinstance(message["content"], str):
939
- user_content += message["content"] + " "
940
- elif isinstance(message["content"], list):
941
- for item in message["content"]:
942
- if (
943
- isinstance(item, dict) and
944
- item.get("type") == "text"
945
- ):
946
- user_content += (
947
- item.get("text", "") +
948
- " "
949
- )
950
-
951
- user_content = user_content.strip()
952
-
953
- user_content_replaced = user_content.replace(
954
- '\n', '\\n'
955
- ).replace('\r', '\\n')
956
- response_content_replaced = response_content.replace(
957
- '\n', '\\n'
958
- ).replace('\r', '\\n')
959
-
960
- logging.info(
961
- f"使用的key: {api_key}, "
962
- f"提示token: {prompt_tokens}, "
963
- f"输出token: {completion_tokens}, "
964
- f"首字用时: {first_token_time:.4f}秒, "
965
- f"总共用时: {total_time:.4f}秒, "
966
- f"使用的模型: {model_name}, "
967
- f"用户的内容: {user_content_replaced}, "
968
- f"输出的内容: {response_content_replaced}"
969
- )
970
-
971
- with data_lock:
972
- request_timestamps.append(time.time())
973
- token_counts.append(prompt_tokens+completion_tokens)
974
-
975
- return Response(
976
- stream_with_context(generate()),
977
- content_type=response.headers['Content-Type']
978
- )
979
- else:
980
- response.raise_for_status()
981
- end_time = time.time()
982
- response_json = response.json()
983
- total_time = end_time - start_time
984
-
985
- try:
986
- prompt_tokens = response_json["usage"]["prompt_tokens"]
987
- completion_tokens = response_json[
988
- "usage"
989
- ]["completion_tokens"]
990
- response_content = response_json[
991
- "choices"
992
- ][0]["message"]["content"]
993
- except (KeyError, ValueError, IndexError) as e:
994
- logging.error(
995
- f"解析非流式响应 JSON 失败: {e}, "
996
- f"完整内容: {response_json}"
997
- )
998
- prompt_tokens = 0
999
- completion_tokens = 0
1000
- response_content = ""
1001
-
1002
- user_content = ""
1003
- messages = data.get("messages", [])
1004
- for message in messages:
1005
- if message["role"] == "user":
1006
- if isinstance(message["content"], str):
1007
- user_content += message["content"] + " "
1008
- elif isinstance(message["content"], list):
1009
- for item in message["content"]:
1010
- if (
1011
- isinstance(item, dict) and
1012
- item.get("type") == "text"
1013
- ):
1014
- user_content += (
1015
- item.get("text", "") +
1016
- " "
1017
- )
1018
-
1019
- user_content = user_content.strip()
1020
-
1021
- user_content_replaced = user_content.replace(
1022
- '\n', '\\n'
1023
- ).replace('\r', '\\n')
1024
- response_content_replaced = response_content.replace(
1025
- '\n', '\\n'
1026
- ).replace('\r', '\\n')
1027
-
1028
- logging.info(
1029
- f"使用的key: {api_key}, "
1030
- f"提示token: {prompt_tokens}, "
1031
- f"输出token: {completion_tokens}, "
1032
- f"首字用时: 0, "
1033
- f"总共用时: {total_time:.4f}秒, "
1034
- f"使用的模型: {model_name}, "
1035
- f"用户的内容: {user_content_replaced}, "
1036
- f"输出的内容: {response_content_replaced}"
1037
- )
1038
- with data_lock:
1039
- request_timestamps.append(time.time())
1040
- if "prompt_tokens" in response_json["usage"] and "completion_tokens" in response_json["usage"]:
1041
- token_counts.append(response_json["usage"]["prompt_tokens"] + response_json["usage"]["completion_tokens"])
1042
- else:
1043
- token_counts.append(0)
1044
-
1045
- return jsonify(response_json)
1046
-
1047
- except requests.exceptions.RequestException as e:
1048
- logging.error(f"请求转发异常: {e}")
1049
- return jsonify({"error": str(e)}), 500
1050
-
1051
- @app.route('/handsome/v1/models', methods=['GET'])
1052
- def list_models():
1053
- if not check_authorization(request):
1054
- return jsonify({"error": "Unauthorized"}), 401
1055
-
1056
- detailed_models = []
1057
-
1058
- for model in text_models:
1059
- detailed_models.append({
1060
- "id": model,
1061
- "object": "model",
1062
- "created": 1678888888,
1063
- "owned_by": "openai",
1064
- "permission": [
1065
- {
1066
- "id": f"modelperm-{uuid.uuid4().hex}",
1067
- "object": "model_permission",
1068
- "created": 1678888888,
1069
- "allow_create_engine": False,
1070
- "allow_sampling": True,
1071
- "allow_logprobs": True,
1072
- "allow_search_indices": False,
1073
- "allow_view": True,
1074
- "allow_fine_tuning": False,
1075
- "organization": "*",
1076
- "group": None,
1077
- "is_blocking": False
1078
- }
1079
- ],
1080
- "root": model,
1081
- "parent": None
1082
- })
1083
-
1084
- for model in embedding_models:
1085
- detailed_models.append({
1086
- "id": model,
1087
- "object": "model",
1088
- "created": 1678888888,
1089
- "owned_by": "openai",
1090
- "permission": [
1091
- {
1092
- "id": f"modelperm-{uuid.uuid4().hex}",
1093
- "object": "model_permission",
1094
- "created": 1678888888,
1095
- "allow_create_engine": False,
1096
- "allow_sampling": True,
1097
- "allow_logprobs": True,
1098
- "allow_search_indices": False,
1099
- "allow_view": True,
1100
- "allow_fine_tuning": False,
1101
- "organization": "*",
1102
- "group": None,
1103
- "is_blocking": False
1104
- }
1105
- ],
1106
- "root": model,
1107
- "parent": None
1108
- })
1109
-
1110
- for model in image_models:
1111
- detailed_models.append({
1112
- "id": model,
1113
- "object": "model",
1114
- "created": 1678888888,
1115
- "owned_by": "openai",
1116
- "permission": [
1117
- {
1118
- "id": f"modelperm-{uuid.uuid4().hex}",
1119
- "object": "model_permission",
1120
- "created": 1678888888,
1121
- "allow_create_engine": False,
1122
- "allow_sampling": True,
1123
- "allow_logprobs": True,
1124
- "allow_search_indices": False,
1125
- "allow_view": True,
1126
- "allow_fine_tuning": False,
1127
- "organization": "*",
1128
- "group": None,
1129
- "is_blocking": False
1130
- }
1131
- ],
1132
- "root": model,
1133
- "parent": None
1134
- })
1135
-
1136
- return jsonify({
1137
- "success": True,
1138
- "data": detailed_models
1139
- })
1140
-
1141
- def get_billing_info():
1142
- keys = valid_keys_global + unverified_keys_global
1143
- total_balance = 0
1144
-
1145
- with concurrent.futures.ThreadPoolExecutor(
1146
- max_workers=20
1147
- ) as executor:
1148
- futures = [
1149
- executor.submit(get_credit_summary, key) for key in keys
1150
- ]
1151
-
1152
- for future in concurrent.futures.as_completed(futures):
1153
- try:
1154
- credit_summary = future.result()
1155
- if credit_summary:
1156
- total_balance += credit_summary.get(
1157
- "total_balance",
1158
- 0
1159
- )
1160
- except Exception as exc:
1161
- logging.error(f"获取额度信息生成异常: {exc}")
1162
-
1163
- return total_balance
1164
-
1165
- @app.route('/handsome/v1/dashboard/billing/usage', methods=['GET'])
1166
- def billing_usage():
1167
- if not check_authorization(request):
1168
- return jsonify({"error": "Unauthorized"}), 401
1169
-
1170
- end_date = datetime.now()
1171
- start_date = end_date - timedelta(days=30)
1172
-
1173
- daily_usage = []
1174
- current_date = start_date
1175
- while current_date <= end_date:
1176
- daily_usage.append({
1177
- "timestamp": int(current_date.timestamp()),
1178
- "daily_usage": 0
1179
- })
1180
- current_date += timedelta(days=1)
1181
-
1182
- return jsonify({
1183
- "object": "list",
1184
- "data": daily_usage,
1185
- "total_usage": 0
1186
- })
1187
-
1188
- @app.route('/handsome/v1/dashboard/billing/subscription', methods=['GET'])
1189
- def billing_subscription():
1190
- if not check_authorization(request):
1191
- return jsonify({"error": "Unauthorized"}), 401
1192
-
1193
- total_balance = get_billing_info()
1194
-
1195
- return jsonify({
1196
- "object": "billing_subscription",
1197
- "has_payment_method": False,
1198
- "canceled": False,
1199
- "canceled_at": None,
1200
- "delinquent": None,
1201
- "access_until": int(datetime(9999, 12, 31).timestamp()),
1202
- "soft_limit": 0,
1203
- "hard_limit": total_balance,
1204
- "system_hard_limit": total_balance,
1205
- "soft_limit_usd": 0,
1206
- "hard_limit_usd": total_balance,
1207
- "system_hard_limit_usd": total_balance,
1208
- "plan": {
1209
- "name": "SiliconFlow API",
1210
- "id": "siliconflow-api"
1211
- },
1212
- "account_name": "SiliconFlow User",
1213
- "po_number": None,
1214
- "billing_email": None,
1215
- "tax_ids": [],
1216
- "billing_address": None,
1217
- "business_address": None
1218
- })
1219
-
1220
- @app.route('/handsome/v1/embeddings', methods=['POST'])
1221
- def handsome_embeddings():
1222
- if not check_authorization(request):
1223
- return jsonify({"error": "Unauthorized"}), 401
1224
-
1225
- data = request.get_json()
1226
- if not data or 'model' not in data:
1227
- return jsonify({"error": "Invalid request data"}), 400
1228
-
1229
- model_name = data['model']
1230
- request_type = determine_request_type(
1231
- model_name,
1232
- embedding_models,
1233
- free_embedding_models
1234
- )
1235
- api_key = select_key(request_type, model_name)
1236
-
1237
- if not api_key:
1238
- return jsonify(
1239
- {
1240
- "error": (
1241
- "No available API key for this "
1242
- "request type or all keys have "
1243
- "reached their limits"
1244
- )
1245
- }
1246
- ), 429
1247
-
1248
- headers = {
1249
- "Authorization": f"Bearer {api_key}",
1250
- "Content-Type": "application/json"
1251
- }
1252
-
1253
- try:
1254
- start_time = time.time()
1255
- response = requests.post(
1256
- EMBEDDINGS_ENDPOINT,
1257
- headers=headers,
1258
- json=data,
1259
- timeout=120
1260
- )
1261
-
1262
- if response.status_code == 429:
1263
- return jsonify(response.json()), 429
1264
-
1265
- response.raise_for_status()
1266
- end_time = time.time()
1267
- response_json = response.json()
1268
- total_time = end_time - start_time
1269
 
1270
  try:
1271
  prompt_tokens = response_json["usage"]["prompt_tokens"]
@@ -1302,10 +803,6 @@ def handsome_embeddings():
1302
  except requests.exceptions.RequestException as e:
1303
  return jsonify({"error": str(e)}), 500
1304
 
1305
- import base64
1306
- import io
1307
- from PIL import Image
1308
-
1309
  @app.route('/handsome/v1/images/generations', methods=['POST'])
1310
  def handsome_images_generations():
1311
  if not check_authorization(request):
@@ -1341,116 +838,610 @@ def handsome_images_generations():
1341
  "Content-Type": "application/json"
1342
  }
1343
 
1344
- response_data = {}
1345
-
1346
- if "stable-diffusion" in model_name:
1347
- # Map OpenAI-style parameters to SiliconFlow's parameters
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1348
  siliconflow_data = {
1349
  "model": model_name,
1350
- "prompt": data.get("prompt"),
1351
- "image_size": data.get("size", "1024x1024"),
1352
- "batch_size": data.get("n", 1),
1353
- "num_inference_steps": data.get("steps", 20),
1354
- "guidance_scale": data.get("guidance_scale", 7.5),
1355
- "negative_prompt": data.get("negative_prompt"),
1356
- "seed": data.get("seed"),
1357
  "prompt_enhancement": False,
1358
  }
1359
 
1360
- # Parameter validation and adjustments
1361
- if siliconflow_data["batch_size"] < 1:
1362
- siliconflow_data["batch_size"] = 1
1363
- if siliconflow_data["batch_size"] > 4:
1364
- siliconflow_data["batch_size"] = 4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1365
 
1366
- if siliconflow_data["num_inference_steps"] < 1:
1367
- siliconflow_data["num_inference_steps"] = 1
1368
- if siliconflow_data["num_inference_steps"] > 50:
1369
- siliconflow_data["num_inference_steps"] = 50
1370
-
1371
- if siliconflow_data["guidance_scale"] < 0:
1372
- siliconflow_data["guidance_scale"] = 0
1373
- if siliconflow_data["guidance_scale"] > 100:
1374
- siliconflow_data["guidance_scale"] = 100
1375
-
1376
- if siliconflow_data["image_size"] not in ["1024x1024", "512x1024", "768x512", "768x1024", "1024x576", "576x1024"]:
1377
- siliconflow_data["image_size"] = "1024x1024"
1378
-
1379
  try:
1380
  start_time = time.time()
1381
  response = requests.post(
1382
- "https://api.siliconflow.cn/v1/images/generations",
1383
  headers=headers,
1384
- json=siliconflow_data,
1385
- timeout=120
 
1386
  )
1387
-
1388
  if response.status_code == 429:
1389
  return jsonify(response.json()), 429
1390
 
1391
- response.raise_for_status()
1392
- end_time = time.time()
1393
- response_json = response.json()
1394
- total_time = end_time - start_time
1395
-
1396
- try:
1397
- images = response_json.get("images", [])
1398
- openai_images = []
1399
- for item in images:
1400
- if isinstance(item, dict) and "url" in item:
1401
- image_url = item["url"]
1402
- print(f"image_url: {image_url}") # 打印 URL
1403
- if data.get("response_format") == "b64_json":
1404
- try:
1405
- image_data = requests.get(image_url, stream=True).raw
1406
- image = Image.open(image_data)
1407
- buffered = io.BytesIO()
1408
- image.save(buffered, format="PNG")
1409
- img_str = base64.b64encode(buffered.getvalue()).decode()
1410
- openai_images.append({"b64_json": img_str})
1411
- except Exception as e:
1412
- logging.error(f"图片转base64失败: {e}")
1413
- openai_images.append({"url": image_url})
1414
- else:
1415
- openai_images.append({"url": image_url})
1416
- else:
1417
- logging.error(f"无效的图片数据: {item}")
1418
- openai_images.append({"url": item})
1419
 
 
 
 
 
 
 
1420
 
1421
- response_data = {
1422
- "created": int(time.time()),
1423
- "data": openai_images
1424
- }
 
 
 
 
 
 
1425
 
1426
- except (KeyError, ValueError, IndexError) as e:
1427
- logging.error(
1428
- f"解析响应 JSON 失败: {e}, "
1429
- f"完整内容: {response_json}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1430
  )
1431
- response_data = {
1432
- "created": int(time.time()),
1433
- "data": []
1434
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1435
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1436
 
1437
- logging.info(
1438
- f"使用的key: {api_key}, "
1439
- f"总共用时: {total_time:.4f}秒, "
1440
- f"使用的模型: {model_name}"
1441
- )
1442
 
1443
- with data_lock:
1444
- request_timestamps.append(time.time())
1445
- token_counts.append(0) # Image generation doesn't use tokens
 
 
 
1446
 
1447
- return jsonify(response_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1448
 
1449
  except requests.exceptions.RequestException as e:
1450
  logging.error(f"请求转发异常: {e}")
1451
- return jsonify({"error": str(e)}), 500
1452
- else:
1453
- return jsonify({"error": "Unsupported model"}), 400
1454
 
1455
  if __name__ == '__main__':
1456
  import json
 
7
  import uuid
8
  import concurrent.futures
9
  import threading
10
+ import base64
11
+ import io
12
+ from PIL import Image
13
  from datetime import datetime, timedelta
14
  from apscheduler.schedulers.background import BackgroundScheduler
15
  from flask import Flask, request, jsonify, Response, stream_with_context
 
548
  )
549
 
550
  return jsonify(results)
551
+
552
+ @app.route('/handsome/v1/models', methods=['GET'])
553
+ def list_models():
554
+ if not check_authorization(request):
555
+ return jsonify({"error": "Unauthorized"}), 401
556
 
557
+ detailed_models = []
558
+
559
+ for model in text_models:
560
+ detailed_models.append({
561
+ "id": model,
562
+ "object": "model",
563
+ "created": 1678888888,
564
+ "owned_by": "openai",
565
+ "permission": [
566
+ {
567
+ "id": f"modelperm-{uuid.uuid4().hex}",
568
+ "object": "model_permission",
569
+ "created": 1678888888,
570
+ "allow_create_engine": False,
571
+ "allow_sampling": True,
572
+ "allow_logprobs": True,
573
+ "allow_search_indices": False,
574
+ "allow_view": True,
575
+ "allow_fine_tuning": False,
576
+ "organization": "*",
577
+ "group": None,
578
+ "is_blocking": False
579
+ }
580
+ ],
581
+ "root": model,
582
+ "parent": None
583
+ })
584
+
585
+ for model in embedding_models:
586
+ detailed_models.append({
587
+ "id": model,
588
+ "object": "model",
589
+ "created": 1678888888,
590
+ "owned_by": "openai",
591
+ "permission": [
592
+ {
593
+ "id": f"modelperm-{uuid.uuid4().hex}",
594
+ "object": "model_permission",
595
+ "created": 1678888888,
596
+ "allow_create_engine": False,
597
+ "allow_sampling": True,
598
+ "allow_logprobs": True,
599
+ "allow_search_indices": False,
600
+ "allow_view": True,
601
+ "allow_fine_tuning": False,
602
+ "organization": "*",
603
+ "group": None,
604
+ "is_blocking": False
605
+ }
606
+ ],
607
+ "root": model,
608
+ "parent": None
609
+ })
610
+
611
+ for model in image_models:
612
+ detailed_models.append({
613
+ "id": model,
614
+ "object": "model",
615
+ "created": 1678888888,
616
+ "owned_by": "openai",
617
+ "permission": [
618
+ {
619
+ "id": f"modelperm-{uuid.uuid4().hex}",
620
+ "object": "model_permission",
621
+ "created": 1678888888,
622
+ "allow_create_engine": False,
623
+ "allow_sampling": True,
624
+ "allow_logprobs": True,
625
+ "allow_search_indices": False,
626
+ "allow_view": True,
627
+ "allow_fine_tuning": False,
628
+ "organization": "*",
629
+ "group": None,
630
+ "is_blocking": False
631
+ }
632
+ ],
633
+ "root": model,
634
+ "parent": None
635
+ })
636
+
637
+ return jsonify({
638
+ "success": True,
639
+ "data": detailed_models
640
+ })
641
+
642
+ def get_billing_info():
643
+ keys = valid_keys_global + unverified_keys_global
644
+ total_balance = 0
645
+
646
+ with concurrent.futures.ThreadPoolExecutor(
647
+ max_workers=20
648
+ ) as executor:
649
+ futures = [
650
+ executor.submit(get_credit_summary, key) for key in keys
651
+ ]
652
+
653
+ for future in concurrent.futures.as_completed(futures):
654
+ try:
655
+ credit_summary = future.result()
656
+ if credit_summary:
657
+ total_balance += credit_summary.get(
658
+ "total_balance",
659
+ 0
660
+ )
661
+ except Exception as exc:
662
+ logging.error(f"获取额度信息生成异常: {exc}")
663
+
664
+ return total_balance
665
+
666
+ @app.route('/handsome/v1/dashboard/billing/usage', methods=['GET'])
667
+ def billing_usage():
668
+ if not check_authorization(request):
669
+ return jsonify({"error": "Unauthorized"}), 401
670
+
671
+ end_date = datetime.now()
672
+ start_date = end_date - timedelta(days=30)
673
+
674
+ daily_usage = []
675
+ current_date = start_date
676
+ while current_date <= end_date:
677
+ daily_usage.append({
678
+ "timestamp": int(current_date.timestamp()),
679
+ "daily_usage": 0
680
+ })
681
+ current_date += timedelta(days=1)
682
+
683
+ return jsonify({
684
+ "object": "list",
685
+ "data": daily_usage,
686
+ "total_usage": 0
687
+ })
688
+
689
+ @app.route('/handsome/v1/dashboard/billing/subscription', methods=['GET'])
690
+ def billing_subscription():
691
+ if not check_authorization(request):
692
+ return jsonify({"error": "Unauthorized"}), 401
693
+
694
+ total_balance = get_billing_info()
695
+
696
+ return jsonify({
697
+ "object": "billing_subscription",
698
+ "has_payment_method": False,
699
+ "canceled": False,
700
+ "canceled_at": None,
701
+ "delinquent": None,
702
+ "access_until": int(datetime(9999, 12, 31).timestamp()),
703
+ "soft_limit": 0,
704
+ "hard_limit": total_balance,
705
+ "system_hard_limit": total_balance,
706
+ "soft_limit_usd": 0,
707
+ "hard_limit_usd": total_balance,
708
+ "system_hard_limit_usd": total_balance,
709
+ "plan": {
710
+ "name": "SiliconFlow API",
711
+ "id": "siliconflow-api"
712
+ },
713
+ "account_name": "SiliconFlow User",
714
+ "po_number": None,
715
+ "billing_email": None,
716
+ "tax_ids": [],
717
+ "billing_address": None,
718
+ "business_address": None
719
+ })
720
+
721
+ @app.route('/handsome/v1/embeddings', methods=['POST'])
722
+ def handsome_embeddings():
723
  if not check_authorization(request):
724
  return jsonify({"error": "Unauthorized"}), 401
725
 
 
728
  return jsonify({"error": "Invalid request data"}), 400
729
 
730
  model_name = data['model']
 
731
  request_type = determine_request_type(
732
  model_name,
733
+ embedding_models,
734
+ free_embedding_models
735
  )
 
736
  api_key = select_key(request_type, model_name)
737
 
738
  if not api_key:
 
750
  "Authorization": f"Bearer {api_key}",
751
  "Content-Type": "application/json"
752
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
 
754
+ try:
755
+ start_time = time.time()
756
+ response = requests.post(
757
+ EMBEDDINGS_ENDPOINT,
758
+ headers=headers,
759
+ json=data,
760
+ timeout=120
761
+ )
 
 
 
 
 
762
 
763
+ if response.status_code == 429:
764
+ return jsonify(response.json()), 429
 
 
 
765
 
766
+ response.raise_for_status()
767
+ end_time = time.time()
768
+ response_json = response.json()
769
+ total_time = end_time - start_time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
770
 
771
  try:
772
  prompt_tokens = response_json["usage"]["prompt_tokens"]
 
803
  except requests.exceptions.RequestException as e:
804
  return jsonify({"error": str(e)}), 500
805
 
 
 
 
 
806
  @app.route('/handsome/v1/images/generations', methods=['POST'])
807
  def handsome_images_generations():
808
  if not check_authorization(request):
 
838
  "Content-Type": "application/json"
839
  }
840
 
841
+ response_data = {}
842
+
843
+ if "stable-diffusion" in model_name:
844
+ siliconflow_data = {
845
+ "model": model_name,
846
+ "prompt": data.get("prompt"),
847
+ "image_size": data.get("size", "1024x1024"),
848
+ "batch_size": data.get("n", 1),
849
+ "num_inference_steps": data.get("steps", 20),
850
+ "guidance_scale": data.get("guidance_scale", 7.5),
851
+ "negative_prompt": data.get("negative_prompt"),
852
+ "seed": data.get("seed"),
853
+ "prompt_enhancement": False,
854
+ }
855
+
856
+ # Parameter validation and adjustments
857
+ if siliconflow_data["batch_size"] < 1:
858
+ siliconflow_data["batch_size"] = 1
859
+ if siliconflow_data["batch_size"] > 4:
860
+ siliconflow_data["batch_size"] = 4
861
+
862
+ if siliconflow_data["num_inference_steps"] < 1:
863
+ siliconflow_data["num_inference_steps"] = 1
864
+ if siliconflow_data["num_inference_steps"] > 50:
865
+ siliconflow_data["num_inference_steps"] = 50
866
+
867
+ if siliconflow_data["guidance_scale"] < 0:
868
+ siliconflow_data["guidance_scale"] = 0
869
+ if siliconflow_data["guidance_scale"] > 100:
870
+ siliconflow_data["guidance_scale"] = 100
871
+
872
+ if siliconflow_data["image_size"] not in ["1024x1024", "512x1024", "768x512", "768x1024", "1024x576", "576x1024"]:
873
+ siliconflow_data["image_size"] = "1024x1024"
874
+
875
+ try:
876
+ start_time = time.time()
877
+ response = requests.post(
878
+ "https://api.siliconflow.cn/v1/images/generations",
879
+ headers=headers,
880
+ json=siliconflow_data,
881
+ timeout=120
882
+ )
883
+
884
+ if response.status_code == 429:
885
+ return jsonify(response.json()), 429
886
+
887
+ response.raise_for_status()
888
+ end_time = time.time()
889
+ response_json = response.json()
890
+ total_time = end_time - start_time
891
+
892
+ try:
893
+ images = response_json.get("images", [])
894
+ openai_images = []
895
+ for item in images:
896
+ if isinstance(item, dict) and "url" in item:
897
+ image_url = item["url"]
898
+ print(f"image_url: {image_url}")
899
+ if data.get("response_format") == "b64_json":
900
+ try:
901
+ image_data = requests.get(image_url, stream=True).raw
902
+ image = Image.open(image_data)
903
+ buffered = io.BytesIO()
904
+ image.save(buffered, format="PNG")
905
+ img_str = base64.b64encode(buffered.getvalue()).decode()
906
+ openai_images.append({"b64_json": img_str})
907
+ except Exception as e:
908
+ logging.error(f"图片转base64失败: {e}")
909
+ openai_images.append({"url": image_url})
910
+ else:
911
+ openai_images.append({"url": image_url})
912
+ else:
913
+ logging.error(f"无效的图片数据: {item}")
914
+ openai_images.append({"url": item})
915
+
916
+
917
+ response_data = {
918
+ "created": int(time.time()),
919
+ "data": openai_images
920
+ }
921
+
922
+ except (KeyError, ValueError, IndexError) as e:
923
+ logging.error(
924
+ f"解析响应 JSON 失败: {e}, "
925
+ f"完整内容: {response_json}"
926
+ )
927
+ response_data = {
928
+ "created": int(time.time()),
929
+ "data": []
930
+ }
931
+
932
+
933
+ logging.info(
934
+ f"使用的key: {api_key}, "
935
+ f"总共用时: {total_time:.4f}秒, "
936
+ f"使用的模型: {model_name}"
937
+ )
938
+
939
+ with data_lock:
940
+ request_timestamps.append(time.time())
941
+ token_counts.append(0)
942
+
943
+ return jsonify(response_data)
944
+
945
+ except requests.exceptions.RequestException as e:
946
+ logging.error(f"请求转发异常: {e}")
947
+ return jsonify({"error": str(e)}), 500
948
+ else:
949
+ return jsonify({"error": "Unsupported model"}), 400
950
+
951
+ @app.route('/handsome/v1/chat/completions', methods=['POST'])
952
+ def handsome_chat_completions():
953
+ if not check_authorization(request):
954
+ return jsonify({"error": "Unauthorized"}), 401
955
+
956
+ data = request.get_json()
957
+ if not data or 'model' not in data:
958
+ return jsonify({"error": "Invalid request data"}), 400
959
+
960
+ model_name = data['model']
961
+
962
+ request_type = determine_request_type(
963
+ model_name,
964
+ text_models + image_models,
965
+ free_text_models + free_image_models
966
+ )
967
+
968
+ api_key = select_key(request_type, model_name)
969
+
970
+ if not api_key:
971
+ return jsonify(
972
+ {
973
+ "error": (
974
+ "No available API key for this "
975
+ "request type or all keys have "
976
+ "reached their limits"
977
+ )
978
+ }
979
+ ), 429
980
+
981
+ headers = {
982
+ "Authorization": f"Bearer {api_key}",
983
+ "Content-Type": "application/json"
984
+ }
985
+
986
+ if model_name in image_models:
987
+ # Handle image generation
988
+ user_content = ""
989
+ messages = data.get("messages", [])
990
+ for message in messages:
991
+ if message["role"] == "user":
992
+ if isinstance(message["content"], str):
993
+ user_content += message["content"] + " "
994
+ elif isinstance(message["content"], list):
995
+ for item in message["content"]:
996
+ if (
997
+ isinstance(item, dict) and
998
+ item.get("type") == "text"
999
+ ):
1000
+ user_content += (
1001
+ item.get("text", "") +
1002
+ " "
1003
+ )
1004
+ user_content = user_content.strip()
1005
+
1006
  siliconflow_data = {
1007
  "model": model_name,
1008
+ "prompt": user_content,
1009
+ "image_size": "1024x1024",
1010
+ "batch_size": 1,
1011
+ "num_inference_steps": 20,
1012
+ "guidance_scale": 7.5,
 
 
1013
  "prompt_enhancement": False,
1014
  }
1015
 
1016
+ if data.get("size"):
1017
+ siliconflow_data["image_size"] = data.get("size")
1018
+ if data.get("n"):
1019
+ siliconflow_data["batch_size"] = data.get("n")
1020
+ if data.get("steps"):
1021
+ siliconflow_data["num_inference_steps"] = data.get("steps")
1022
+ if data.get("guidance_scale"):
1023
+ siliconflow_data["guidance_scale"] = data.get("guidance_scale")
1024
+ if data.get("negative_prompt"):
1025
+ siliconflow_data["negative_prompt"] = data.get("negative_prompt")
1026
+ if data.get("seed"):
1027
+ siliconflow_data["seed"] = data.get("seed")
1028
+
1029
+ if siliconflow_data["batch_size"] < 1:
1030
+ siliconflow_data["batch_size"] = 1
1031
+ if siliconflow_data["batch_size"] > 4:
1032
+ siliconflow_data["batch_size"] = 4
1033
+
1034
+ if siliconflow_data["num_inference_steps"] < 1:
1035
+ siliconflow_data["num_inference_steps"] = 1
1036
+ if siliconflow_data["num_inference_steps"] > 50:
1037
+ siliconflow_data["num_inference_steps"] = 50
1038
+
1039
+ if siliconflow_data["guidance_scale"] < 0:
1040
+ siliconflow_data["guidance_scale"] = 0
1041
+ if siliconflow_data["guidance_scale"] > 100:
1042
+ siliconflow_data["guidance_scale"] = 100
1043
+
1044
+ if siliconflow_data["image_size"] not in ["1024x1024", "512x1024", "768x512", "768x1024", "1024x576", "576x1024"]:
1045
+ siliconflow_data["image_size"] = "1024x1024"
1046
+
1047
+ try:
1048
+ start_time = time.time()
1049
+ response = requests.post(
1050
+ "https://api.siliconflow.cn/v1/images/generations",
1051
+ headers=headers,
1052
+ json=siliconflow_data,
1053
+ timeout=120,
1054
+ stream=data.get("stream", False)
1055
+ )
1056
+
1057
+ if response.status_code == 429:
1058
+ return jsonify(response.json()), 429
1059
+
1060
+ if data.get("stream", False):
1061
+ def generate():
1062
+ first_chunk_time = None
1063
+ full_response_content = ""
1064
+ try:
1065
+ response.raise_for_status()
1066
+ end_time = time.time()
1067
+ response_json = response.json()
1068
+ total_time = end_time - start_time
1069
+
1070
+ images = response_json.get("images", [])
1071
+
1072
+ image_url = ""
1073
+ if images and isinstance(images[0], dict) and "url" in images[0]:
1074
+ image_url = images[0]["url"]
1075
+ logging.info(f"Extracted image URL: {image_url}")
1076
+ elif images and isinstance(images[0], str):
1077
+ image_url = images[0]
1078
+ logging.info(f"Extracted image URL: {image_url}")
1079
+
1080
+ markdown_image_link = f"![image]({image_url})"
1081
+ if image_url:
1082
+ chunk_data = {
1083
+ "id": f"chatcmpl-{uuid.uuid4()}",
1084
+ "object": "chat.completion.chunk",
1085
+ "created": int(time.time()),
1086
+ "model": model_name,
1087
+ "choices": [
1088
+ {
1089
+ "index": 0,
1090
+ "delta": {
1091
+ "role": "assistant",
1092
+ "content": markdown_image_link
1093
+ },
1094
+ "finish_reason": None
1095
+ }
1096
+ ]
1097
+ }
1098
+ yield f"data: {json.dumps(chunk_data)}\n\n".encode('utf-8')
1099
+ full_response_content = markdown_image_link
1100
+ else:
1101
+ chunk_data = {
1102
+ "id": f"chatcmpl-{uuid.uuid4()}",
1103
+ "object": "chat.completion.chunk",
1104
+ "created": int(time.time()),
1105
+ "model": model_name,
1106
+ "choices": [
1107
+ {
1108
+ "index": 0,
1109
+ "delta": {
1110
+ "role": "assistant",
1111
+ "content": "Failed to generate image"
1112
+ },
1113
+ "finish_reason": None
1114
+ }
1115
+ ]
1116
+ }
1117
+ yield f"data: {json.dumps(chunk_data)}\n\n".encode('utf-8')
1118
+ full_response_content = "Failed to generate image"
1119
+
1120
+ end_chunk_data = {
1121
+ "id": f"chatcmpl-{uuid.uuid4()}",
1122
+ "object": "chat.completion.chunk",
1123
+ "created": int(time.time()),
1124
+ "model": model_name,
1125
+ "choices": [
1126
+ {
1127
+ "index": 0,
1128
+ "delta": {},
1129
+ "finish_reason": "stop"
1130
+ }
1131
+ ]
1132
+ }
1133
+ yield f"data: {json.dumps(end_chunk_data)}\n\n".encode('utf-8')
1134
+
1135
+ with data_lock:
1136
+ request_timestamps.append(time.time())
1137
+ token_counts.append(0)
1138
+ except requests.exceptions.RequestException as e:
1139
+ logging.error(f"请求转发异常: {e}")
1140
+ error_chunk_data = {
1141
+ "id": f"chatcmpl-{uuid.uuid4()}",
1142
+ "object": "chat.completion.chunk",
1143
+ "created": int(time.time()),
1144
+ "model": model_name,
1145
+ "choices": [
1146
+ {
1147
+ "index": 0,
1148
+ "delta": {
1149
+ "role": "assistant",
1150
+ "content": f"Error: {str(e)}"
1151
+ },
1152
+ "finish_reason": None
1153
+ }
1154
+ ]
1155
+ }
1156
+ yield f"data: {json.dumps(error_chunk_data)}\n\n".encode('utf-8')
1157
+ end_chunk_data = {
1158
+ "id": f"chatcmpl-{uuid.uuid4()}",
1159
+ "object": "chat.completion.chunk",
1160
+ "created": int(time.time()),
1161
+ "model": model_name,
1162
+ "choices": [
1163
+ {
1164
+ "index": 0,
1165
+ "delta": {},
1166
+ "finish_reason": "stop"
1167
+ }
1168
+ ]
1169
+ }
1170
+ yield f"data: {json.dumps(end_chunk_data)}\n\n".encode('utf-8')
1171
+
1172
+ logging.info(
1173
+ f"使用的key: {api_key}, "
1174
+ f"使用的模型: {model_name}"
1175
+ )
1176
+ yield "data: [DONE]\n\n".encode('utf-8')
1177
+ return Response(stream_with_context(generate()), content_type='text/event-stream')
1178
+ else:
1179
+ response.raise_for_status()
1180
+ end_time = time.time()
1181
+ response_json = response.json()
1182
+ total_time = end_time - start_time
1183
+
1184
+ try:
1185
+ images = response_json.get("images", [])
1186
+
1187
+ image_url = ""
1188
+ if images and isinstance(images[0], dict) and "url" in images[0]:
1189
+ image_url = images[0]["url"]
1190
+ logging.info(f"Extracted image URL: {image_url}")
1191
+ elif images and isinstance(images[0], str):
1192
+ image_url = images[0]
1193
+ logging.info(f"Extracted image URL: {image_url}")
1194
+
1195
+ markdown_image_link = f"![image]({image_url})"
1196
+ response_data = {
1197
+ "id": f"chatcmpl-{uuid.uuid4()}",
1198
+ "object": "chat.completion",
1199
+ "created": int(time.time()),
1200
+ "model": model_name,
1201
+ "choices": [
1202
+ {
1203
+ "index": 0,
1204
+ "message": {
1205
+ "role": "assistant",
1206
+ "content": markdown_image_link if image_url else "Failed to generate image", # Directly return the URL in content
1207
+ },
1208
+ "finish_reason": "stop",
1209
+ }
1210
+ ],
1211
+ }
1212
+
1213
+ except (KeyError, ValueError, IndexError) as e:
1214
+ logging.error(
1215
+ f"解析响应 JSON 失败: {e}, "
1216
+ f"完整内容: {response_json}"
1217
+ )
1218
+ response_data = {
1219
+ "id": f"chatcmpl-{uuid.uuid4()}",
1220
+ "object": "chat.completion",
1221
+ "created": int(time.time()),
1222
+ "model": model_name,
1223
+ "choices": [
1224
+ {
1225
+ "index": 0,
1226
+ "message": {
1227
+ "role": "assistant",
1228
+ "content": "Failed to process image data",
1229
+ },
1230
+ "finish_reason": "stop",
1231
+ }
1232
+ ],
1233
+ }
1234
+
1235
+ logging.info(
1236
+ f"使用的key: {api_key}, "
1237
+ f"总共用时: {total_time:.4f}秒, "
1238
+ f"使用的模型: {model_name}"
1239
+ )
1240
+
1241
+ with data_lock:
1242
+ request_timestamps.append(time.time())
1243
+ token_counts.append(0)
1244
 
1245
+ return jsonify(response_data)
1246
+ except requests.exceptions.RequestException as e:
1247
+ logging.error(f"请求转发异常: {e}")
1248
+ return jsonify({"error": str(e)}), 500
1249
+ else:
 
 
 
 
 
 
 
 
1250
  try:
1251
  start_time = time.time()
1252
  response = requests.post(
1253
+ TEST_MODEL_ENDPOINT,
1254
  headers=headers,
1255
+ json=data,
1256
+ stream=data.get("stream", False),
1257
+ timeout=60
1258
  )
 
1259
  if response.status_code == 429:
1260
  return jsonify(response.json()), 429
1261
 
1262
+ if data.get("stream", False):
1263
+ def generate():
1264
+ first_chunk_time = None
1265
+ full_response_content = ""
1266
+ for chunk in response.iter_content(chunk_size=1024):
1267
+ if chunk:
1268
+ if first_chunk_time is None:
1269
+ first_chunk_time = time.time()
1270
+ full_response_content += chunk.decode("utf-8")
1271
+ yield chunk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1272
 
1273
+ end_time = time.time()
1274
+ first_token_time = (
1275
+ first_chunk_time - start_time
1276
+ if first_chunk_time else 0
1277
+ )
1278
+ total_time = end_time - start_time
1279
 
1280
+ prompt_tokens = 0
1281
+ completion_tokens = 0
1282
+ response_content = ""
1283
+ for line in full_response_content.splitlines():
1284
+ if line.startswith("data:"):
1285
+ line = line[5:].strip()
1286
+ if line == "[DONE]":
1287
+ continue
1288
+ try:
1289
+ response_json = json.loads(line)
1290
 
1291
+ if (
1292
+ "usage" in response_json and
1293
+ "completion_tokens" in response_json["usage"]
1294
+ ):
1295
+ completion_tokens = response_json[
1296
+ "usage"
1297
+ ]["completion_tokens"]
1298
+
1299
+ if (
1300
+ "choices" in response_json and
1301
+ len(response_json["choices"]) > 0 and
1302
+ "delta" in response_json["choices"][0] and
1303
+ "content" in response_json[
1304
+ "choices"
1305
+ ][0]["delta"]
1306
+ ):
1307
+ response_content += response_json[
1308
+ "choices"
1309
+ ][0]["delta"]["content"]
1310
+
1311
+ if (
1312
+ "usage" in response_json and
1313
+ "prompt_tokens" in response_json["usage"]
1314
+ ):
1315
+ prompt_tokens = response_json[
1316
+ "usage"
1317
+ ]["prompt_tokens"]
1318
+
1319
+ except (
1320
+ KeyError,
1321
+ ValueError,
1322
+ IndexError
1323
+ ) as e:
1324
+ logging.error(
1325
+ f"解析流式响应单行 JSON 失败: {e}, "
1326
+ f"行内容: {line}"
1327
+ )
1328
+
1329
+ user_content = ""
1330
+ messages = data.get("messages", [])
1331
+ for message in messages:
1332
+ if message["role"] == "user":
1333
+ if isinstance(message["content"], str):
1334
+ user_content += message["content"] + " "
1335
+ elif isinstance(message["content"], list):
1336
+ for item in message["content"]:
1337
+ if (
1338
+ isinstance(item, dict) and
1339
+ item.get("type") == "text"
1340
+ ):
1341
+ user_content += (
1342
+ item.get("text", "") +
1343
+ " "
1344
+ )
1345
+
1346
+ user_content = user_content.strip()
1347
+
1348
+ user_content_replaced = user_content.replace(
1349
+ '\n', '\\n'
1350
+ ).replace('\r', '\\n')
1351
+ response_content_replaced = response_content.replace(
1352
+ '\n', '\\n'
1353
+ ).replace('\r', '\\n')
1354
+
1355
+ logging.info(
1356
+ f"使用的key: {api_key}, "
1357
+ f"提示token: {prompt_tokens}, "
1358
+ f"输出token: {completion_tokens}, "
1359
+ f"首字用时: {first_token_time:.4f}秒, "
1360
+ f"总共用时: {total_time:.4f}秒, "
1361
+ f"使用的模型: {model_name}, "
1362
+ f"用户的内容: {user_content_replaced}, "
1363
+ f"输出的内容: {response_content_replaced}"
1364
+ )
1365
+
1366
+ with data_lock:
1367
+ request_timestamps.append(time.time())
1368
+ token_counts.append(prompt_tokens+completion_tokens)
1369
+
1370
+ return Response(
1371
+ stream_with_context(generate()),
1372
+ content_type=response.headers['Content-Type']
1373
  )
1374
+ else:
1375
+ response.raise_for_status()
1376
+ end_time = time.time()
1377
+ response_json = response.json()
1378
+ total_time = end_time - start_time
1379
+
1380
+ try:
1381
+ prompt_tokens = response_json["usage"]["prompt_tokens"]
1382
+ completion_tokens = response_json[
1383
+ "usage"
1384
+ ]["completion_tokens"]
1385
+ response_content = response_json[
1386
+ "choices"
1387
+ ][0]["message"]["content"]
1388
+ except (KeyError, ValueError, IndexError) as e:
1389
+ logging.error(
1390
+ f"解析非流式响应 JSON 失败: {e}, "
1391
+ f"完整内容: {response_json}"
1392
+ )
1393
+ prompt_tokens = 0
1394
+ completion_tokens = 0
1395
+ response_content = ""
1396
 
1397
+ user_content = ""
1398
+ messages = data.get("messages", [])
1399
+ for message in messages:
1400
+ if message["role"] == "user":
1401
+ if isinstance(message["content"], str):
1402
+ user_content += message["content"] + " "
1403
+ elif isinstance(message["content"], list):
1404
+ for item in message["content"]:
1405
+ if (
1406
+ isinstance(item, dict) and
1407
+ item.get("type") == "text"
1408
+ ):
1409
+ user_content += (
1410
+ item.get("text", "") +
1411
+ " "
1412
+ )
1413
 
1414
+ user_content = user_content.strip()
 
 
 
 
1415
 
1416
+ user_content_replaced = user_content.replace(
1417
+ '\n', '\\n'
1418
+ ).replace('\r', '\\n')
1419
+ response_content_replaced = response_content.replace(
1420
+ '\n', '\\n'
1421
+ ).replace('\r', '\\n')
1422
 
1423
+ logging.info(
1424
+ f"使用的key: {api_key}, "
1425
+ f"提示token: {prompt_tokens}, "
1426
+ f"输出token: {completion_tokens}, "
1427
+ f"首字用时: 0, "
1428
+ f"总共用时: {total_time:.4f}秒, "
1429
+ f"使用的模型: {model_name}, "
1430
+ f"用户的内容: {user_content_replaced}, "
1431
+ f"输出的内容: {response_content_replaced}"
1432
+ )
1433
+ with data_lock:
1434
+ request_timestamps.append(time.time())
1435
+ if "prompt_tokens" in response_json["usage"] and "completion_tokens" in response_json["usage"]:
1436
+ token_counts.append(response_json["usage"]["prompt_tokens"] + response_json["usage"]["completion_tokens"])
1437
+ else:
1438
+ token_counts.append(0)
1439
+
1440
+ return jsonify(response_json)
1441
 
1442
  except requests.exceptions.RequestException as e:
1443
  logging.error(f"请求转发异常: {e}")
1444
+ return jsonify({"error": str(e)}), 500
 
 
1445
 
1446
  if __name__ == '__main__':
1447
  import json