feat(provider): 重构模型测试对话框,加固 Vertex AI 传输层

模型测试:
- 将消息输入替换为完整 JSON 请求体编辑器,支持格式化和校验
- 新增端点选择面板,测试前可选择目标端点
- 新增调试检查器,可查看每次尝试的请求/响应头和体
- 结果视图改用 HorizontalRequestTimeline 组件展示请求追踪
- endpoint_checker 返回完整调试数据,通过 candidate extra_data 持久化

Vertex AI:
- 改进上下文检测逻辑,不再仅依赖 provider_type,支持从 base_url 推断
- Service Account 密钥现支持自动拉取模型(使用 auth_config 而非 api_key)
- 移除 Gemini Developer API 回退,API Key 仅走 Express 模式
- 端点表单为 Vertex AI 显示格式特定的默认路径模板
- 密钥格式校验仅在 auth_type/api_formats 变更时执行

其他:
- 禁用 ClaudeCode 提供商类型创建入口
- Dialog 组件新增 closeOnBackdrop 属性
This commit is contained in:
fawney19
2026-03-19 23:52:17 +08:00
parent e4ebd5cca1
commit 6984984c22
31 changed files with 1609 additions and 434 deletions
@@ -105,7 +105,7 @@
<div class="space-y-1.5">
<Label class="text-xs text-muted-foreground">自定义路径</Label>
<Input
:model-value="getEndpointEditState(endpoint.id)?.path ?? (endpoint.custom_path || '')"
:model-value="getDisplayedPath(endpoint)"
:placeholder="getDefaultPath(endpoint.api_format, endpoint.base_url) || '留空使用默认'"
:disabled="isFixedProvider"
@update:model-value="(v) => updateEndpointField(endpoint.id, 'path', v)"
@@ -1276,10 +1276,19 @@ async function preloadDefaultBodyRules(endpoints: ProviderEndpoint[]): Promise<v
// 获取指定 API 格式的默认路径
function getDefaultPath(apiFormat: string, baseUrl?: string): string {
const providerType = (props.provider?.provider_type || '').toLowerCase()
if (providerType === 'vertex_ai') {
if (apiFormat === 'gemini:chat') {
return '/v1/publishers/google/models/{model}:{action}'
}
if (apiFormat === 'claude:chat') {
return '/v1/projects/{project_id}/locations/{region}/publishers/anthropic/models/{model}:{action}'
}
}
const format = apiFormats.value.find(f => f.value === apiFormat)
const defaultPath = format?.default_path || ''
// Codex 端点使用 /responses 而非 /v1/responses
const providerType = (props.provider?.provider_type || '').toLowerCase()
const isCodex = providerType
? providerType === 'codex'
: (!!baseUrl && isCodexUrl(baseUrl))
@@ -1289,6 +1298,13 @@ function getDefaultPath(apiFormat: string, baseUrl?: string): string {
return defaultPath
}
function getDisplayedPath(endpoint: ProviderEndpoint): string {
if (isFixedProvider.value) {
return getDefaultPath(endpoint.api_format, endpoint.base_url)
}
return getEndpointEditState(endpoint.id)?.path ?? (endpoint.custom_path || '')
}
// 判断是否是 Codex OAuth 端点
function isCodexUrl(baseUrl: string): boolean {
const url = baseUrl.replace(/\/+$/, '')