mirror of
https://github.com/TheSmallHanCat/sora2api.git
synced 2026-02-16 19:54:43 +08:00
feat: 优化CF429错误处理及超时日志记录
This commit is contained in:
@@ -533,7 +533,7 @@ class GenerationHandler:
|
|||||||
await self.token_manager.record_usage(token_obj.id, is_video=is_video)
|
await self.token_manager.record_usage(token_obj.id, is_video=is_video)
|
||||||
|
|
||||||
# Poll for results with timeout
|
# Poll for results with timeout
|
||||||
async for chunk in self._poll_task_result(task_id, token_obj.token, is_video, stream, prompt, token_obj.id):
|
async for chunk in self._poll_task_result(task_id, token_obj.token, is_video, stream, prompt, token_obj.id, log_id, start_time):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
# Record success
|
# Record success
|
||||||
@@ -591,12 +591,6 @@ class GenerationHandler:
|
|||||||
if is_video and token_obj and self.concurrency_manager:
|
if is_video and token_obj and self.concurrency_manager:
|
||||||
await self.concurrency_manager.release_video(token_obj.id)
|
await self.concurrency_manager.release_video(token_obj.id)
|
||||||
|
|
||||||
# Record error (check if it's an overload error)
|
|
||||||
if token_obj:
|
|
||||||
error_str = str(e).lower()
|
|
||||||
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
|
||||||
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
|
||||||
|
|
||||||
# Parse error message to check if it's a structured error (JSON)
|
# Parse error message to check if it's a structured error (JSON)
|
||||||
error_response = None
|
error_response = None
|
||||||
try:
|
try:
|
||||||
@@ -604,15 +598,31 @@ class GenerationHandler:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Check for CF shield/429 error
|
||||||
|
is_cf_or_429 = False
|
||||||
|
if error_response and isinstance(error_response, dict):
|
||||||
|
error_info = error_response.get("error", {})
|
||||||
|
if error_info.get("code") == "cf_shield_429":
|
||||||
|
is_cf_or_429 = True
|
||||||
|
|
||||||
|
# Record error (check if it's an overload error or CF/429 error)
|
||||||
|
if token_obj:
|
||||||
|
error_str = str(e).lower()
|
||||||
|
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
||||||
|
# Don't record error for CF shield/429 (not token's fault)
|
||||||
|
if not is_cf_or_429:
|
||||||
|
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
||||||
|
|
||||||
# Update log entry with error data
|
# Update log entry with error data
|
||||||
duration = time.time() - start_time
|
duration = time.time() - start_time
|
||||||
if log_id:
|
if log_id:
|
||||||
if error_response:
|
if error_response:
|
||||||
# Structured error (e.g., unsupported_country_code)
|
# Structured error (e.g., unsupported_country_code, cf_shield_429)
|
||||||
|
status_code = 429 if is_cf_or_429 else 400
|
||||||
await self.db.update_request_log(
|
await self.db.update_request_log(
|
||||||
log_id,
|
log_id,
|
||||||
response_body=json.dumps(error_response),
|
response_body=json.dumps(error_response),
|
||||||
status_code=400,
|
status_code=status_code,
|
||||||
duration=duration
|
duration=duration
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -626,7 +636,8 @@ class GenerationHandler:
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
async def _poll_task_result(self, task_id: str, token: str, is_video: bool,
|
async def _poll_task_result(self, task_id: str, token: str, is_video: bool,
|
||||||
stream: bool, prompt: str, token_id: int = None) -> AsyncGenerator[str, None]:
|
stream: bool, prompt: str, token_id: int = None,
|
||||||
|
log_id: int = None, start_time: float = None) -> AsyncGenerator[str, None]:
|
||||||
"""Poll for task result with timeout"""
|
"""Poll for task result with timeout"""
|
||||||
# Get timeout from config
|
# Get timeout from config
|
||||||
timeout = config.video_timeout if is_video else config.image_timeout
|
timeout = config.video_timeout if is_video else config.image_timeout
|
||||||
@@ -669,7 +680,19 @@ class GenerationHandler:
|
|||||||
await self.concurrency_manager.release_video(token_id)
|
await self.concurrency_manager.release_video(token_id)
|
||||||
debug_logger.log_info(f"Released concurrency slot for token {token_id} due to timeout")
|
debug_logger.log_info(f"Released concurrency slot for token {token_id} due to timeout")
|
||||||
|
|
||||||
|
# Update task status to failed
|
||||||
await self.db.update_task(task_id, "failed", 0, error_message=f"Generation timeout after {elapsed_time:.1f} seconds")
|
await self.db.update_task(task_id, "failed", 0, error_message=f"Generation timeout after {elapsed_time:.1f} seconds")
|
||||||
|
|
||||||
|
# Update request log with timeout error
|
||||||
|
if log_id and start_time:
|
||||||
|
duration = time.time() - start_time
|
||||||
|
await self.db.update_request_log(
|
||||||
|
log_id,
|
||||||
|
response_body=json.dumps({"error": f"Generation timeout after {elapsed_time:.1f} seconds"}),
|
||||||
|
status_code=408,
|
||||||
|
duration=duration
|
||||||
|
)
|
||||||
|
|
||||||
raise Exception(f"Upstream API timeout: Generation exceeded {timeout} seconds limit")
|
raise Exception(f"Upstream API timeout: Generation exceeded {timeout} seconds limit")
|
||||||
|
|
||||||
|
|
||||||
@@ -1057,6 +1080,61 @@ class GenerationHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Check for CF shield/429 error - don't retry these
|
||||||
|
error_str = str(e)
|
||||||
|
is_cf_or_429 = False
|
||||||
|
try:
|
||||||
|
error_response = json.loads(error_str)
|
||||||
|
if isinstance(error_response, dict):
|
||||||
|
error_info = error_response.get("error", {})
|
||||||
|
if error_info.get("code") == "cf_shield_429":
|
||||||
|
is_cf_or_429 = True
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# CF shield/429 detected - fail immediately
|
||||||
|
if is_cf_or_429:
|
||||||
|
debug_logger.log_error(
|
||||||
|
error_message="CF Shield/429 detected during polling, failing task immediately",
|
||||||
|
status_code=429,
|
||||||
|
response_text=error_str
|
||||||
|
)
|
||||||
|
# Update task status to failed
|
||||||
|
await self.db.update_task(task_id, "failed", 0, error_message="Cloudflare challenge or rate limit (429) triggered")
|
||||||
|
|
||||||
|
# Update request log with CF/429 error
|
||||||
|
if log_id and start_time:
|
||||||
|
duration = time.time() - start_time
|
||||||
|
await self.db.update_request_log(
|
||||||
|
log_id,
|
||||||
|
response_body=json.dumps({"error": "Cloudflare challenge or rate limit (429) triggered"}),
|
||||||
|
status_code=429,
|
||||||
|
duration=duration
|
||||||
|
)
|
||||||
|
|
||||||
|
# Release resources
|
||||||
|
if not is_video and token_id:
|
||||||
|
await self.load_balancer.token_lock.release_lock(token_id)
|
||||||
|
if self.concurrency_manager:
|
||||||
|
await self.concurrency_manager.release_image(token_id)
|
||||||
|
if is_video and token_id and self.concurrency_manager:
|
||||||
|
await self.concurrency_manager.release_video(token_id)
|
||||||
|
|
||||||
|
# Send error message to client if streaming
|
||||||
|
if stream:
|
||||||
|
yield self._format_stream_chunk(
|
||||||
|
reasoning_content="**CF Shield/429 Error**\\n\\nCloudflare challenge or rate limit (429) triggered\\n"
|
||||||
|
)
|
||||||
|
yield self._format_stream_chunk(
|
||||||
|
content="❌ Generation failed: Cloudflare challenge or rate limit (429) triggered. Please change proxy or reduce request frequency.",
|
||||||
|
finish_reason="STOP"
|
||||||
|
)
|
||||||
|
yield "data: [DONE]\\n\\n"
|
||||||
|
|
||||||
|
# Exit polling immediately
|
||||||
|
return
|
||||||
|
|
||||||
|
# For other errors, retry if not last attempt
|
||||||
if attempt >= max_attempts - 1:
|
if attempt >= max_attempts - 1:
|
||||||
raise e
|
raise e
|
||||||
continue
|
continue
|
||||||
@@ -1315,6 +1393,20 @@ class GenerationHandler:
|
|||||||
yield "data: [DONE]\n\n"
|
yield "data: [DONE]\n\n"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Parse error to check for CF shield/429
|
||||||
|
error_response = None
|
||||||
|
try:
|
||||||
|
error_response = json.loads(str(e))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check for CF shield/429 error
|
||||||
|
is_cf_or_429 = False
|
||||||
|
if error_response and isinstance(error_response, dict):
|
||||||
|
error_info = error_response.get("error", {})
|
||||||
|
if error_info.get("code") == "cf_shield_429":
|
||||||
|
is_cf_or_429 = True
|
||||||
|
|
||||||
# Log failed character creation
|
# Log failed character creation
|
||||||
duration = time.time() - start_time
|
duration = time.time() - start_time
|
||||||
await self._log_request(
|
await self._log_request(
|
||||||
@@ -1328,13 +1420,21 @@ class GenerationHandler:
|
|||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e)
|
"error": str(e)
|
||||||
},
|
},
|
||||||
status_code=500,
|
status_code=429 if is_cf_or_429 else 500,
|
||||||
duration=duration
|
duration=duration
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Record error (check if it's an overload error or CF/429 error)
|
||||||
|
if token_obj:
|
||||||
|
error_str = str(e).lower()
|
||||||
|
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
||||||
|
# Don't record error for CF shield/429 (not token's fault)
|
||||||
|
if not is_cf_or_429:
|
||||||
|
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
||||||
|
|
||||||
debug_logger.log_error(
|
debug_logger.log_error(
|
||||||
error_message=f"Character creation failed: {str(e)}",
|
error_message=f"Character creation failed: {str(e)}",
|
||||||
status_code=500,
|
status_code=429 if is_cf_or_429 else 500,
|
||||||
response_text=str(e)
|
response_text=str(e)
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
@@ -1531,14 +1631,30 @@ class GenerationHandler:
|
|||||||
duration=duration
|
duration=duration
|
||||||
)
|
)
|
||||||
|
|
||||||
# Record error (check if it's an overload error)
|
# Parse error to check for CF shield/429
|
||||||
|
error_response = None
|
||||||
|
try:
|
||||||
|
error_response = json.loads(str(e))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check for CF shield/429 error
|
||||||
|
is_cf_or_429 = False
|
||||||
|
if error_response and isinstance(error_response, dict):
|
||||||
|
error_info = error_response.get("error", {})
|
||||||
|
if error_info.get("code") == "cf_shield_429":
|
||||||
|
is_cf_or_429 = True
|
||||||
|
|
||||||
|
# Record error (check if it's an overload error or CF/429 error)
|
||||||
if token_obj:
|
if token_obj:
|
||||||
error_str = str(e).lower()
|
error_str = str(e).lower()
|
||||||
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
||||||
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
# Don't record error for CF shield/429 (not token's fault)
|
||||||
|
if not is_cf_or_429:
|
||||||
|
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
||||||
debug_logger.log_error(
|
debug_logger.log_error(
|
||||||
error_message=f"Character and video generation failed: {str(e)}",
|
error_message=f"Character and video generation failed: {str(e)}",
|
||||||
status_code=500,
|
status_code=429 if is_cf_or_429 else 500,
|
||||||
response_text=str(e)
|
response_text=str(e)
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
@@ -1624,14 +1740,30 @@ class GenerationHandler:
|
|||||||
await self.token_manager.record_success(token_obj.id, is_video=True)
|
await self.token_manager.record_success(token_obj.id, is_video=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Record error (check if it's an overload error)
|
# Parse error to check for CF shield/429
|
||||||
|
error_response = None
|
||||||
|
try:
|
||||||
|
error_response = json.loads(str(e))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check for CF shield/429 error
|
||||||
|
is_cf_or_429 = False
|
||||||
|
if error_response and isinstance(error_response, dict):
|
||||||
|
error_info = error_response.get("error", {})
|
||||||
|
if error_info.get("code") == "cf_shield_429":
|
||||||
|
is_cf_or_429 = True
|
||||||
|
|
||||||
|
# Record error (check if it's an overload error or CF/429 error)
|
||||||
if token_obj:
|
if token_obj:
|
||||||
error_str = str(e).lower()
|
error_str = str(e).lower()
|
||||||
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
is_overload = "heavy_load" in error_str or "under heavy load" in error_str
|
||||||
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
# Don't record error for CF shield/429 (not token's fault)
|
||||||
|
if not is_cf_or_429:
|
||||||
|
await self.token_manager.record_error(token_obj.id, is_overload=is_overload)
|
||||||
debug_logger.log_error(
|
debug_logger.log_error(
|
||||||
error_message=f"Remix generation failed: {str(e)}",
|
error_message=f"Remix generation failed: {str(e)}",
|
||||||
status_code=500,
|
status_code=429 if is_cf_or_429 else 500,
|
||||||
response_text=str(e)
|
response_text=str(e)
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|||||||
Reference in New Issue
Block a user