refactor: 重构格式转换系统为 Hub-and-Spoke Normalizer 架构

- 移除旧的 converters 模块,改用基于 Internal 中间表示的 Normalizer 模式
- 新增 internal.py 定义统一的内部数据结构(InternalRequest/Response/Error)
- 新增 normalizer.py 定义 FormatNormalizer 基类接口
- 实现 OpenAI/Claude/Gemini 及其 CLI 格式的 Normalizer
- 新增 stream_events.py 和 stream_state.py 支持流式转换
- 重构 registry.py 使用 Normalizer 进行格式转换
- 更新 handlers 适配新的转换架构
- 前端移除 CLI 格式转换按钮的限制
- 添加 golden data 测试确保转换正确性
This commit is contained in:
fawney19
2026-01-27 02:17:18 +08:00
parent 6cf251b19a
commit add477fece
84 changed files with 9169 additions and 3932 deletions

View File

@@ -198,7 +198,7 @@ class ClaudeChatAdapter(ChatAdapterBase):
else:
return f"{base_url}/v1/messages"
# build_request_body 使用基类实现,通过 converter_registry 自动转换 OPENAI -> CLAUDE
# build_request_body 使用基类实现,通过 format_conversion_registry 自动转换 OPENAI -> CLAUDE
def build_claude_adapter(x_app_header: Optional[str]):

View File

@@ -74,18 +74,23 @@ class ClaudeChatHandler(ChatHandlerBase):
Returns:
ClaudeMessagesRequest 对象
"""
from src.core.api_format import OpenAIToClaudeConverter
from src.core.api_format.conversion.registry import (
format_conversion_registry,
register_default_normalizers,
)
from src.models.claude import ClaudeMessagesRequest
from src.models.openai import OpenAIRequest
register_default_normalizers()
# 如果已经是 Claude 格式,直接返回
if isinstance(request, ClaudeMessagesRequest):
return request
# 如果是 OpenAI 格式,转换为 Claude 格式
if isinstance(request, OpenAIRequest):
converter = OpenAIToClaudeConverter()
claude_dict = converter.convert_request(request.dict())
req_dict = request.model_dump() if hasattr(request, "model_dump") else request.dict()
claude_dict = format_conversion_registry.convert_request(req_dict, "OPENAI", "CLAUDE")
return ClaudeMessagesRequest(**claude_dict)
# 如果是字典,根据内容判断格式
@@ -94,8 +99,7 @@ class ClaudeChatHandler(ChatHandlerBase):
first_msg = request["messages"][0]
if "role" in first_msg and "content" in first_msg:
# 可能是 OpenAI 格式
converter = OpenAIToClaudeConverter()
claude_dict = converter.convert_request(request)
claude_dict = format_conversion_registry.convert_request(request, "OPENAI", "CLAUDE")
return ClaudeMessagesRequest(**claude_dict)
# 否则假设已经是 Claude 格式