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

@@ -1,7 +1,8 @@
import pytest
from unittest.mock import AsyncMock, MagicMock
from src.core.api_format import APIFormat, register_all_converters
from src.core.api_format import APIFormat
from src.core.api_format.conversion import register_default_normalizers
from src.services.cache.aware_scheduler import CacheAwareScheduler
@@ -26,7 +27,7 @@ def _mock_endpoint(api_format: str, config: dict | None = None) -> MagicMock:
@pytest.mark.asyncio
async def test_build_candidates_blocks_cross_format_when_global_switch_off() -> None:
register_all_converters()
register_default_normalizers()
scheduler = CacheAwareScheduler()
scheduler._check_model_support = AsyncMock(return_value=(True, None, None, {"m"})) # type: ignore[attr-defined]
@@ -56,7 +57,7 @@ async def test_build_candidates_blocks_cross_format_when_global_switch_off() ->
@pytest.mark.asyncio
async def test_build_candidates_includes_cross_format_when_enabled() -> None:
register_all_converters()
register_default_normalizers()
scheduler = CacheAwareScheduler()
scheduler._check_model_support = AsyncMock(return_value=(True, None, None, {"m"})) # type: ignore[attr-defined]
@@ -88,7 +89,7 @@ async def test_build_candidates_includes_cross_format_when_enabled() -> None:
@pytest.mark.asyncio
async def test_exact_matches_rank_before_convertible() -> None:
register_all_converters()
register_default_normalizers()
scheduler = CacheAwareScheduler()
scheduler._check_model_support = AsyncMock(return_value=(True, None, None, {"m"})) # type: ignore[attr-defined]