Spaces:
Paused
Paused
Upload 2 files
Browse files
app.py
CHANGED
|
@@ -59,6 +59,10 @@ safety_settings = [
|
|
| 59 |
{
|
| 60 |
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 61 |
"threshold": "BLOCK_NONE"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
]
|
| 64 |
safety_settings_g2 = [
|
|
@@ -77,6 +81,10 @@ safety_settings_g2 = [
|
|
| 77 |
{
|
| 78 |
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 79 |
"threshold": "OFF"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
}
|
| 81 |
]
|
| 82 |
@dataclass
|
|
@@ -230,7 +238,7 @@ GEMINI_MODELS = [
|
|
| 230 |
|
| 231 |
@app.route('/')
|
| 232 |
def index():
|
| 233 |
-
main_content = "Moonfanz Reminiproxy v2.3.
|
| 234 |
html_template = """
|
| 235 |
<!DOCTYPE html>
|
| 236 |
<html>
|
|
@@ -289,98 +297,102 @@ def increment_request_count(api_key):
|
|
| 289 |
request_counts[api_key] = deque()
|
| 290 |
request_counts[api_key].append(now)
|
| 291 |
|
| 292 |
-
def handle_api_error(error, attempt):
|
| 293 |
if attempt > MAX_RETRIES:
|
| 294 |
logger.error(f"{MAX_RETRIES} 次尝试后仍然失败,请修改预设或输入")
|
| 295 |
return 0, jsonify({
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
})
|
| 301 |
|
| 302 |
-
if isinstance(error,
|
| 303 |
-
|
| 304 |
-
key_manager.blacklist_key(current_api_key)
|
| 305 |
-
switch_api_key()
|
| 306 |
-
return 0, None
|
| 307 |
|
| 308 |
-
|
| 309 |
-
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 310 |
-
logger.warning(f"{current_api_key[:8]} ... {current_api_key[-3:]} → 429 官方资源耗尽 → {delay} 秒后重试...")
|
| 311 |
-
key_manager.blacklist_key(current_api_key)
|
| 312 |
-
switch_api_key()
|
| 313 |
-
time.sleep(delay)
|
| 314 |
-
return 0, None
|
| 315 |
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
|
| 323 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 325 |
-
logger.warning(f"
|
| 326 |
-
switch_api_key()
|
| 327 |
time.sleep(delay)
|
| 328 |
return 0, None
|
| 329 |
|
| 330 |
-
elif isinstance(error,
|
| 331 |
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 332 |
-
logger.warning(f"
|
| 333 |
-
switch_api_key()
|
| 334 |
time.sleep(delay)
|
| 335 |
return 0, None
|
| 336 |
|
| 337 |
-
elif isinstance(error, PermissionDenied):
|
| 338 |
-
logger.error(f"{current_api_key[:8]} ... {current_api_key[-3:]} → 403 权限被拒绝,该 API KEY 可能已经被官方封禁")
|
| 339 |
-
key_manager.blacklist_key(current_api_key)
|
| 340 |
-
switch_api_key()
|
| 341 |
-
return 0, None
|
| 342 |
-
|
| 343 |
-
elif isinstance(error, StopCandidateException):
|
| 344 |
-
logger.warning(f"AI输出内容被Gemini官方阻挡,代理没有得到有效回复")
|
| 345 |
-
switch_api_key()
|
| 346 |
-
return 0, None
|
| 347 |
-
|
| 348 |
-
elif isinstance(error, BlockedPromptException):
|
| 349 |
-
try:
|
| 350 |
-
full_reason_str = str(error.args[0])
|
| 351 |
-
logger.error(f"{full_reason_str}")
|
| 352 |
-
if "block_reason:" in full_reason_str:
|
| 353 |
-
start_index = full_reason_str.find("block_reason:") + len("block_reason:")
|
| 354 |
-
block_reason_str = full_reason_str[start_index:].strip()
|
| 355 |
-
|
| 356 |
-
if block_reason_str == "SAFETY":
|
| 357 |
-
logger.warning(f"用户输入因安全原因被阻止")
|
| 358 |
-
return 1, None
|
| 359 |
-
elif block_reason_str == "BLOCKLIST":
|
| 360 |
-
logger.warning(f"用户输入因包含阻止列表中的术语而被阻止")
|
| 361 |
-
return 1, None
|
| 362 |
-
elif block_reason_str == "PROHIBITED_CONTENT":
|
| 363 |
-
logger.warning(f"用户输入因包含禁止内容而被阻止")
|
| 364 |
-
return 1, None
|
| 365 |
-
elif block_reason_str == "OTHER":
|
| 366 |
-
logger.warning(f"用户输入因未知原因被阻止")
|
| 367 |
-
return 1, None
|
| 368 |
-
else:
|
| 369 |
-
logger.warning(f"用户输入被阻止,原因: {block_reason_str}")
|
| 370 |
-
return 1, None
|
| 371 |
-
else:
|
| 372 |
-
logger.warning(f"用户输入被阻止,原因: {full_reason_str}")
|
| 373 |
-
return 1, None
|
| 374 |
-
|
| 375 |
-
except (IndexError, AttributeError) as e:
|
| 376 |
-
logger.error(f"获取提示原因失败↙\n{e}")
|
| 377 |
-
logger.error(f"提示被阻止↙\n{error}")
|
| 378 |
-
return 2, None
|
| 379 |
-
|
| 380 |
else:
|
| 381 |
-
logger.error(f"
|
| 382 |
-
|
| 383 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
|
| 385 |
@app.route('/hf/v1/chat/completions', methods=['POST'])
|
| 386 |
def chat_completions():
|
|
@@ -436,16 +448,14 @@ def chat_completions():
|
|
| 436 |
|
| 437 |
try:
|
| 438 |
response = requests.post(url, headers=headers, json=data, stream=True)
|
| 439 |
-
response.raise_for_status()
|
| 440 |
|
| 441 |
if stream:
|
| 442 |
return 1, response
|
| 443 |
else:
|
| 444 |
return 1, ResponseWrapper(response.json())
|
| 445 |
except requests.exceptions.RequestException as e:
|
| 446 |
-
return handle_api_error(e, attempt)
|
| 447 |
-
except (StopCandidateException, BlockedPromptException) as e:
|
| 448 |
-
return handle_api_error(e, attempt)
|
| 449 |
|
| 450 |
def generate_stream(response):
|
| 451 |
logger.info(f"流式开始 →")
|
|
@@ -512,23 +522,14 @@ def chat_completions():
|
|
| 512 |
|
| 513 |
if success == 0:
|
| 514 |
continue
|
| 515 |
-
elif success ==
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
response = {
|
| 519 |
-
'error': {
|
| 520 |
-
'message': f'{model} 很可能暂时不可用,请更换模型或未来一段时间再试',
|
| 521 |
-
'type': 'internal_server_error'
|
| 522 |
-
}
|
| 523 |
-
}
|
| 524 |
-
return jsonify(response), 503
|
| 525 |
-
|
| 526 |
-
if stream:
|
| 527 |
return Response(
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
try:
|
| 533 |
text_content = response.text
|
| 534 |
prompt_tokens = response.prompt_token_count
|
|
@@ -550,7 +551,7 @@ def chat_completions():
|
|
| 550 |
continue
|
| 551 |
|
| 552 |
response_data = {
|
| 553 |
-
'id': 'chatcmpl-xxxxxxxxxxxx',
|
| 554 |
'object': 'chat.completion',
|
| 555 |
'created': int(datetime.now().timestamp()),
|
| 556 |
'model': model,
|
|
@@ -570,7 +571,17 @@ def chat_completions():
|
|
| 570 |
}
|
| 571 |
logger.info(f"200!")
|
| 572 |
return jsonify(response_data)
|
| 573 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
else:
|
| 575 |
logger.error(f"{MAX_RETRIES} 次尝试均失败,请调整配置,或等待官方恢复,或向Moonfanz反馈")
|
| 576 |
response = {
|
|
@@ -655,7 +666,7 @@ if __name__ == '__main__':
|
|
| 655 |
|
| 656 |
scheduler.add_job(keep_alive, 'interval', hours=12)
|
| 657 |
scheduler.start()
|
| 658 |
-
logger.info(f"Reminiproxy v2.3.
|
| 659 |
logger.info(f"最大尝试次数/MaxRetries: {MAX_RETRIES}")
|
| 660 |
logger.info(f"最大请求次数/MaxRequests: {MAX_REQUESTS}")
|
| 661 |
logger.info(f"请求限额窗口/LimitWindow: {LIMIT_WINDOW} 秒")
|
|
|
|
| 59 |
{
|
| 60 |
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 61 |
"threshold": "BLOCK_NONE"
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"category": 'HARM_CATEGORY_CIVIC_INTEGRITY',
|
| 65 |
+
"threshold": 'BLOCK_NONE'
|
| 66 |
}
|
| 67 |
]
|
| 68 |
safety_settings_g2 = [
|
|
|
|
| 81 |
{
|
| 82 |
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 83 |
"threshold": "OFF"
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"category": 'HARM_CATEGORY_CIVIC_INTEGRITY',
|
| 87 |
+
"threshold": 'OFF'
|
| 88 |
}
|
| 89 |
]
|
| 90 |
@dataclass
|
|
|
|
| 238 |
|
| 239 |
@app.route('/')
|
| 240 |
def index():
|
| 241 |
+
main_content = "Moonfanz Reminiproxy v2.3.4 2025-01-14"
|
| 242 |
html_template = """
|
| 243 |
<!DOCTYPE html>
|
| 244 |
<html>
|
|
|
|
| 297 |
request_counts[api_key] = deque()
|
| 298 |
request_counts[api_key].append(now)
|
| 299 |
|
| 300 |
+
def handle_api_error(error, attempt, current_api_key):
|
| 301 |
if attempt > MAX_RETRIES:
|
| 302 |
logger.error(f"{MAX_RETRIES} 次尝试后仍然失败,请修改预设或输入")
|
| 303 |
return 0, jsonify({
|
| 304 |
+
'error': {
|
| 305 |
+
'message': f"{MAX_RETRIES} 次尝试后仍然失败,请修改预设或输入",
|
| 306 |
+
'type': 'max_retries_exceeded'
|
| 307 |
+
}
|
| 308 |
})
|
| 309 |
|
| 310 |
+
if isinstance(error, requests.exceptions.HTTPError):
|
| 311 |
+
status_code = error.response.status_code
|
|
|
|
|
|
|
|
|
|
| 312 |
|
| 313 |
+
if status_code == 400:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
+
try:
|
| 316 |
+
error_data = error.response.json()
|
| 317 |
+
if 'error' in error_data:
|
| 318 |
+
if error_data['error'].get('code') == "invalid_argument":
|
| 319 |
+
logger.error(f"{current_api_key[:8]} ... {current_api_key[-3:]} → 无效,可能已过期或被删除")
|
| 320 |
+
key_manager.blacklist_key(current_api_key)
|
| 321 |
+
switch_api_key()
|
| 322 |
+
return 0, None
|
| 323 |
+
error_message = error_data['error'].get('message', 'Bad Request')
|
| 324 |
+
error_type = error_data['error'].get('type', 'invalid_request_error')
|
| 325 |
+
logger.warning(f"400 Bad Request: {error_message}")
|
| 326 |
+
return 1, jsonify({'error': {'message': error_message, 'type': error_type}})
|
| 327 |
+
except ValueError:
|
| 328 |
+
logger.warning("400 Bad Request (Unable to parse error response)")
|
| 329 |
+
return 1, jsonify({'error': {'message': 'Bad Request', 'type': 'invalid_request_error'}})
|
| 330 |
+
|
| 331 |
+
elif status_code == 429:
|
| 332 |
+
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 333 |
+
logger.warning(
|
| 334 |
+
f"{current_api_key[:8]} ... {current_api_key[-3:]} → 429 官方资源耗尽 → {delay} 秒后重试..."
|
| 335 |
+
)
|
| 336 |
+
key_manager.blacklist_key(current_api_key)
|
| 337 |
+
switch_api_key()
|
| 338 |
+
time.sleep(delay)
|
| 339 |
+
return 0, None
|
| 340 |
+
|
| 341 |
+
elif status_code == 403:
|
| 342 |
+
logger.error(
|
| 343 |
+
f"{current_api_key[:8]} ... {current_api_key[-3:]} → 403 权限被拒绝,该 API KEY 可能已经被官方封禁"
|
| 344 |
+
)
|
| 345 |
+
key_manager.blacklist_key(current_api_key)
|
| 346 |
+
switch_api_key()
|
| 347 |
+
return 0, None
|
| 348 |
+
|
| 349 |
+
elif status_code == 500:
|
| 350 |
+
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 351 |
+
logger.warning(
|
| 352 |
+
f"{current_api_key[:8]} ... {current_api_key[-3:]} → 500 服务器内部错误 → {delay} 秒后重试..."
|
| 353 |
+
)
|
| 354 |
+
switch_api_key()
|
| 355 |
+
time.sleep(delay)
|
| 356 |
+
return 0, None
|
| 357 |
+
|
| 358 |
+
elif status_code == 503:
|
| 359 |
+
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 360 |
+
logger.warning(
|
| 361 |
+
f"{current_api_key[:8]} ... {current_api_key[-3:]} → 503 ��务不可用 → {delay} 秒后重试..."
|
| 362 |
+
)
|
| 363 |
+
switch_api_key()
|
| 364 |
+
time.sleep(delay)
|
| 365 |
+
return 0, None
|
| 366 |
|
| 367 |
+
else:
|
| 368 |
+
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 369 |
+
logger.warning(
|
| 370 |
+
f"{current_api_key[:8]} ... {current_api_key[-3:]} → {status_code} 未知错误 → {delay} 秒后重试..."
|
| 371 |
+
)
|
| 372 |
+
switch_api_key()
|
| 373 |
+
time.sleep(delay)
|
| 374 |
+
return 0, None
|
| 375 |
+
|
| 376 |
+
elif isinstance(error, requests.exceptions.ConnectionError):
|
| 377 |
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 378 |
+
logger.warning(f"连接错误 → {delay} 秒后重试...")
|
|
|
|
| 379 |
time.sleep(delay)
|
| 380 |
return 0, None
|
| 381 |
|
| 382 |
+
elif isinstance(error, requests.exceptions.Timeout):
|
| 383 |
delay = min(RETRY_DELAY * (2 ** attempt), MAX_RETRY_DELAY)
|
| 384 |
+
logger.warning(f"请求超时 → {delay} 秒后重试...")
|
|
|
|
| 385 |
time.sleep(delay)
|
| 386 |
return 0, None
|
| 387 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
else:
|
| 389 |
+
logger.error(f"发生未知错误: {error}")
|
| 390 |
+
return 0, jsonify({
|
| 391 |
+
'error': {
|
| 392 |
+
'message': f"发生未知错误: {error}",
|
| 393 |
+
'type': 'unknown_error'
|
| 394 |
+
}
|
| 395 |
+
})
|
| 396 |
|
| 397 |
@app.route('/hf/v1/chat/completions', methods=['POST'])
|
| 398 |
def chat_completions():
|
|
|
|
| 448 |
|
| 449 |
try:
|
| 450 |
response = requests.post(url, headers=headers, json=data, stream=True)
|
| 451 |
+
response.raise_for_status() # This will raise an HTTPError for bad responses
|
| 452 |
|
| 453 |
if stream:
|
| 454 |
return 1, response
|
| 455 |
else:
|
| 456 |
return 1, ResponseWrapper(response.json())
|
| 457 |
except requests.exceptions.RequestException as e:
|
| 458 |
+
return handle_api_error(e, attempt, current_api_key)
|
|
|
|
|
|
|
| 459 |
|
| 460 |
def generate_stream(response):
|
| 461 |
logger.info(f"流式开始 →")
|
|
|
|
| 522 |
|
| 523 |
if success == 0:
|
| 524 |
continue
|
| 525 |
+
elif success == 1 and response is None:
|
| 526 |
+
continue
|
| 527 |
+
elif success == 1 and isinstance(response, Response):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 528 |
return Response(
|
| 529 |
+
stream_with_context(generate_stream(response)),
|
| 530 |
+
mimetype='text/event-stream'
|
| 531 |
+
)
|
| 532 |
+
elif success == 1 and isinstance(response, ResponseWrapper):
|
| 533 |
try:
|
| 534 |
text_content = response.text
|
| 535 |
prompt_tokens = response.prompt_token_count
|
|
|
|
| 551 |
continue
|
| 552 |
|
| 553 |
response_data = {
|
| 554 |
+
'id': 'chatcmpl-xxxxxxxxxxxx',
|
| 555 |
'object': 'chat.completion',
|
| 556 |
'created': int(datetime.now().timestamp()),
|
| 557 |
'model': model,
|
|
|
|
| 571 |
}
|
| 572 |
logger.info(f"200!")
|
| 573 |
return jsonify(response_data)
|
| 574 |
+
elif success == 1 and isinstance(response, tuple):
|
| 575 |
+
return response[1], response[0]
|
| 576 |
+
elif success == 2:
|
| 577 |
+
logger.error(f"{model} 很可能暂时不可用,请更换模型或未来一段时间再试")
|
| 578 |
+
response = {
|
| 579 |
+
'error': {
|
| 580 |
+
'message': f'{model} 很可能暂时不可用,请更换模型或未来一段时间再试',
|
| 581 |
+
'type': 'internal_server_error'
|
| 582 |
+
}
|
| 583 |
+
}
|
| 584 |
+
return jsonify(response), 503
|
| 585 |
else:
|
| 586 |
logger.error(f"{MAX_RETRIES} 次尝试均失败,请调整配置,或等待官方恢复,或向Moonfanz反馈")
|
| 587 |
response = {
|
|
|
|
| 666 |
|
| 667 |
scheduler.add_job(keep_alive, 'interval', hours=12)
|
| 668 |
scheduler.start()
|
| 669 |
+
logger.info(f"Reminiproxy v2.3.4 启动")
|
| 670 |
logger.info(f"最大尝试次数/MaxRetries: {MAX_RETRIES}")
|
| 671 |
logger.info(f"最大请求次数/MaxRequests: {MAX_REQUESTS}")
|
| 672 |
logger.info(f"请求限额窗口/LimitWindow: {LIMIT_WINDOW} 秒")
|