yangtb24 commited on
Commit
91ee28f
1 Parent(s): 89fb80e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -85
app.py CHANGED
@@ -24,6 +24,12 @@ free_keys_global = []
24
  unverified_keys_global = []
25
  valid_keys_global = []
26
 
 
 
 
 
 
 
27
  def get_credit_summary(api_key):
28
  """
29
  使用 API 密钥获取额度信息。
@@ -48,7 +54,7 @@ def get_credit_summary(api_key):
48
  logging.error(f"total_balance 无法转换为浮点数,API Key:{api_key},错误信息:{e}")
49
  return None
50
 
51
- FREE_MODEL_TEST_KEY = "sk-bmjbjzleaqfgtqfzmcnsbagxrlohriadnxqrzfocbizaxukw" # 硬编码免费模型测试 KEY
52
 
53
  def test_model_availability(api_key, model_name):
54
  """
@@ -68,7 +74,6 @@ def test_model_availability(api_key, model_name):
68
  "stream": False
69
  },
70
  timeout=10)
71
- # 正常返回或返回 429 都被认为是免费模型
72
  if response.status_code == 429 or response.status_code == 200 :
73
  return True
74
  else:
@@ -83,16 +88,11 @@ def refresh_models():
83
  """
84
  global all_models, free_models
85
 
86
- # 使用 FREE_MODEL_TEST_KEY 获取所有模型列表
87
  all_models = get_all_models(FREE_MODEL_TEST_KEY)
88
-
89
  free_models = []
90
 
91
- # 使用 ThreadPoolExecutor 并发执行测试
92
- with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: # 限制最大工作线程数为 10,可根据实际情况调整
93
- # 创建一个模型到 future 的映射
94
  future_to_model = {executor.submit(test_model_availability, FREE_MODEL_TEST_KEY, model): model for model in all_models}
95
-
96
  for future in concurrent.futures.as_completed(future_to_model):
97
  model = future_to_model[future]
98
  try:
@@ -108,39 +108,43 @@ def refresh_models():
108
  def load_keys():
109
  """
110
  从环境变量中加载 keys,并根据额度和模型可用性进行分类,然后记录到日志中。
 
111
  """
112
  keys_str = os.environ.get("KEYS")
113
  test_model = os.environ.get("TEST_MODEL", "Pro/google/gemma-2-9b-it")
114
 
115
- invalid_keys = []
116
- free_keys = []
117
- unverified_keys = []
118
- valid_keys = []
119
-
120
  if keys_str:
121
  keys = [key.strip() for key in keys_str.split(',')]
122
  logging.info(f"加载的 keys:{keys}")
123
 
124
- for key in keys:
125
- credit_summary = get_credit_summary(key)
126
- if credit_summary is None:
127
- invalid_keys.append(key)
128
- else:
129
- total_balance = credit_summary.get("total_balance", 0)
130
- if total_balance <= 0:
131
- free_keys.append(key)
132
- else:
133
- if test_model_availability(key, test_model):
134
- valid_keys.append(key)
135
- else:
 
 
 
 
 
136
  unverified_keys.append(key)
 
 
 
 
137
 
138
  logging.info(f"无效 KEY:{invalid_keys}")
139
  logging.info(f"免费 KEY:{free_keys}")
140
  logging.info(f"未实名 KEY:{unverified_keys}")
141
  logging.info(f"有效 KEY:{valid_keys}")
142
-
143
- # 更新全局的 key 列表
144
  global invalid_keys_global, free_keys_global, unverified_keys_global, valid_keys_global
145
  invalid_keys_global = invalid_keys
146
  free_keys_global = free_keys
@@ -150,6 +154,23 @@ def load_keys():
150
  else:
151
  logging.warning("环境变量 KEYS 未设置。")
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  def get_all_models(api_key):
154
  """
155
  获取所有模型列表。
@@ -162,7 +183,6 @@ def get_all_models(api_key):
162
  response = requests.get(MODELS_ENDPOINT, headers=headers, params={"sub_type": "chat"})
163
  response.raise_for_status()
164
  data = response.json()
165
- # 确保 data 是字典且包含 'data' 键,'data' 对应的值是一个列表
166
  if isinstance(data, dict) and 'data' in data and isinstance(data['data'], list):
167
  return [model.get("id") for model in data["data"] if isinstance(model, dict) and "id" in model]
168
  else:
@@ -186,26 +206,59 @@ def determine_request_type(model_name):
186
  else:
187
  return "unknown"
188
 
189
- def select_key(request_type):
190
  """
191
- 根据请求类型选择合适的 KEY
192
  """
193
  if request_type == "free":
194
- # 免费请求:使用 2、3、4 类 KEY
195
  available_keys = free_keys_global + unverified_keys_global + valid_keys_global
196
  elif request_type == "paid":
197
- # 付费请求:使用 3、4 类 KEY
198
  available_keys = unverified_keys_global + valid_keys_global
199
  else:
200
- # 未知请求:使用所有 KEY
201
  available_keys = free_keys_global + unverified_keys_global + valid_keys_global
202
 
203
  if not available_keys:
204
  return None
205
 
206
- # 简单的轮询策略选择 KEY
207
- key = available_keys[int(time.time() * 1000) % len(available_keys)]
208
- return key
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
  def check_authorization(request):
211
  """
@@ -227,50 +280,50 @@ def check_authorization(request):
227
 
228
  return True
229
 
230
- # 创建一个后台调度器
231
  scheduler = BackgroundScheduler()
232
-
233
- # 添加定时任务,每小时执行一次 load_keys 函数
234
  scheduler.add_job(load_keys, 'interval', hours=1)
235
- # 添加定时任务,每10分钟执行一次 refresh_models 函数
236
  scheduler.add_job(refresh_models, 'interval', minutes=10)
237
 
238
  @app.route('/')
239
  def index():
240
- """
241
- 处理根路由的访问请求。
242
- """
243
  return "<h1>Welcome to SiliconFlow</h1>"
244
 
245
  @app.route('/check_tokens', methods=['POST'])
246
  def check_tokens():
247
  """
248
  处理前端发送的 Token 检测请求。
 
249
  """
250
  tokens = request.json.get('tokens', [])
251
  test_model = os.environ.get("TEST_MODEL", "Pro/google/gemma-2-9b-it")
252
 
253
- results = []
254
- for token in tokens:
255
- credit_summary = get_credit_summary(token)
256
- if credit_summary is None:
257
- results.append({"token": token, "type": "无效 KEY", "balance": 0, "message": "无法获取额度信息"})
258
- else:
259
- total_balance = credit_summary.get("total_balance", 0)
260
- if total_balance <= 0:
261
- results.append({"token": token, "type": "免费 KEY", "balance": total_balance, "message": "额度不足"})
262
- else:
263
- if test_model_availability(token, test_model):
264
- results.append({"token": token, "type": "有效 KEY", "balance": total_balance, "message": "可以使用指定模型"})
265
- else:
266
- results.append({"token": token, "type": "未实名 KEY", "balance": total_balance, "message": "无法使用指定模型"})
 
 
 
 
 
 
267
 
268
  return jsonify(results)
269
 
270
  @app.route('/handsome/v1/chat/completions', methods=['POST'])
271
  def handsome_chat_completions():
272
  """
273
- 处理 /handsome/v1/chat/completions 路由的请求,添加鉴权。
274
  """
275
  if not check_authorization(request):
276
  return jsonify({"error": "Unauthorized"}), 401
@@ -281,17 +334,16 @@ def handsome_chat_completions():
281
 
282
  model_name = data['model']
283
  request_type = determine_request_type(model_name)
284
- api_key = select_key(request_type)
285
 
286
  if not api_key:
287
- return jsonify({"error": "No available API key for this request type"}), 400
288
 
289
  headers = {
290
  "Authorization": f"Bearer {api_key}",
291
  "Content-Type": "application/json"
292
  }
293
 
294
- # 转发请求到真正的 API
295
  try:
296
  response = requests.post(
297
  TEST_MODEL_ENDPOINT,
@@ -300,8 +352,7 @@ def handsome_chat_completions():
300
  stream=data.get("stream", False),
301
  timeout=60
302
  )
303
-
304
- # 检查是否是429错误
305
  if response.status_code == 429:
306
  return jsonify(response.json()), 429
307
 
@@ -315,13 +366,9 @@ def handsome_chat_completions():
315
 
316
  @app.route('/handsome/v1/models', methods=['GET'])
317
  def list_models():
318
- """
319
- 处理 /handsome/v1/models 路由的请求,返回模型列表。
320
- """
321
  if not check_authorization(request):
322
  return jsonify({"error": "Unauthorized"}), 401
323
 
324
- # 返回模型列表和免费模型列表
325
  return jsonify({
326
  "data": [{"id": model, "object": "model"} for model in all_models],
327
  "free_models": free_models
@@ -330,26 +377,32 @@ def list_models():
330
  def get_billing_info():
331
  """
332
  获取所有KEY的额度信息。
 
333
  """
 
334
  total_balance = 0
335
- for key in valid_keys_global + unverified_keys_global:
336
- credit_summary = get_credit_summary(key)
337
- if credit_summary:
338
- total_balance += credit_summary.get("total_balance", 0)
 
 
 
 
 
 
 
 
339
  return total_balance
340
 
341
  @app.route('/handsome/v1/dashboard/billing/usage', methods=['GET'])
342
  def billing_usage():
343
- """
344
- 处理 /handsome/v1/dashboard/billing/usage 路由的请求,返回用量信息(修改后,始终返回 0)。
345
- """
346
  if not check_authorization(request):
347
  return jsonify({"error": "Unauthorized"}), 401
348
 
349
  end_date = datetime.now()
350
  start_date = end_date - timedelta(days=30)
351
 
352
- # 构造用量数据(修改后,始终返回 0)
353
  daily_usage = []
354
  current_date = start_date
355
  while current_date <= end_date:
@@ -367,9 +420,6 @@ def billing_usage():
367
 
368
  @app.route('/handsome/v1/dashboard/billing/subscription', methods=['GET'])
369
  def billing_subscription():
370
- """
371
- 处理 /handsome/v1/dashboard/billing/subscription 路由的请求,返回订阅信息。
372
- """
373
  if not check_authorization(request):
374
  return jsonify({"error": "Unauthorized"}), 401
375
 
@@ -381,7 +431,7 @@ def billing_subscription():
381
  "canceled": False,
382
  "canceled_at": None,
383
  "delinquent": None,
384
- "access_until": int(datetime(9999, 12, 31).timestamp()), # 设置一个较远的未来时间
385
  "soft_limit": 0,
386
  "hard_limit": total_balance,
387
  "system_hard_limit": total_balance,
@@ -401,25 +451,19 @@ def billing_subscription():
401
  })
402
 
403
  if __name__ == '__main__':
404
- # 打印所有环境变量,方便调试
405
  logging.info(f"环境变量:{os.environ}")
406
 
407
- # 初始化全局的 key 列表
408
  invalid_keys_global = []
409
  free_keys_global = []
410
  unverified_keys_global = []
411
  valid_keys_global = []
412
 
413
- # 启动调度器
414
  scheduler.start()
415
 
416
- # 手动触发一次 load_keys 任务
417
  load_keys()
418
  logging.info("首次加载 keys 已手动触发执行")
419
 
420
- # 手动触发一次 refresh_models 任务
421
  refresh_models()
422
  logging.info("首次刷新模型列表已手动触发执行")
423
 
424
- # 启动 Flask 应用,监听所有 IP 的 7860 端口(Hugging Face Space 默认端口)
425
  app.run(debug=False, host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
 
24
  unverified_keys_global = []
25
  valid_keys_global = []
26
 
27
+ # 使用 ThreadPoolExecutor 管理并发
28
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=20)
29
+
30
+ # 为每个模型维护一个独立的 key 索引
31
+ model_key_indices = {}
32
+
33
  def get_credit_summary(api_key):
34
  """
35
  使用 API 密钥获取额度信息。
 
54
  logging.error(f"total_balance 无法转换为浮点数,API Key:{api_key},错误信息:{e}")
55
  return None
56
 
57
+ FREE_MODEL_TEST_KEY = "sk-bmjbjzleaqfgtqfzmcnsbagxrlohriadnxqrzfocbizaxukw"
58
 
59
  def test_model_availability(api_key, model_name):
60
  """
 
74
  "stream": False
75
  },
76
  timeout=10)
 
77
  if response.status_code == 429 or response.status_code == 200 :
78
  return True
79
  else:
 
88
  """
89
  global all_models, free_models
90
 
 
91
  all_models = get_all_models(FREE_MODEL_TEST_KEY)
 
92
  free_models = []
93
 
94
+ with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
 
 
95
  future_to_model = {executor.submit(test_model_availability, FREE_MODEL_TEST_KEY, model): model for model in all_models}
 
96
  for future in concurrent.futures.as_completed(future_to_model):
97
  model = future_to_model[future]
98
  try:
 
108
  def load_keys():
109
  """
110
  从环境变量中加载 keys,并根据额度和模型可用性进行分类,然后记录到日志中。
111
+ 使用线程池并发处理每个 key。
112
  """
113
  keys_str = os.environ.get("KEYS")
114
  test_model = os.environ.get("TEST_MODEL", "Pro/google/gemma-2-9b-it")
115
 
 
 
 
 
 
116
  if keys_str:
117
  keys = [key.strip() for key in keys_str.split(',')]
118
  logging.info(f"加载的 keys:{keys}")
119
 
120
+ with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
121
+ future_to_key = {executor.submit(process_key, key, test_model): key for key in keys}
122
+
123
+ invalid_keys = []
124
+ free_keys = []
125
+ unverified_keys = []
126
+ valid_keys = []
127
+
128
+ for future in concurrent.futures.as_completed(future_to_key):
129
+ key = future_to_key[future]
130
+ try:
131
+ key_type = future.result()
132
+ if key_type == "invalid":
133
+ invalid_keys.append(key)
134
+ elif key_type == "free":
135
+ free_keys.append(key)
136
+ elif key_type == "unverified":
137
  unverified_keys.append(key)
138
+ elif key_type == "valid":
139
+ valid_keys.append(key)
140
+ except Exception as exc:
141
+ logging.error(f"处理 KEY {key} 生成异常: {exc}")
142
 
143
  logging.info(f"无效 KEY:{invalid_keys}")
144
  logging.info(f"免费 KEY:{free_keys}")
145
  logging.info(f"未实名 KEY:{unverified_keys}")
146
  logging.info(f"有效 KEY:{valid_keys}")
147
+
 
148
  global invalid_keys_global, free_keys_global, unverified_keys_global, valid_keys_global
149
  invalid_keys_global = invalid_keys
150
  free_keys_global = free_keys
 
154
  else:
155
  logging.warning("环境变量 KEYS 未设置。")
156
 
157
+ def process_key(key, test_model):
158
+ """
159
+ 处理单个 key,判断其类型。
160
+ """
161
+ credit_summary = get_credit_summary(key)
162
+ if credit_summary is None:
163
+ return "invalid"
164
+ else:
165
+ total_balance = credit_summary.get("total_balance", 0)
166
+ if total_balance <= 0:
167
+ return "free"
168
+ else:
169
+ if test_model_availability(key, test_model):
170
+ return "valid"
171
+ else:
172
+ return "unverified"
173
+
174
  def get_all_models(api_key):
175
  """
176
  获取所有模型列表。
 
183
  response = requests.get(MODELS_ENDPOINT, headers=headers, params={"sub_type": "chat"})
184
  response.raise_for_status()
185
  data = response.json()
 
186
  if isinstance(data, dict) and 'data' in data and isinstance(data['data'], list):
187
  return [model.get("id") for model in data["data"] if isinstance(model, dict) and "id" in model]
188
  else:
 
206
  else:
207
  return "unknown"
208
 
209
+ def select_key(request_type, model_name):
210
  """
211
+ 根据请求类型和模型名称选择合适的 KEY,并实现轮询和重试机制。
212
  """
213
  if request_type == "free":
 
214
  available_keys = free_keys_global + unverified_keys_global + valid_keys_global
215
  elif request_type == "paid":
 
216
  available_keys = unverified_keys_global + valid_keys_global
217
  else:
 
218
  available_keys = free_keys_global + unverified_keys_global + valid_keys_global
219
 
220
  if not available_keys:
221
  return None
222
 
223
+ # 获取当前模型的索引,如果没有则初始化为 0
224
+ current_index = model_key_indices.get(model_name, 0)
225
+
226
+ # 轮询并重试
227
+ for _ in range(len(available_keys)):
228
+ key = available_keys[current_index % len(available_keys)]
229
+ current_index += 1
230
+
231
+ # 检查 KEY 是否有效
232
+ if key_is_valid(key, request_type):
233
+ # 更新模型索引并返回 KEY
234
+ model_key_indices[model_name] = current_index
235
+ return key
236
+ else:
237
+ logging.warning(f"KEY {key} 无效或达到限制,尝试下一个 KEY")
238
+
239
+ # 所有 KEY 都尝试过,重置索引并返回 None
240
+ model_key_indices[model_name] = 0
241
+ return None
242
+
243
+ def key_is_valid(key, request_type):
244
+ """
245
+ 检查 KEY 是否有效,根据不同的请求类型进行不同的检查。
246
+ """
247
+ if request_type == "invalid":
248
+ return False # 无效 KEY 始终返回 False
249
+
250
+ credit_summary = get_credit_summary(key)
251
+ if credit_summary is None:
252
+ return False
253
+
254
+ total_balance = credit_summary.get("total_balance", 0)
255
+
256
+ if request_type == "free":
257
+ return True # 免费 KEY,只要能获取到信息,就认为是有效的
258
+ elif request_type == "paid" or request_type == "unverified":
259
+ return total_balance > 0 # 付费 KEY 或未实名 KEY,需要余额大于 0 才是有效的
260
+ else:
261
+ return False # 未知类型,返回 False
262
 
263
  def check_authorization(request):
264
  """
 
280
 
281
  return True
282
 
 
283
  scheduler = BackgroundScheduler()
 
 
284
  scheduler.add_job(load_keys, 'interval', hours=1)
 
285
  scheduler.add_job(refresh_models, 'interval', minutes=10)
286
 
287
  @app.route('/')
288
  def index():
 
 
 
289
  return "<h1>Welcome to SiliconFlow</h1>"
290
 
291
  @app.route('/check_tokens', methods=['POST'])
292
  def check_tokens():
293
  """
294
  处理前端发送的 Token 检测请求。
295
+ 使用线程池并发处理每个 token。
296
  """
297
  tokens = request.json.get('tokens', [])
298
  test_model = os.environ.get("TEST_MODEL", "Pro/google/gemma-2-9b-it")
299
 
300
+ with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
301
+ future_to_token = {executor.submit(process_key, token, test_model): token for token in tokens}
302
+
303
+ results = []
304
+ for future in concurrent.futures.as_completed(future_to_token):
305
+ token = future_to_token[future]
306
+ try:
307
+ key_type = future.result()
308
+ credit_summary = get_credit_summary(token)
309
+ balance = credit_summary.get("total_balance", 0) if credit_summary else 0
310
+ if key_type == "invalid":
311
+ results.append({"token": token, "type": "无效 KEY", "balance": balance, "message": "无法获取额度信息"})
312
+ elif key_type == "free":
313
+ results.append({"token": token, "type": "免费 KEY", "balance": balance, "message": "额度不足"})
314
+ elif key_type == "unverified":
315
+ results.append({"token": token, "type": "未实名 KEY", "balance": balance, "message": "无法使用指定模型"})
316
+ elif key_type == "valid":
317
+ results.append({"token": token, "type": "有效 KEY", "balance": balance, "message": "可以使用指定模型"})
318
+ except Exception as exc:
319
+ logging.error(f"处理 Token {token} 生成异常: {exc}")
320
 
321
  return jsonify(results)
322
 
323
  @app.route('/handsome/v1/chat/completions', methods=['POST'])
324
  def handsome_chat_completions():
325
  """
326
+ 处理 /handsome/v1/chat/completions 路由的请求,添加鉴权,并实现 KEY 的轮询和重试机制。
327
  """
328
  if not check_authorization(request):
329
  return jsonify({"error": "Unauthorized"}), 401
 
334
 
335
  model_name = data['model']
336
  request_type = determine_request_type(model_name)
337
+ api_key = select_key(request_type, model_name)
338
 
339
  if not api_key:
340
+ return jsonify({"error": "No available API key for this request type or all keys have reached their limits"}), 429
341
 
342
  headers = {
343
  "Authorization": f"Bearer {api_key}",
344
  "Content-Type": "application/json"
345
  }
346
 
 
347
  try:
348
  response = requests.post(
349
  TEST_MODEL_ENDPOINT,
 
352
  stream=data.get("stream", False),
353
  timeout=60
354
  )
355
+
 
356
  if response.status_code == 429:
357
  return jsonify(response.json()), 429
358
 
 
366
 
367
  @app.route('/handsome/v1/models', methods=['GET'])
368
  def list_models():
 
 
 
369
  if not check_authorization(request):
370
  return jsonify({"error": "Unauthorized"}), 401
371
 
 
372
  return jsonify({
373
  "data": [{"id": model, "object": "model"} for model in all_models],
374
  "free_models": free_models
 
377
  def get_billing_info():
378
  """
379
  获取所有KEY的额度信息。
380
+ 使用线程池并发处理每个 key。
381
  """
382
+ keys = valid_keys_global + unverified_keys_global
383
  total_balance = 0
384
+
385
+ with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
386
+ futures = [executor.submit(get_credit_summary, key) for key in keys]
387
+
388
+ for future in concurrent.futures.as_completed(futures):
389
+ try:
390
+ credit_summary = future.result()
391
+ if credit_summary:
392
+ total_balance += credit_summary.get("total_balance", 0)
393
+ except Exception as exc:
394
+ logging.error(f"获取额度信息生成异常: {exc}")
395
+
396
  return total_balance
397
 
398
  @app.route('/handsome/v1/dashboard/billing/usage', methods=['GET'])
399
  def billing_usage():
 
 
 
400
  if not check_authorization(request):
401
  return jsonify({"error": "Unauthorized"}), 401
402
 
403
  end_date = datetime.now()
404
  start_date = end_date - timedelta(days=30)
405
 
 
406
  daily_usage = []
407
  current_date = start_date
408
  while current_date <= end_date:
 
420
 
421
  @app.route('/handsome/v1/dashboard/billing/subscription', methods=['GET'])
422
  def billing_subscription():
 
 
 
423
  if not check_authorization(request):
424
  return jsonify({"error": "Unauthorized"}), 401
425
 
 
431
  "canceled": False,
432
  "canceled_at": None,
433
  "delinquent": None,
434
+ "access_until": int(datetime(9999, 12, 31).timestamp()),
435
  "soft_limit": 0,
436
  "hard_limit": total_balance,
437
  "system_hard_limit": total_balance,
 
451
  })
452
 
453
  if __name__ == '__main__':
 
454
  logging.info(f"环境变量:{os.environ}")
455
 
 
456
  invalid_keys_global = []
457
  free_keys_global = []
458
  unverified_keys_global = []
459
  valid_keys_global = []
460
 
 
461
  scheduler.start()
462
 
 
463
  load_keys()
464
  logging.info("首次加载 keys 已手动触发执行")
465
 
 
466
  refresh_models()
467
  logging.info("首次刷新模型列表已手动触发执行")
468
 
 
469
  app.run(debug=False, host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))