""" 请求构建器 - 透传模式 透传模式 (Passthrough): CLI 和 Chat 等场景,原样转发请求体和头部 - 清理敏感头部:authorization, x-api-key, host, content-length 等 - 保留所有其他头部和请求体字段 - 适用于:Claude CLI、OpenAI CLI、Chat API 等场景 使用方式: builder = PassthroughRequestBuilder() payload, headers = builder.build(original_body, original_headers, endpoint, key) """ from __future__ import annotations from abc import ABC, abstractmethod from typing import Any, Dict, FrozenSet, Optional, Tuple from src.core.crypto import crypto_service # ============================================================================== # 统一的头部配置常量 # ============================================================================== # 敏感头部 - 透传时需要清理(黑名单) # 这些头部要么包含认证信息,要么由代理层重新生成 SENSITIVE_HEADERS: FrozenSet[str] = frozenset( { "authorization", "x-api-key", "x-goog-api-key", # Gemini API 认证头 "host", "content-length", "transfer-encoding", "connection", # 不透传 accept-encoding,让 httpx 自己协商压缩格式 # 避免客户端请求 brotli/zstd 但 httpx 不支持解压的问题 "accept-encoding", } ) # ============================================================================== # 请求构建器 # ============================================================================== class RequestBuilder(ABC): """请求构建器抽象基类""" @abstractmethod def build_payload( self, original_body: Dict[str, Any], *, mapped_model: Optional[str] = None, is_stream: bool = False, ) -> Dict[str, Any]: """构建请求体""" pass @abstractmethod def build_headers( self, original_headers: Dict[str, str], endpoint: Any, key: Any, *, extra_headers: Optional[Dict[str, str]] = None, ) -> Dict[str, str]: """构建请求头""" pass def build( self, original_body: Dict[str, Any], original_headers: Dict[str, str], endpoint: Any, key: Any, *, mapped_model: Optional[str] = None, is_stream: bool = False, extra_headers: Optional[Dict[str, str]] = None, ) -> Tuple[Dict[str, Any], Dict[str, str]]: """ 构建完整的请求(请求体 + 请求头) Returns: Tuple[payload, headers] """ payload = self.build_payload( original_body, mapped_model=mapped_model, is_stream=is_stream, ) headers = self.build_headers( original_headers, endpoint, key, extra_headers=extra_headers, ) return payload, headers class PassthroughRequestBuilder(RequestBuilder): """ 透传模式请求构建器 适用于 CLI 等场景,尽量保持请求原样: - 请求体:直接复制,只修改必要字段(model, stream) - 请求头:清理敏感头部(黑名单),透传其他所有头部 """ def build_payload( self, original_body: Dict[str, Any], *, mapped_model: Optional[str] = None, # noqa: ARG002 - 由 apply_mapped_model 处理 is_stream: bool = False, # noqa: ARG002 - 保留原始值,不自动添加 ) -> Dict[str, Any]: """ 透传请求体 - 原样复制,不做任何修改 透传模式下: - model: 由各 handler 的 apply_mapped_model 方法处理 - stream: 保留客户端原始值(不同 API 处理方式不同) """ return dict(original_body) def build_headers( self, original_headers: Dict[str, str], endpoint: Any, key: Any, *, extra_headers: Optional[Dict[str, str]] = None, ) -> Dict[str, str]: """ 透传请求头 - 清理敏感头部(黑名单),透传其他所有头部 """ from src.core.api_format_metadata import get_auth_config, resolve_api_format headers: Dict[str, str] = {} # 1. 根据 API 格式自动设置认证头 decrypted_key = crypto_service.decrypt(key.api_key) api_format = getattr(endpoint, "api_format", None) resolved_format = resolve_api_format(api_format) auth_header, auth_type = ( get_auth_config(resolved_format) if resolved_format else ("Authorization", "bearer") ) if auth_type == "bearer": headers[auth_header] = f"Bearer {decrypted_key}" else: headers[auth_header] = decrypted_key # 2. 添加 endpoint 配置的额外头部 if endpoint.headers: headers.update(endpoint.headers) # 3. 透传原始头部(排除敏感头部 - 黑名单模式) if original_headers: for name, value in original_headers.items(): lower_name = name.lower() # 跳过敏感头部 if lower_name in SENSITIVE_HEADERS: continue headers[name] = value # 4. 添加额外头部 if extra_headers: headers.update(extra_headers) # 5. 确保有 Content-Type if "Content-Type" not in headers and "content-type" not in headers: headers["Content-Type"] = "application/json" return headers # ============================================================================== # 便捷函数 # ============================================================================== def build_passthrough_request( original_body: Dict[str, Any], original_headers: Dict[str, str], endpoint: Any, key: Any, ) -> Tuple[Dict[str, Any], Dict[str, str]]: """ 构建透传模式的请求 纯透传:原样复制请求体,只处理请求头(认证等)。 model mapping 和 stream 由调用方自行处理(不同 API 格式处理方式不同)。 """ builder = PassthroughRequestBuilder() return builder.build( original_body, original_headers, endpoint, key, )