Update app.py
Browse files
app.py
CHANGED
@@ -56,17 +56,21 @@ session = requests_session_with_retries()
|
|
56 |
app = Flask(__name__)
|
57 |
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
70 |
|
71 |
executor = concurrent.futures.ThreadPoolExecutor(max_workers=10000)
|
72 |
model_key_indices = {}
|
@@ -112,30 +116,34 @@ FREE_IMAGE_LIST = [
|
|
112 |
"stabilityai/stable-diffusion-2-1"
|
113 |
]
|
114 |
|
115 |
-
def test_model_availability(api_key, model_name):
|
116 |
headers = {
|
117 |
"Authorization": f"Bearer {api_key}",
|
118 |
"Content-Type": "application/json"
|
119 |
}
|
|
|
|
|
|
|
|
|
120 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
response = session.post(
|
122 |
-
|
123 |
headers=headers,
|
124 |
-
json=
|
125 |
-
|
126 |
-
"messages": [{"role": "user", "content": "hi"}],
|
127 |
-
"max_tokens": 5,
|
128 |
-
"stream": False
|
129 |
-
},
|
130 |
-
timeout=5
|
131 |
)
|
132 |
-
|
133 |
-
return True
|
134 |
-
else:
|
135 |
-
return False
|
136 |
except requests.exceptions.RequestException as e:
|
137 |
logging.error(
|
138 |
-
f"
|
139 |
f"API Key:{api_key},错误信息:{e}"
|
140 |
)
|
141 |
return False
|
@@ -176,188 +184,109 @@ def create_base64_markdown_image(image_url):
|
|
176 |
return None
|
177 |
|
178 |
def refresh_models():
|
179 |
-
global
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
|
|
189 |
|
190 |
-
|
191 |
ban_models = []
|
|
|
192 |
if ban_models_str:
|
193 |
try:
|
194 |
ban_models = json.loads(ban_models_str)
|
195 |
if not isinstance(ban_models, list):
|
196 |
-
logging.warning(
|
197 |
-
"环境变量 BAN_MODELS 格式不正确,应为 JSON 数组。"
|
198 |
-
)
|
199 |
ban_models = []
|
200 |
except json.JSONDecodeError:
|
201 |
-
logging.warning(
|
202 |
-
"环境变量 BAN_MODELS JSON 解析失败,请检查格式。"
|
203 |
-
)
|
204 |
-
ban_models = []
|
205 |
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
model = future_to_model[future]
|
222 |
-
try:
|
223 |
-
is_free = future.result()
|
224 |
-
if is_free:
|
225 |
-
free_text_models.append(model)
|
226 |
-
except Exception as exc:
|
227 |
-
logging.error(f"模型 {model} 测试生成异常: {exc}")
|
228 |
-
|
229 |
-
with concurrent.futures.ThreadPoolExecutor(
|
230 |
-
max_workers=10000
|
231 |
-
) as executor:
|
232 |
-
future_to_model = {
|
233 |
-
executor.submit(
|
234 |
-
test_embedding_model_availability,
|
235 |
-
FREE_MODEL_TEST_KEY, model
|
236 |
-
): model for model in embedding_models
|
237 |
-
}
|
238 |
-
for future in concurrent.futures.as_completed(future_to_model):
|
239 |
-
model = future_to_model[future]
|
240 |
-
try:
|
241 |
-
is_free = future.result()
|
242 |
-
if is_free:
|
243 |
-
free_embedding_models.append(model)
|
244 |
-
except Exception as exc:
|
245 |
-
logging.error(f"模型 {model} 测试生成异常: {exc}")
|
246 |
-
|
247 |
-
with concurrent.futures.ThreadPoolExecutor(
|
248 |
-
max_workers=10000
|
249 |
-
) as executor:
|
250 |
-
future_to_model = {
|
251 |
-
executor.submit(
|
252 |
-
test_image_model_availability,
|
253 |
-
FREE_MODEL_TEST_KEY, model
|
254 |
-
): model for model in image_models
|
255 |
-
}
|
256 |
-
for future in concurrent.futures.as_completed(future_to_model):
|
257 |
-
model = future_to_model[future]
|
258 |
-
try:
|
259 |
-
is_free = future.result()
|
260 |
-
if is_free:
|
261 |
-
free_image_models.append(model)
|
262 |
-
except Exception as exc:
|
263 |
-
logging.error(f"模型 {model} 测试生成异常: {exc}")
|
264 |
-
|
265 |
-
logging.info(f"所有文本模型列表:{text_models}")
|
266 |
-
logging.info(f"免费文本模型列表:{free_text_models}")
|
267 |
-
logging.info(f"所有向量模型列表:{embedding_models}")
|
268 |
-
logging.info(f"免费向量模型列表:{free_embedding_models}")
|
269 |
-
logging.info(f"所有生图模型列表:{image_models}")
|
270 |
-
logging.info(f"免费生图模型列表:{free_image_models}")
|
271 |
-
|
272 |
-
def test_embedding_model_availability(api_key, model_name):
|
273 |
-
headers = {
|
274 |
-
"Authorization": f"Bearer {api_key}",
|
275 |
-
"Content-Type": "application/json"
|
276 |
-
}
|
277 |
-
try:
|
278 |
-
response = session.post(
|
279 |
-
EMBEDDINGS_ENDPOINT,
|
280 |
-
headers=headers,
|
281 |
-
json={
|
282 |
-
"model": model_name,
|
283 |
-
"input": ["hi"],
|
284 |
-
},
|
285 |
-
timeout=10
|
286 |
-
)
|
287 |
-
if response.status_code == 429 or response.status_code == 200:
|
288 |
-
return True
|
289 |
-
else:
|
290 |
-
return False
|
291 |
-
except requests.exceptions.RequestException as e:
|
292 |
-
logging.error(
|
293 |
-
f"测试向量模型 {model_name} 可用性失败,"
|
294 |
-
f"API Key:{api_key},错误信息:{e}"
|
295 |
-
)
|
296 |
-
return False
|
297 |
-
|
298 |
-
def test_image_model_availability(api_key, model_name):
|
299 |
-
return model_name in FREE_IMAGE_LIST
|
300 |
-
|
301 |
-
def load_keys():
|
302 |
-
keys_str = os.environ.get("KEYS")
|
303 |
-
test_model = os.environ.get(
|
304 |
-
"TEST_MODEL",
|
305 |
-
"Pro/google/gemma-2-9b-it"
|
306 |
-
)
|
307 |
-
|
308 |
-
if keys_str:
|
309 |
-
keys = [key.strip() for key in keys_str.split(',')]
|
310 |
-
unique_keys = list(set(keys))
|
311 |
-
keys_str = ','.join(unique_keys)
|
312 |
-
os.environ["KEYS"] = keys_str
|
313 |
-
|
314 |
-
logging.info(f"加载的 keys:{unique_keys}")
|
315 |
-
|
316 |
-
with concurrent.futures.ThreadPoolExecutor(
|
317 |
-
max_workers=10000
|
318 |
-
) as executor:
|
319 |
-
future_to_key = {
|
320 |
executor.submit(
|
321 |
-
|
322 |
-
|
|
|
|
|
|
|
323 |
}
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
unverified_keys = []
|
328 |
-
valid_keys = []
|
329 |
-
|
330 |
-
for future in concurrent.futures.as_completed(
|
331 |
-
future_to_key
|
332 |
-
):
|
333 |
-
key = future_to_key[future]
|
334 |
try:
|
335 |
-
|
336 |
-
if
|
337 |
-
|
338 |
-
elif key_type == "free":
|
339 |
-
free_keys.append(key)
|
340 |
-
elif key_type == "unverified":
|
341 |
-
unverified_keys.append(key)
|
342 |
-
elif key_type == "valid":
|
343 |
-
valid_keys.append(key)
|
344 |
except Exception as exc:
|
345 |
-
logging.error(f"
|
346 |
|
347 |
-
|
348 |
-
|
349 |
-
logging.info(f"
|
350 |
-
logging.info(f"
|
351 |
|
352 |
-
global invalid_keys_global, free_keys_global
|
353 |
-
global unverified_keys_global, valid_keys_global
|
354 |
-
invalid_keys_global = invalid_keys
|
355 |
-
free_keys_global = free_keys
|
356 |
-
unverified_keys_global = unverified_keys
|
357 |
-
valid_keys_global = valid_keys
|
358 |
|
359 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
logging.warning("环境变量 KEYS 未设置。")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
361 |
|
362 |
def process_key(key, test_model):
|
363 |
credit_summary = get_credit_summary(key)
|
@@ -525,7 +454,14 @@ def list_models():
|
|
525 |
|
526 |
detailed_models = []
|
527 |
|
528 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
529 |
detailed_models.append({
|
530 |
"id": model,
|
531 |
"object": "model",
|
@@ -596,14 +532,14 @@ def handsome_embeddings():
|
|
596 |
data = request.get_json()
|
597 |
if not data or 'model' not in data:
|
598 |
return jsonify({"error": "Invalid request data"}), 400
|
599 |
-
if data['model'] not in
|
600 |
return jsonify({"error": "Invalid model"}), 400
|
601 |
|
602 |
model_name = data['model']
|
603 |
request_type = determine_request_type(
|
604 |
model_name,
|
605 |
-
|
606 |
-
|
607 |
)
|
608 |
api_key = select_key(request_type, model_name)
|
609 |
|
@@ -677,15 +613,15 @@ def handsome_images_generations():
|
|
677 |
data = request.get_json()
|
678 |
if not data or 'model' not in data:
|
679 |
return jsonify({"error": "Invalid request data"}), 400
|
680 |
-
if data['model'] not in
|
681 |
return jsonify({"error": "Invalid model"}), 400
|
682 |
|
683 |
model_name = data.get('model')
|
684 |
|
685 |
request_type = determine_request_type(
|
686 |
model_name,
|
687 |
-
|
688 |
-
|
689 |
)
|
690 |
|
691 |
api_key = select_key(request_type, model_name)
|
@@ -848,15 +784,15 @@ def handsome_chat_completions():
|
|
848 |
data = request.get_json()
|
849 |
if not data or 'model' not in data:
|
850 |
return jsonify({"error": "Invalid request data"}), 400
|
851 |
-
if data['model'] not in
|
852 |
return jsonify({"error": "Invalid model"}), 400
|
853 |
|
854 |
model_name = data['model']
|
855 |
|
856 |
request_type = determine_request_type(
|
857 |
model_name,
|
858 |
-
|
859 |
-
|
860 |
)
|
861 |
|
862 |
api_key = select_key(request_type, model_name)
|
@@ -877,7 +813,7 @@ def handsome_chat_completions():
|
|
877 |
"Content-Type": "application/json"
|
878 |
}
|
879 |
|
880 |
-
if model_name in
|
881 |
user_content = ""
|
882 |
messages = data.get("messages", [])
|
883 |
for message in messages:
|
@@ -1175,7 +1111,7 @@ def handsome_chat_completions():
|
|
1175 |
headers=headers,
|
1176 |
json=data,
|
1177 |
stream=data.get("stream", False),
|
1178 |
-
timeout=
|
1179 |
)
|
1180 |
|
1181 |
if response.status_code == 429:
|
@@ -1388,4 +1324,4 @@ if __name__ == '__main__':
|
|
1388 |
debug=False,
|
1389 |
host='0.0.0.0',
|
1390 |
port=int(os.environ.get('PORT', 7860))
|
1391 |
-
)
|
|
|
56 |
app = Flask(__name__)
|
57 |
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
|
58 |
|
59 |
+
models = {
|
60 |
+
"text": [],
|
61 |
+
"free_text": [],
|
62 |
+
"embedding": [],
|
63 |
+
"free_embedding": [],
|
64 |
+
"image": [],
|
65 |
+
"free_image": []
|
66 |
+
}
|
67 |
+
|
68 |
+
key_status = {
|
69 |
+
"invalid": [],
|
70 |
+
"free": [],
|
71 |
+
"unverified": [],
|
72 |
+
"valid": []
|
73 |
+
}
|
74 |
|
75 |
executor = concurrent.futures.ThreadPoolExecutor(max_workers=10000)
|
76 |
model_key_indices = {}
|
|
|
116 |
"stabilityai/stable-diffusion-2-1"
|
117 |
]
|
118 |
|
119 |
+
def test_model_availability(api_key, model_name, model_type="chat"):
|
120 |
headers = {
|
121 |
"Authorization": f"Bearer {api_key}",
|
122 |
"Content-Type": "application/json"
|
123 |
}
|
124 |
+
|
125 |
+
if model_type == "image":
|
126 |
+
return model_name in FREE_IMAGE_LIST
|
127 |
+
|
128 |
try:
|
129 |
+
endpoint = EMBEDDINGS_ENDPOINT if model_type == "embedding" else TEST_MODEL_ENDPOINT
|
130 |
+
payload = (
|
131 |
+
{"model": model_name, "input": ["hi"]}
|
132 |
+
if model_type == "embedding"
|
133 |
+
else {"model": model_name, "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5, "stream": False}
|
134 |
+
)
|
135 |
+
timeout = 10 if model_type == "embedding" else 5
|
136 |
+
|
137 |
response = session.post(
|
138 |
+
endpoint,
|
139 |
headers=headers,
|
140 |
+
json=payload,
|
141 |
+
timeout=timeout
|
|
|
|
|
|
|
|
|
|
|
142 |
)
|
143 |
+
return response.status_code in [200, 429]
|
|
|
|
|
|
|
144 |
except requests.exceptions.RequestException as e:
|
145 |
logging.error(
|
146 |
+
f"测试{model_type}模型 {model_name} 可用性失败,"
|
147 |
f"API Key:{api_key},错误信息:{e}"
|
148 |
)
|
149 |
return False
|
|
|
184 |
return None
|
185 |
|
186 |
def refresh_models():
|
187 |
+
global models
|
188 |
+
|
189 |
+
# 获取各类型模型列表
|
190 |
+
models["text"] = get_all_models(FREE_MODEL_TEST_KEY, "chat")
|
191 |
+
models["embedding"] = get_all_models(FREE_MODEL_TEST_KEY, "embedding")
|
192 |
+
models["image"] = get_all_models(FREE_MODEL_TEST_KEY, "text-to-image")
|
193 |
+
|
194 |
+
# 重置免费模型列表
|
195 |
+
models["free_text"] = []
|
196 |
+
models["free_embedding"] = []
|
197 |
+
models["free_image"] = []
|
198 |
|
199 |
+
# 处理禁用模型
|
200 |
ban_models = []
|
201 |
+
ban_models_str = os.environ.get("BAN_MODELS")
|
202 |
if ban_models_str:
|
203 |
try:
|
204 |
ban_models = json.loads(ban_models_str)
|
205 |
if not isinstance(ban_models, list):
|
206 |
+
logging.warning("环境变量 BAN_MODELS 格式不正确,应为 JSON 数组。")
|
|
|
|
|
207 |
ban_models = []
|
208 |
except json.JSONDecodeError:
|
209 |
+
logging.warning("环境变量 BAN_MODELS JSON 解析失败,请检查格式。")
|
|
|
|
|
|
|
210 |
|
211 |
+
# 过滤禁用模型
|
212 |
+
models["text"] = [model for model in models["text"] if model not in ban_models]
|
213 |
+
models["embedding"] = [model for model in models["embedding"] if model not in ban_models]
|
214 |
+
models["image"] = [model for model in models["image"] if model not in ban_models]
|
215 |
+
|
216 |
+
# 使用统一的测试函数测试各类型模型
|
217 |
+
model_types = [
|
218 |
+
("text", "chat"),
|
219 |
+
("embedding", "embedding"),
|
220 |
+
("image", "image")
|
221 |
+
]
|
222 |
+
|
223 |
+
for model_type, test_type in model_types:
|
224 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=10000) as executor:
|
225 |
+
future_to_model = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
executor.submit(
|
227 |
+
test_model_availability,
|
228 |
+
FREE_MODEL_TEST_KEY,
|
229 |
+
model,
|
230 |
+
test_type
|
231 |
+
): model for model in models[model_type]
|
232 |
}
|
233 |
+
|
234 |
+
for future in concurrent.futures.as_completed(future_to_model):
|
235 |
+
model = future_to_model[future]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
try:
|
237 |
+
is_free = future.result()
|
238 |
+
if is_free:
|
239 |
+
models[f"free_{model_type}"].append(model)
|
|
|
|
|
|
|
|
|
|
|
|
|
240 |
except Exception as exc:
|
241 |
+
logging.error(f"{model_type}模型 {model} 测试生成异常: {exc}")
|
242 |
|
243 |
+
# 记录日志
|
244 |
+
for model_type in ["text", "embedding", "image"]:
|
245 |
+
logging.info(f"所有{model_type}模型列表:{models[model_type]}")
|
246 |
+
logging.info(f"免费{model_type}模型列表:{models[f'free_{model_type}']}")
|
247 |
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
|
249 |
+
def load_keys():
|
250 |
+
global key_status
|
251 |
+
# 重置key状态
|
252 |
+
for status in key_status:
|
253 |
+
key_status[status] = []
|
254 |
+
|
255 |
+
keys_str = os.environ.get("KEYS")
|
256 |
+
if not keys_str:
|
257 |
logging.warning("环境变量 KEYS 未设置。")
|
258 |
+
return
|
259 |
+
|
260 |
+
test_model = os.environ.get("TEST_MODEL", "Pro/google/gemma-2-9b-it")
|
261 |
+
unique_keys = list(set(key.strip() for key in keys_str.split(',')))
|
262 |
+
os.environ["KEYS"] = ','.join(unique_keys)
|
263 |
+
|
264 |
+
logging.info(f"加载的 keys:{unique_keys}")
|
265 |
+
|
266 |
+
def process_key_with_logging(key):
|
267 |
+
try:
|
268 |
+
key_type = process_key(key, test_model)
|
269 |
+
if key_type in key_status:
|
270 |
+
key_status[key_type].append(key)
|
271 |
+
return key_type
|
272 |
+
except Exception as exc:
|
273 |
+
logging.error(f"处理 KEY {key} 生成异常: {exc}")
|
274 |
+
return "invalid"
|
275 |
+
|
276 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=10000) as executor:
|
277 |
+
futures = [executor.submit(process_key_with_logging, key) for key in unique_keys]
|
278 |
+
concurrent.futures.wait(futures)
|
279 |
+
|
280 |
+
# 记录每种状态的keys
|
281 |
+
for status, keys in key_status.items():
|
282 |
+
logging.info(f"{status.capitalize()} KEYS: {keys}")
|
283 |
+
|
284 |
+
# 更新全局变量
|
285 |
+
global invalid_keys_global, free_keys_global, unverified_keys_global, valid_keys_global
|
286 |
+
invalid_keys_global = key_status["invalid"]
|
287 |
+
free_keys_global = key_status["free"]
|
288 |
+
unverified_keys_global = key_status["unverified"]
|
289 |
+
valid_keys_global = key_status["valid"]
|
290 |
|
291 |
def process_key(key, test_model):
|
292 |
credit_summary = get_credit_summary(key)
|
|
|
454 |
|
455 |
detailed_models = []
|
456 |
|
457 |
+
# 合并所有类型的模型
|
458 |
+
all_models = chain(
|
459 |
+
models["text"],
|
460 |
+
models["embedding"],
|
461 |
+
models["image"]
|
462 |
+
)
|
463 |
+
|
464 |
+
for model in all_models:
|
465 |
detailed_models.append({
|
466 |
"id": model,
|
467 |
"object": "model",
|
|
|
532 |
data = request.get_json()
|
533 |
if not data or 'model' not in data:
|
534 |
return jsonify({"error": "Invalid request data"}), 400
|
535 |
+
if data['model'] not in models["embedding"]:
|
536 |
return jsonify({"error": "Invalid model"}), 400
|
537 |
|
538 |
model_name = data['model']
|
539 |
request_type = determine_request_type(
|
540 |
model_name,
|
541 |
+
models["embedding"],
|
542 |
+
models["free_embedding"]
|
543 |
)
|
544 |
api_key = select_key(request_type, model_name)
|
545 |
|
|
|
613 |
data = request.get_json()
|
614 |
if not data or 'model' not in data:
|
615 |
return jsonify({"error": "Invalid request data"}), 400
|
616 |
+
if data['model'] not in models["image"]:
|
617 |
return jsonify({"error": "Invalid model"}), 400
|
618 |
|
619 |
model_name = data.get('model')
|
620 |
|
621 |
request_type = determine_request_type(
|
622 |
model_name,
|
623 |
+
models["image"],
|
624 |
+
models["free_image"]
|
625 |
)
|
626 |
|
627 |
api_key = select_key(request_type, model_name)
|
|
|
784 |
data = request.get_json()
|
785 |
if not data or 'model' not in data:
|
786 |
return jsonify({"error": "Invalid request data"}), 400
|
787 |
+
if data['model'] not in models["text"] and data['model'] not in models["image"]:
|
788 |
return jsonify({"error": "Invalid model"}), 400
|
789 |
|
790 |
model_name = data['model']
|
791 |
|
792 |
request_type = determine_request_type(
|
793 |
model_name,
|
794 |
+
models["text"] + models["image"],
|
795 |
+
models["free_text"] + models["free_image"]
|
796 |
)
|
797 |
|
798 |
api_key = select_key(request_type, model_name)
|
|
|
813 |
"Content-Type": "application/json"
|
814 |
}
|
815 |
|
816 |
+
if model_name in models["image"]:
|
817 |
user_content = ""
|
818 |
messages = data.get("messages", [])
|
819 |
for message in messages:
|
|
|
1111 |
headers=headers,
|
1112 |
json=data,
|
1113 |
stream=data.get("stream", False),
|
1114 |
+
timeout=600
|
1115 |
)
|
1116 |
|
1117 |
if response.status_code == 429:
|
|
|
1324 |
debug=False,
|
1325 |
host='0.0.0.0',
|
1326 |
port=int(os.environ.get('PORT', 7860))
|
1327 |
+
)
|