diff --git a/src/external/response_adapter.py b/src/external/response_adapter.py index 4517a26..1ef7f5d 100644 --- a/src/external/response_adapter.py +++ b/src/external/response_adapter.py @@ -21,6 +21,7 @@ def build_proxy_response( stream_context_func: Any, logger: Logger, on_complete: Optional[Callable[[Dict[str, Any]], None]] = None, + forward_stream_usage: bool = False, ) -> Response: """基于上游响应与输出钩子构建 Flask 响应。""" hook_func = getattr(hook, "output_body_hook", None) if hook else None @@ -62,6 +63,14 @@ def build_proxy_response( return None return stripped[5:].strip() + def is_usage_only_stream_chunk(payload: Any) -> bool: + if not isinstance(payload, dict): + return False + if not isinstance(payload.get("usage"), dict): + return False + choices = payload.get("choices") + return choices is None or (isinstance(choices, list) and len(choices) == 0) + def process_sse_event(event_text: str) -> Iterator[bytes]: # Preserve non-data SSE fields to avoid breaking strict clients. normalized_text = event_text.replace("\r\n", "\n").replace("\r", "\n") @@ -97,6 +106,8 @@ def build_proxy_response( if isinstance(data, dict): update_meta_from_payload(data) + if not forward_stream_usage and is_usage_only_stream_chunk(data): + return if hook_func: modified = hook_func(ctx, data) diff --git a/src/presentation/proxy_controller.py b/src/presentation/proxy_controller.py index b65f92f..11f4fd5 100644 --- a/src/presentation/proxy_controller.py +++ b/src/presentation/proxy_controller.py @@ -71,6 +71,9 @@ class ProxyController: ) stream_options = request_data.get("stream_options") + client_requested_usage_chunk = isinstance(stream_options, dict) and ( + stream_options.get("include_usage") is True + ) if not isinstance(stream_options, dict): stream_options = {} if stream_options.get("include_usage") is not True: @@ -109,7 +112,11 @@ class ProxyController: ) result, status_code = self._proxy_service.proxy_request( - provider, request_data, headers, on_complete=on_proxy_complete + provider, + request_data, + headers, + on_complete=on_proxy_complete, + forward_stream_usage=client_requested_usage_chunk, ) if result is None: self._logger.error(f"Proxy failed after retries: model={model_name!r}, ip={client_ip}") diff --git a/src/services/proxy_service.py b/src/services/proxy_service.py index 3812acd..654c7ac 100644 --- a/src/services/proxy_service.py +++ b/src/services/proxy_service.py @@ -30,6 +30,7 @@ class ProxyService: request_data: Dict[str, Any], request_headers: Dict[str, str], on_complete: Optional[Callable[[Dict[str, Any]], None]] = None, + forward_stream_usage: bool = False, ) -> Tuple[Optional[Response], int]: """代理请求到目标 provider,并处理重试与输出钩子。""" target_url = provider.api @@ -123,6 +124,7 @@ class ProxyService: stream_with_context, self._logger, on_complete=on_complete, + forward_stream_usage=forward_stream_usage, ) self._logger.info( "Upstream request completed: provider=%s status=%s stream=%s",