mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
refactor: 将格式转换全局开关迁移到环境变量配置
- 新增 FORMAT_CONVERSION_ENABLED 环境变量(默认开启) - 移除数据库中的 format_conversion_enabled 系统配置 - 移除前端系统设置页面的格式转换开关 UI - 统一同族格式转换(OPENAI/OPENAI_CLI)逻辑,也需检查全局开关和端点配置 - 流生成器使用 ctx.needs_conversion 替代 _needs_format_conversion 方法调用 - 更新测试用例适配新逻辑 - 清理 .env.example 中过时的超时和连接池配置注释
This commit is contained in:
@@ -862,8 +862,9 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
# 使用增量解码器处理跨 chunk 的 UTF-8 字符
|
||||
decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
|
||||
|
||||
# 检查是否需要格式转换
|
||||
needs_conversion = self._needs_format_conversion(ctx)
|
||||
# 使用已设置的 ctx.needs_conversion(由候选筛选阶段根据端点配置判断)
|
||||
# 不再调用 _needs_format_conversion,它只检查格式差异,不检查端点配置
|
||||
needs_conversion = ctx.needs_conversion
|
||||
|
||||
async for chunk in stream_response.aiter_bytes():
|
||||
buffer += chunk
|
||||
@@ -1224,8 +1225,9 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
# 使用增量解码器处理跨 chunk 的 UTF-8 字符
|
||||
decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
|
||||
|
||||
# 检查是否需要格式转换
|
||||
needs_conversion = self._needs_format_conversion(ctx)
|
||||
# 使用已设置的 ctx.needs_conversion(由候选筛选阶段根据端点配置判断)
|
||||
# 不再调用 _needs_format_conversion,它只检查格式差异,不检查端点配置
|
||||
needs_conversion = ctx.needs_conversion
|
||||
|
||||
# 先处理预读的字节块
|
||||
for chunk in prefetched_chunks:
|
||||
@@ -2394,7 +2396,13 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
|
||||
def _needs_format_conversion(self, ctx: StreamContext) -> bool:
|
||||
"""
|
||||
检查是否需要进行格式转换
|
||||
[已废弃] 仅根据格式差异判断是否需要转换
|
||||
|
||||
警告:此方法只检查格式是否不同,不检查端点的 format_acceptance_config 配置!
|
||||
正确的判断应使用候选筛选阶段的结果(ctx.needs_conversion),该结果由
|
||||
is_format_compatible() 函数根据全局开关和端点配置计算得出。
|
||||
|
||||
此方法保留仅供调试和日志输出使用,流生成器中不应调用此方法。
|
||||
|
||||
当 Provider 的 API 格式与客户端请求的 API 格式不同时,需要转换响应。
|
||||
例如:客户端请求 Claude 格式,但 Provider 返回 OpenAI 格式。
|
||||
|
||||
@@ -99,9 +99,10 @@ def _get_formats_for_api(api_format: str) -> list[str]:
|
||||
return _OPENAI_FORMATS
|
||||
|
||||
|
||||
def _is_format_conversion_enabled(db: Session) -> bool:
|
||||
"""检查全局格式转换开关"""
|
||||
return bool(SystemConfigService.get_config(db, "format_conversion_enabled", False))
|
||||
def _is_format_conversion_enabled() -> bool:
|
||||
"""检查全局格式转换开关(从环境变量读取,默认开启)"""
|
||||
from src.config.settings import config
|
||||
return config.format_conversion_enabled
|
||||
|
||||
|
||||
def _get_convertible_formats(client_format: str, global_conversion_enabled: bool) -> list[str]:
|
||||
@@ -511,7 +512,7 @@ async def list_models(
|
||||
restrictions = AccessRestrictions.from_api_key_and_user(key_record, user)
|
||||
|
||||
# 获取可用格式(包括可转换的格式)
|
||||
global_conversion_enabled = _is_format_conversion_enabled(db)
|
||||
global_conversion_enabled = _is_format_conversion_enabled()
|
||||
candidate_formats = _get_convertible_formats(api_format, global_conversion_enabled)
|
||||
candidate_formats, empty_response = _filter_formats_by_restrictions(
|
||||
candidate_formats, restrictions, api_format
|
||||
@@ -615,7 +616,7 @@ async def retrieve_model(
|
||||
restrictions = AccessRestrictions.from_api_key_and_user(key_record, user)
|
||||
|
||||
# 获取可用格式(包括可转换的格式)
|
||||
global_conversion_enabled = _is_format_conversion_enabled(db)
|
||||
global_conversion_enabled = _is_format_conversion_enabled()
|
||||
candidate_formats = _get_convertible_formats(api_format, global_conversion_enabled)
|
||||
candidate_formats, _ = _filter_formats_by_restrictions(
|
||||
candidate_formats, restrictions, api_format
|
||||
@@ -700,7 +701,7 @@ async def list_models_gemini(
|
||||
restrictions = AccessRestrictions.from_api_key_and_user(key_record, user)
|
||||
|
||||
# 获取可用格式(包括可转换的格式)
|
||||
global_conversion_enabled = _is_format_conversion_enabled(db)
|
||||
global_conversion_enabled = _is_format_conversion_enabled()
|
||||
candidate_formats = _get_convertible_formats("gemini", global_conversion_enabled)
|
||||
candidate_formats, empty_response = _filter_formats_by_restrictions(
|
||||
candidate_formats, restrictions, "gemini"
|
||||
@@ -781,7 +782,7 @@ async def get_model_gemini(
|
||||
restrictions = AccessRestrictions.from_api_key_and_user(key_record, user)
|
||||
|
||||
# 获取可用格式(包括可转换的格式)
|
||||
global_conversion_enabled = _is_format_conversion_enabled(db)
|
||||
global_conversion_enabled = _is_format_conversion_enabled()
|
||||
candidate_formats = _get_convertible_formats("gemini", global_conversion_enabled)
|
||||
candidate_formats, _ = _filter_formats_by_restrictions(
|
||||
candidate_formats, restrictions, "gemini"
|
||||
|
||||
@@ -147,6 +147,13 @@ class Config:
|
||||
# HTTP_REQUEST_TIMEOUT: 非流式请求整体超时(秒),默认 300 秒
|
||||
self.http_request_timeout = float(os.getenv("HTTP_REQUEST_TIMEOUT", "300.0"))
|
||||
|
||||
# 格式转换配置
|
||||
# FORMAT_CONVERSION_ENABLED: 全局格式转换总开关,默认开启
|
||||
# 注意:即使开启,也需要端点配置 format_acceptance_config.enabled=true 才能生效
|
||||
self.format_conversion_enabled = os.getenv(
|
||||
"FORMAT_CONVERSION_ENABLED", "true"
|
||||
).lower() == "true"
|
||||
|
||||
# HTTP 连接池配置
|
||||
# HTTP_MAX_CONNECTIONS: 最大连接数,影响并发能力
|
||||
# - 每个连接占用一个 socket,过多会耗尽系统资源
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
"""
|
||||
格式兼容性检查
|
||||
|
||||
用于候选筛选时判断端点是否可以处理客户端请式。
|
||||
用于候选筛选时判断端点是否可以处理客户端请求格式。
|
||||
|
||||
转换逻辑:
|
||||
1. 格式完全匹配 -> 透传(无需转换)
|
||||
2. 同族格式透传(CLAUDE/CLAUDE_CLI、GEMINI/GEMINI_CLI 格式相同,只是认证不同)
|
||||
3. 需要转换的情况 -> 检查全局开关 + 端点配置 + 转换器能力
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -33,7 +38,7 @@ def is_format_compatible(
|
||||
endpoint_api_format: 端点的 API 格式
|
||||
endpoint_format_acceptance_config: 端点的格式接受配置
|
||||
is_stream: 是否是流式请求
|
||||
global_conversion_enabled: 全局格式转换开关
|
||||
global_conversion_enabled: 全局格式转换开关(来自环境变量 FORMAT_CONVERSION_ENABLED,默认 True)
|
||||
registry: 转换器注册表(可选,默认使用全局单例)
|
||||
|
||||
Returns:
|
||||
@@ -55,7 +60,7 @@ def is_format_compatible(
|
||||
provider_format = endpoint_api_format.upper()
|
||||
client_format_upper = client_format.upper()
|
||||
|
||||
# 1. 格式完全匹配 -> 兼容,无需转换
|
||||
# 1. 格式完全匹配 -> 透传(无需转换)
|
||||
if provider_format == client_format_upper:
|
||||
return True, False, None
|
||||
|
||||
@@ -63,32 +68,18 @@ def is_format_compatible(
|
||||
provider_base = get_base_format(provider_format)
|
||||
client_base = get_base_format(client_format_upper)
|
||||
|
||||
if provider_base == client_base:
|
||||
# 同族格式:检查是否需要转换
|
||||
# - OPENAI 和 OPENAI_CLI 的请求/响应格式不同(Chat Completions vs Responses API),需要转换
|
||||
# - CLAUDE 和 CLAUDE_CLI、GEMINI 和 GEMINI_CLI 格式相同,只是认证不同,可透传
|
||||
if provider_base == "OPENAI":
|
||||
# OPENAI/OPENAI_CLI 同族转换:兼容但需要转换
|
||||
# 检查转换器能力(同族转换不需要全局开关和端点配置)
|
||||
if registry.can_convert_full(
|
||||
client_format_upper, provider_format, require_stream=is_stream
|
||||
):
|
||||
return True, True, None # 兼容,需要转换
|
||||
else:
|
||||
return (
|
||||
False,
|
||||
False,
|
||||
f"不存在 {client_format} <-> {provider_format} 的完整转换器",
|
||||
)
|
||||
else:
|
||||
# CLAUDE/CLAUDE_CLI、GEMINI/GEMINI_CLI 等:格式相同,可透传
|
||||
return True, False, None
|
||||
is_same_family = provider_base == client_base
|
||||
if is_same_family and provider_base != "OPENAI":
|
||||
# CLAUDE/CLAUDE_CLI、GEMINI/GEMINI_CLI 等:格式相同,只是认证不同,可透传
|
||||
# 注意:这些格式之间没有定义转换器,因为数据格式完全相同
|
||||
return True, False, None
|
||||
|
||||
# 3. 检查全局开关
|
||||
# 3. 需要转换的情况(OPENAI/OPENAI_CLI 同族转换 或 跨格式转换)
|
||||
# 检查全局开关(来自环境变量,默认开启)
|
||||
if not global_conversion_enabled:
|
||||
return False, False, "全局格式转换未启用"
|
||||
return False, False, "全局格式转换未启用(环境变量 FORMAT_CONVERSION_ENABLED=false)"
|
||||
|
||||
# 4. 检查端点配置
|
||||
# 4. 检查端点配置(核心控制)
|
||||
if endpoint_format_acceptance_config is None:
|
||||
return False, False, "端点未配置格式转换"
|
||||
|
||||
|
||||
5
src/services/cache/aware_scheduler.py
vendored
5
src/services/cache/aware_scheduler.py
vendored
@@ -659,9 +659,8 @@ class CacheAwareScheduler:
|
||||
return [], global_model_id
|
||||
|
||||
# 2. 构建候选列表(传入 is_stream 和 capability_requirements 用于过滤)
|
||||
global_conversion_enabled = bool(
|
||||
SystemConfigService.get_config(db, "format_conversion_enabled", False)
|
||||
)
|
||||
from src.config.settings import config
|
||||
global_conversion_enabled = config.format_conversion_enabled
|
||||
candidates = await self._build_candidates(
|
||||
db=db,
|
||||
providers=providers,
|
||||
|
||||
@@ -118,10 +118,6 @@ class SystemConfigService:
|
||||
"value": "cache_affinity",
|
||||
"description": "调度模式:fixed_order(固定顺序模式,严格按优先级顺序) 或 cache_affinity(缓存亲和模式,优先使用已缓存的Provider)",
|
||||
},
|
||||
"format_conversion_enabled": {
|
||||
"value": False,
|
||||
"description": "是否启用全局格式自动转换(需要端点配置 format_acceptance_config 才能生效)",
|
||||
},
|
||||
"auto_delete_expired_keys": {
|
||||
"value": False,
|
||||
"description": "是否自动删除过期的API Key(True=物理删除,False=仅禁用),仅管理员可配置",
|
||||
|
||||
Reference in New Issue
Block a user