refactor: 提取 HandlerAdapterBase 基类,新增 api_family/endpoint_kind 结构化维度

- 从 ChatAdapterBase 和 CliAdapterBase 提取公共逻辑到 HandlerAdapterBase,
  消除头部处理、异常处理、计费策略等重复代码
- Usage 记录链路全程透传 api_family / endpoint_kind / provider_api_family /
  provider_endpoint_kind 四个结构化字段,替代从 api_format 字符串解析
- 删除冗余的 ClaudeCliNormalizer、GeminiCliNormalizer、ClaudeCliResponseParser、
  GeminiCliResponseParser,改用 data_format_id 回退机制自动复用 Chat 版本
- build_endpoint_url 签名统一扩展 request_data / model_name 参数
- 新增 Alembic 迁移,含历史数据回填
This commit is contained in:
fawney19
2026-02-21 13:17:11 +08:00
parent 54988916e3
commit 0fe5346f4d
36 changed files with 967 additions and 1310 deletions

View File

@@ -1,17 +0,0 @@
"""
Claude CLI Normalizer
CLAUDE_CLI 的请求/响应 body 与 CLAUDE 一致Anthropic Messages API差异主要在认证头。
因此这里复用 ClaudeNormalizer 的转换逻辑,仅更换 FORMAT_ID。
如需 CLI 特殊处理,可覆盖 request_from_internal / request_to_internal 等方法。
"""
from src.core.api_format.conversion.normalizers.claude import ClaudeNormalizer
class ClaudeCliNormalizer(ClaudeNormalizer):
FORMAT_ID = "claude:cli"
__all__ = ["ClaudeCliNormalizer"]

View File

@@ -1,17 +0,0 @@
"""
Gemini CLI Normalizer
GEMINI_CLI 的请求/响应 body 与 GEMINI 一致Google Gemini API差异主要在鉴权/UA 等请求层。
因此这里复用 GeminiNormalizer 的转换逻辑,仅更换 FORMAT_ID。
如需 CLI 特殊处理,可覆盖 request_from_internal / request_to_internal 等方法。
"""
from src.core.api_format.conversion.normalizers.gemini import GeminiNormalizer
class GeminiCliNormalizer(GeminiNormalizer):
FORMAT_ID = "gemini:cli"
__all__ = ["GeminiCliNormalizer"]

View File

@@ -54,7 +54,22 @@ class FormatConversionRegistry:
logger.info(f"[FormatConversionRegistry] 注册 normalizer: {normalizer.FORMAT_ID}")
def get_normalizer(self, format_id: str) -> FormatNormalizer | None:
return self._normalizers.get(str(format_id).upper())
key = str(format_id).upper()
# 1. 精确匹配
normalizer = self._normalizers.get(key)
if normalizer is not None:
return normalizer
# 2. data_format_id 回退:如 "claude:cli" (dfid="claude") -> ClaudeNormalizer (dfid="claude")
from src.core.api_format.metadata import get_data_format_id_for_endpoint
target_dfid = get_data_format_id_for_endpoint(format_id)
if not target_dfid:
return None
for reg_key, reg_normalizer in self._normalizers.items():
reg_dfid = get_data_format_id_for_endpoint(reg_key)
if reg_dfid == target_dfid:
return reg_normalizer
return None
def _require_normalizer(self, format_id: str) -> FormatNormalizer:
normalizer = self.get_normalizer(format_id)

View File

@@ -219,6 +219,16 @@ def get_parser_for_format(format_id: str) -> ResponseParser:
)
normalized = normalize_signature_key(format_id)
if normalized not in _PARSER_REGISTRY:
raise KeyError(f"Unknown format: {normalized}")
return _PARSER_REGISTRY[normalized]()
# 1. 精确匹配
if normalized in _PARSER_REGISTRY:
return _PARSER_REGISTRY[normalized]()
# 2. data_format_id 回退:如 "claude:cli" (dfid="claude") -> ClaudeResponseParser (dfid="claude")
from src.core.api_format.metadata import get_data_format_id_for_endpoint
target_dfid = get_data_format_id_for_endpoint(normalized)
if target_dfid:
for reg_key, parser_cls in _PARSER_REGISTRY.items():
reg_dfid = get_data_format_id_for_endpoint(reg_key)
if reg_dfid == target_dfid:
return parser_cls()
raise KeyError(f"Unknown format: {normalized}")