feat: 新增 OAuth 密钥专用编辑对话框 & 统一预设模型管理

- 新增 OAuthKeyEditDialog 组件,支持编辑 OAuth 账号的名称、备注、优先级、RPM 限制、缓存 TTL、熔断探测等配置
- OAuth 账号现在也支持自动获取模型功能,包含模型过滤规则配置
- 移除 OAuth 密钥编辑/权限按钮的 v-if 限制,统一操作入口
- 新增 preset_models.py 模块,统一管理 Kiro/Codex 等无 /v1/models 端点的预设模型
- 重构 Kiro 和 Codex 插件,改用统一的 create_preset_models_fetcher 工厂函数
This commit is contained in:
AAEE86
2026-02-09 12:44:32 +08:00
parent 8673ed1459
commit ea5509f864
6 changed files with 511 additions and 73 deletions

View File

@@ -18,37 +18,13 @@ from urllib.parse import urlencode
from src.core.logger import logger
# ---------------------------------------------------------------------------
# Fixed model catalog
# Preset model catalog
# ---------------------------------------------------------------------------
# Codex upstream (chatgpt.com/backend-api/codex) has no /v1/models endpoint.
# Return a static list of known models.
_CODEX_MODELS: list[dict[str, Any]] = [
{
"id": "gpt-5.2",
"object": "model",
"owned_by": "openai",
"display_name": "gpt-5.2",
},
{
"id": "gpt-5.2-codex",
"object": "model",
"owned_by": "openai",
"display_name": "gpt-5.2-codex",
},
]
# We use the unified preset models registry from preset_models.py.
from src.services.provider.preset_models import create_preset_models_fetcher
async def fetch_models_codex(
ctx: Any,
timeout_seconds: float, # noqa: ARG001
) -> tuple[list[dict], list[str], bool, dict[str, Any] | None]:
"""Return a fixed model catalog for Codex.
Codex upstream does not expose a ``/v1/models`` endpoint, so we skip the
HTTP call entirely and return a hardcoded list.
"""
_ = ctx
return list(_CODEX_MODELS), [], True, None
fetch_models_codex = create_preset_models_fetcher("codex")
# ---------------------------------------------------------------------------

View File

@@ -23,49 +23,13 @@ from src.services.provider.adapters.kiro.constants import (
from src.services.provider.adapters.kiro.context import get_kiro_request_context
# ---------------------------------------------------------------------------
# Fixed model catalog
# Preset model catalog
# ---------------------------------------------------------------------------
# Kiro upstream has no /v1/models endpoint. We return a static list matching
# the models accepted by map_model() in converter.py.
_KIRO_MODELS: list[dict[str, Any]] = [
{
"id": "claude-sonnet-4.5",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Sonnet 4.5",
},
{
"id": "claude-opus-4.5",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Opus 4.5",
},
{
"id": "claude-opus-4.6",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Opus 4.6",
},
{
"id": "claude-haiku-4.5",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Haiku 4.5",
},
]
# Kiro upstream has no /v1/models endpoint. We use the unified preset models
# registry from preset_models.py.
from src.services.provider.preset_models import create_preset_models_fetcher
async def fetch_models_kiro(
ctx: Any,
timeout_seconds: float, # noqa: ARG001
) -> tuple[list[dict], list[str], bool, dict[str, Any] | None]:
"""Return a fixed model catalog for Kiro.
Kiro upstream does not expose a ``/v1/models`` endpoint, so we skip the
HTTP call entirely and return a hardcoded list.
"""
_ = ctx # not needed — no upstream call
return list(_KIRO_MODELS), [], True, None
fetch_models_kiro = create_preset_models_fetcher("kiro")
# ---------------------------------------------------------------------------

View File

@@ -0,0 +1,158 @@
"""预设模型管理模块
为不支持自动获取模型的反代提供商(如 Kiro、Codex提供统一的预设模型管理。
使用方式:
1. 在 PRESET_MODELS 中定义各 provider_type 的预设模型列表
2. 在 plugin.py 中调用 create_preset_models_fetcher() 创建 fetcher 函数
3. 将 fetcher 注册到 UpstreamModelsFetcherRegistry
"""
from __future__ import annotations
from typing import Any
# ---------------------------------------------------------------------------
# 预设模型定义
# ---------------------------------------------------------------------------
# 各 provider_type 对应的预设模型列表
# 格式: provider_type -> list of model dicts
PRESET_MODELS: dict[str, list[dict[str, Any]]] = {
# Kiro (Claude CLI 反代)
"kiro": [
{
"id": "claude-sonnet-4.5",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Sonnet 4.5",
},
{
"id": "claude-opus-4.5",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Opus 4.5",
},
{
"id": "claude-opus-4.6",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Opus 4.6",
},
{
"id": "claude-haiku-4.5",
"object": "model",
"owned_by": "anthropic",
"display_name": "Claude Haiku 4.5",
},
],
# Codex (OpenAI CLI 反代)
"codex": [
{
"id": "gpt-5",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5",
},
{
"id": "gpt-5-codex",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5 Codex",
},
{
"id": "gpt-5-codex-mini",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5 Codex Mini",
},
{
"id": "gpt-5.1",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.1",
},
{
"id": "gpt-5.1-codex",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.1 Codex",
},
{
"id": "gpt-5.1-codex-mini",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.1 Codex Mini",
},
{
"id": "gpt-5.1-codex-max",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.1 Codex Max",
},
{
"id": "gpt-5.2",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.2",
},
{
"id": "gpt-5.2-codex",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.2 Codex",
},
{
"id": "gpt-5.3-codex",
"object": "model",
"owned_by": "openai",
"display_name": "GPT-5.3 Codex",
},
],
}
# ---------------------------------------------------------------------------
# Fetcher 工厂函数
# ---------------------------------------------------------------------------
def get_preset_models(provider_type: str) -> list[dict[str, Any]]:
"""获取指定 provider_type 的预设模型列表。"""
return list(PRESET_MODELS.get(provider_type.lower(), []))
def create_preset_models_fetcher(
provider_type: str,
) -> Any:
"""创建一个返回预设模型列表的 fetcher 函数。
Args:
provider_type: 提供商类型(如 "kiro", "codex"
Returns:
符合 UpstreamModelsFetcherRegistry 签名的 async fetcher 函数
"""
models = get_preset_models(provider_type)
async def fetch_preset_models(
ctx: Any,
timeout_seconds: float, # noqa: ARG001
) -> tuple[list[dict], list[str], bool, dict[str, Any] | None]:
"""Return preset model catalog.
This provider does not expose a /v1/models endpoint, so we skip the
HTTP call entirely and return a hardcoded list.
"""
_ = ctx
_ = timeout_seconds
return list(models), [], True, None
return fetch_preset_models
__all__ = [
"PRESET_MODELS",
"create_preset_models_fetcher",
"get_preset_models",
]