feat(codex): 拆分openai:compact为独立端点,简化Codex请求为透传模式

- 新增openai:compact端点类型(EndpointKind.COMPACT),独立于openai:cli
- OpenAICompactAdapter继承OpenAICliAdapter,自动标记compact模式
- Codex请求补丁改为纯透传:仅清理内部标记,不再修改客户端payload
- stream_policy支持openai:compact独立策略,compact端点移除stream字段
- candidate_builder支持compact回退到cli端点
- auth_type: vertex_ai重命名为service_account,保持向后兼容
- Vertex Provider新增api_formats与auth_type组合校验
- KeyAllowedModels对话框改为从Provider获取模型,展示provider_model_name
- Dialog内Select组件自动禁用Portal,修复层级遮挡问题
- 新增Codex compact端点回填迁移脚本
This commit is contained in:
fawney19
2026-03-01 23:55:26 +08:00
parent 4bf3a453e7
commit 97d42703da
37 changed files with 650 additions and 286 deletions

View File

@@ -422,6 +422,7 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
preferred_order = [
"openai:chat",
"openai:cli",
"openai:compact",
"openai:video",
"claude:chat",
"claude:cli",

View File

@@ -838,15 +838,20 @@ class AdminGetApiFormatsAdapter(AdminApiAdapter):
def _label_for(sig: str) -> str:
fam, kind = (sig.split(":", 1) + [""])[:2]
fam_title = {"claude": "Claude", "openai": "OpenAI", "gemini": "Gemini"}.get(fam, fam)
kind_title = {"chat": "Chat", "cli": "CLI", "video": "Video", "image": "Image"}.get(
kind, kind
)
kind_title = {
"chat": "Chat",
"cli": "CLI",
"compact": "Compact",
"video": "Video",
"image": "Image",
}.get(kind, kind)
return f"{fam_title} {kind_title}".strip()
endpoint_defs = list_endpoint_definitions()
preferred_order = [
"openai:chat",
"openai:cli",
"openai:compact",
"openai:video",
"claude:chat",
"claude:cli",

View File

@@ -2,10 +2,11 @@
OpenAI CLI 透传处理器
"""
from src.api.handlers.openai_cli.adapter import OpenAICliAdapter
from src.api.handlers.openai_cli.adapter import OpenAICliAdapter, OpenAICompactAdapter
from src.api.handlers.openai_cli.handler import OpenAICliMessageHandler
__all__ = [
"OpenAICliAdapter",
"OpenAICompactAdapter",
"OpenAICliMessageHandler",
]

View File

@@ -15,7 +15,7 @@ from src.api.handlers.base.cli_adapter_base import CliAdapterBase, register_cli_
from src.api.handlers.base.cli_handler_base import CliMessageHandlerBase
from src.api.handlers.openai.adapter import OpenAIChatAdapter
from src.config.settings import config
from src.core.api_format import ApiFamily
from src.core.api_format import ApiFamily, EndpointKind
from src.utils.url_utils import is_codex_url
@@ -166,3 +166,18 @@ class OpenAICliAdapter(CliAdapterBase):
__all__ = ["OpenAICliAdapter"]
@register_cli_adapter
class OpenAICompactAdapter(OpenAICliAdapter):
"""OpenAI Compact Responses adapter (/v1/responses/compact)."""
FORMAT_ID = "openai:compact"
ENDPOINT_KIND = EndpointKind.COMPACT
name = "openai.compact"
def __init__(self, allowed_api_formats: list[str] | None = None):
super().__init__(allowed_api_formats=allowed_api_formats, compact=True)
__all__.append("OpenAICompactAdapter")

View File

@@ -36,7 +36,7 @@ router = APIRouter(tags=["System Catalog"])
# 各格式对应的 API 格式列表(包括对应的 CLI 格式)
_CLAUDE_FORMATS = ["claude:chat", "claude:cli"]
_OPENAI_FORMATS = ["openai:chat", "openai:cli"]
_OPENAI_FORMATS = ["openai:chat", "openai:cli", "openai:compact"]
_GEMINI_FORMATS = ["gemini:chat", "gemini:cli"]
# 所有格式(用于格式转换时的查询)

View File

@@ -15,7 +15,7 @@ from sqlalchemy.orm import Session
from src.api.base.pipeline import ApiRequestPipeline
from src.api.handlers.openai import OpenAIChatAdapter
from src.api.handlers.openai_cli import OpenAICliAdapter
from src.api.handlers.openai_cli import OpenAICliAdapter, OpenAICompactAdapter
from src.database import get_db
router = APIRouter(tags=["OpenAI API"])
@@ -68,7 +68,7 @@ async def create_responses_compact(
**认证方式**: Bearer TokenAPI Key 或 JWT Token
"""
adapter = OpenAICliAdapter(compact=True)
adapter = OpenAICompactAdapter()
return await pipeline.run(
adapter=adapter,
http_request=http_request,

View File

@@ -1263,6 +1263,7 @@ class ListAvailableModelsAdapter(AuthenticatedApiAdapter):
all_formats = [
"openai:chat",
"openai:cli",
"openai:compact",
"claude:chat",
"claude:cli",
"gemini:chat",