From 50ffd47546b9d546204852fd2445c35d508ca9d1 Mon Sep 17 00:00:00 2001 From: fawney19 Date: Fri, 19 Dec 2025 01:36:20 +0800 Subject: [PATCH] fix: handle client disconnection after stream completion gracefully - Check has_completion flag before marking client disconnection as failure - Allow graceful termination if response already completed when client disconnects - Change logging level to info for post-completion disconnections - Prevent false error reporting when client closes connection after receiving full response --- src/api/handlers/base/cli_handler_base.py | 6 ++++-- src/api/handlers/base/stream_processor.py | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/api/handlers/base/cli_handler_base.py b/src/api/handlers/base/cli_handler_base.py index 698e0ad..0d8ca16 100644 --- a/src/api/handlers/base/cli_handler_base.py +++ b/src/api/handlers/base/cli_handler_base.py @@ -1114,8 +1114,10 @@ class CliMessageHandlerBase(BaseMessageHandler): async for chunk in stream_generator: yield chunk except asyncio.CancelledError: - ctx.status_code = 499 - ctx.error_message = "Client disconnected" + # 如果响应已完成,不标记为失败 + if not ctx.has_completion: + ctx.status_code = 499 + ctx.error_message = "Client disconnected" raise except httpx.TimeoutException as e: ctx.status_code = 504 diff --git a/src/api/handlers/base/stream_processor.py b/src/api/handlers/base/stream_processor.py index a5bcb22..5429d1e 100644 --- a/src/api/handlers/base/stream_processor.py +++ b/src/api/handlers/base/stream_processor.py @@ -410,16 +410,22 @@ class StreamProcessor: if now >= next_disconnect_check_at: next_disconnect_check_at = now + disconnect_check_interval_s if await is_disconnected(): - logger.warning(f"ID:{self.request_id} | Client disconnected") - ctx.status_code = 499 # Client Closed Request - ctx.error_message = "client_disconnected" - + # 如果响应已完成(收到 finish_reason),客户端断开不算失败 + if ctx.has_completion: + logger.info( + f"ID:{self.request_id} | Client disconnected after completion" + ) + else: + logger.warning(f"ID:{self.request_id} | Client disconnected") + ctx.status_code = 499 # Client Closed Request + ctx.error_message = "client_disconnected" break yield chunk except asyncio.CancelledError: - ctx.status_code = 499 - ctx.error_message = "client_disconnected" - + # 如果响应已完成,不标记为失败 + if not ctx.has_completion: + ctx.status_code = 499 + ctx.error_message = "client_disconnected" raise except Exception as e: ctx.status_code = 500