refactor: 增强跨格式转换系统,支持 thinking/文件/音频/响应格式等完整转换

- 新增 ThinkingConfig/ResponseFormatConfig/FileBlock/AudioBlock 内部表示
- 实现 OpenAI reasoning_effort <-> Claude thinking <-> Gemini thinkingConfig 互转
- 支持 OpenAI web_search_options -> Claude web_search tool 转换
- 新增异步 convert_request_async,在转换阶段解析图片 URL 为 base64
- 将 GlobalModel.output_limit 传播至跨格式转换用于 max_tokens 默认值
- 前端模型表单增加最大输出 Token 和上下文窗口配置
- Claude normalizer 保留 cache_control 透传(system 数组和 content block)
- Gemini normalizer 支持内置工具(googleSearch/codeExecution/urlContext)
- OpenAI normalizer 补全采样参数、response_format、usage details 转换
- 各 normalizer 工具方法提取至基类消除重复代码
- 简化 Kiro model mapping 为直接透传
- 预置模型列表添加 claude-sonnet-4.6
This commit is contained in:
fawney19
2026-02-19 10:40:49 +08:00
parent 11f2ddbba2
commit 0d2cafaec3
20 changed files with 1405 additions and 234 deletions

View File

@@ -706,11 +706,12 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
# 跨格式:先做请求体转换(失败触发 failover
registry = get_format_converter_registry()
if needs_conversion:
request_body = registry.convert_request(
request_body = await registry.convert_request_async(
request_body,
str(client_api_format),
str(provider_api_format),
target_variant=cross_format_variant,
output_limit=candidate.output_limit if candidate else None,
)
# 格式转换后,为需要 model 字段的格式设置模型名
self._set_model_after_conversion(

View File

@@ -155,7 +155,7 @@ class CliHandlerProtocol(Protocol):
mapped_model: str | None,
) -> str | None: ...
def _convert_request_for_cross_format(
async def _convert_request_for_cross_format(
self,
request_body: dict[str, Any],
client_api_format: str,
@@ -165,6 +165,7 @@ class CliHandlerProtocol(Protocol):
is_stream: bool,
*,
target_variant: str | None = ...,
output_limit: int | None = ...,
) -> tuple[dict[str, Any], str]: ...
def _extract_response_metadata(

View File

@@ -228,7 +228,7 @@ class CliRequestMixin:
stream_options["include_usage"] = True
request_body["stream_options"] = stream_options
def _convert_request_for_cross_format(
async def _convert_request_for_cross_format(
self,
request_body: dict[str, Any],
client_api_format: str,
@@ -238,6 +238,7 @@ class CliRequestMixin:
is_stream: bool,
*,
target_variant: str | None = None,
output_limit: int | None = None,
) -> tuple[dict[str, Any], str]:
"""
跨格式请求转换的公共逻辑
@@ -252,16 +253,18 @@ class CliRequestMixin:
fallback_model: 备用模型名(通常是原始请求的 model
is_stream: 是否流式请求
target_variant: 目标变体(如 "codex"),用于同格式但有细微差异的上游
output_limit: GlobalModel 配置的模型输出上限
Returns:
(转换后的请求体, 用于 URL 的模型名)
"""
registry = get_format_converter_registry()
converted_body = registry.convert_request(
converted_body = await registry.convert_request_async(
request_body,
str(client_api_format),
str(provider_api_format),
target_variant=target_variant,
output_limit=output_limit,
)
# 先计算 URL 模型(在清理 body 中的 model 字段之前)

View File

@@ -317,7 +317,7 @@ class CliStreamMixin:
# 跨格式:先做请求体转换(失败触发 failover
if needs_conversion and provider_api_format:
request_body, url_model = self._convert_request_for_cross_format(
request_body, url_model = await self._convert_request_for_cross_format(
request_body,
client_api_format,
provider_api_format,
@@ -325,6 +325,7 @@ class CliStreamMixin:
ctx.model,
is_stream=upstream_is_stream,
target_variant=conversion_variant,
output_limit=candidate.output_limit if candidate else None,
)
else:
# 同格式:按原逻辑做轻量清理(子类可覆盖)

View File

@@ -150,7 +150,7 @@ class CliSyncMixin:
# 跨格式:先做请求体转换(失败触发 failover
if needs_conversion and provider_api_format:
request_body, url_model = self._convert_request_for_cross_format(
request_body, url_model = await self._convert_request_for_cross_format(
request_body,
client_api_format,
provider_api_format,
@@ -158,6 +158,7 @@ class CliSyncMixin:
model,
is_stream=upstream_is_stream,
target_variant=conversion_variant,
output_limit=candidate.output_limit if candidate else None,
)
else:
# 同格式:按原逻辑做轻量清理(子类可覆盖)