feat: 实现跨 API 格式自动转换功能

- 新增端点级 format_acceptance_config 配置,控制是否接受跨格式请求
- 重构 EndpointFormDialog 为卡片式布局,支持内联编辑和格式转换开关
- StreamProcessor 实现流式响应的跨格式转换,支持 OpenAI/Claude/Gemini 互转
- CacheAwareScheduler 按端点格式筛选候选,同格式优先于跨格式
- 健康度/熔断按 Provider 端点格式分桶,而非客户端请求格式
- 新增 format_conversion_total 和 format_conversion_duration_seconds 指标
- 新增全局配置 format_conversion_enabled 控制总开关
- Input 组件新增 size="sm" 尺寸选项
This commit is contained in:
fawney19
2026-01-22 01:48:56 +08:00
parent 99388bfa33
commit cc5db20c58
25 changed files with 1818 additions and 547 deletions

View File

@@ -12,11 +12,11 @@ from src.core.api_format import APIFormat
from src.core.exceptions import ConcurrencyLimitError
from src.core.logger import logger
from src.services.health.monitor import health_monitor
from src.services.provider.format import normalize_api_format
from src.services.rate_limit.adaptive_reservation import get_adaptive_reservation_manager
from src.services.request.candidate import RequestCandidateService
@dataclass
class ExecutionContext:
candidate_id: str
@@ -103,7 +103,9 @@ class RequestExecutor:
# 获取有效的 RPM 限制(自适应或固定)
if key.rpm_limit is None:
# 自适应模式:使用学习值,未学习时为 None不限制等待碰壁学习
effective_key_limit = int(key.learned_rpm_limit) if key.learned_rpm_limit is not None else None
effective_key_limit = (
int(key.learned_rpm_limit) if key.learned_rpm_limit is not None else None
)
else:
effective_key_limit = int(key.rpm_limit)
@@ -114,9 +116,11 @@ class RequestExecutor:
)
dynamic_reservation_ratio = reservation_result.ratio
logger.debug(f"[Executor] 动态预留: key={key.id[:8]}..., "
logger.debug(
f"[Executor] 动态预留: key={key.id[:8]}..., "
f"ratio={dynamic_reservation_ratio:.0%}, phase={reservation_result.phase}, "
f"confidence={reservation_result.confidence:.0%}")
f"confidence={reservation_result.confidence:.0%}"
)
async with self.concurrency_manager.rpm_guard(
key_id=key.id,
@@ -140,12 +144,21 @@ class RequestExecutor:
context.elapsed_ms = int((time.time() - context.start_time) * 1000)
provider_api_format = getattr(endpoint, "api_format", None)
provider_format_str = (
provider_api_format.value
if isinstance(provider_api_format, APIFormat)
else str(provider_api_format or "")
)
client_format_str = (
api_format.value if isinstance(api_format, APIFormat) else str(api_format)
)
health_format = normalize_api_format(provider_format_str or client_format_str).value
health_monitor.record_success(
db=self.db,
key_id=key.id,
api_format=(
api_format.value if isinstance(api_format, APIFormat) else api_format
),
api_format=health_format,
response_time_ms=context.elapsed_ms,
)
@@ -180,7 +193,9 @@ class RequestExecutor:
"is_cached_user": is_cached_user,
"model_name": model_name,
"api_format": (
api_format.value if isinstance(api_format, APIFormat) else api_format
api_format.value
if isinstance(api_format, APIFormat)
else api_format
),
},
)