mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat(provider,adapter): Codex 默认 body_rules 按 provider_type 维度注册,adapter 全链路传递 provider_type
- 新增 register_provider_default_body_rules 注册机制,将 Codex 特有的 body_rules 从 EndpointDefinition 全局默认移至 codex plugin 按 (provider_type, endpoint_sig) 维度注册 - handler adapter 的 build_endpoint_url/build_request_body/get_cli_extra_headers 增加 provider_type 参数,Codex 判断优先使用 provider_type 而非 URL 匹配 - 前端 getDefaultBodyRules API 支持 provider_type 参数,缓存 key 区分不同 provider 类型;Codex 路径判断同样优先使用 provider_type - ProviderDetailDrawer 将 mapping-preview 拆为独立加载,不阻塞首屏渲染 - PoolManagement 补全 KeyFormDialog 缺失的 endpoint/available-api-formats props - 固定类型 Provider 创建时自动填充 provider-scoped 默认 body_rules
This commit is contained in:
@@ -72,7 +72,9 @@ export async function deleteEndpoint(endpointId: string): Promise<{ message: str
|
||||
/**
|
||||
* 获取指定 API 格式的默认请求体规则
|
||||
*/
|
||||
export async function getDefaultBodyRules(apiFormat: string): Promise<{ api_format: string; body_rules: BodyRule[] }> {
|
||||
const response = await client.get(`/api/admin/endpoints/defaults/${encodeURIComponent(apiFormat)}/body-rules`)
|
||||
export async function getDefaultBodyRules(apiFormat: string, providerType?: string): Promise<{ api_format: string; body_rules: BodyRule[] }> {
|
||||
const params: Record<string, string> = {}
|
||||
if (providerType) params.provider_type = providerType
|
||||
const response = await client.get(`/api/admin/endpoints/defaults/${encodeURIComponent(apiFormat)}/body-rules`, { params })
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -1280,30 +1280,30 @@ const deleteConfirmDescription = computed(() => {
|
||||
|
||||
async function loadDefaultBodyRulesForFormat(apiFormat: string, force = false): Promise<BodyRule[]> {
|
||||
if (!apiFormat) return []
|
||||
if (!force && defaultBodyRulesLoaded.value[apiFormat]) {
|
||||
return defaultBodyRulesByFormat.value[apiFormat] || []
|
||||
const providerType = (props.provider?.provider_type || '').toLowerCase()
|
||||
// 缓存 key 需要包含 provider_type,不同类型的 provider 有不同的默认规则
|
||||
const cacheKey = providerType ? `${apiFormat}:${providerType}` : apiFormat
|
||||
if (!force && defaultBodyRulesLoaded.value[cacheKey]) {
|
||||
return defaultBodyRulesByFormat.value[cacheKey] || []
|
||||
}
|
||||
if (loadingDefaultBodyRulesByFormat.value[apiFormat]) {
|
||||
return defaultBodyRulesByFormat.value[apiFormat] || []
|
||||
if (loadingDefaultBodyRulesByFormat.value[cacheKey]) {
|
||||
return defaultBodyRulesByFormat.value[cacheKey] || []
|
||||
}
|
||||
|
||||
loadingDefaultBodyRulesByFormat.value[apiFormat] = true
|
||||
loadingDefaultBodyRulesByFormat.value[cacheKey] = true
|
||||
try {
|
||||
const response = await getDefaultBodyRules(apiFormat)
|
||||
const normalized = response.api_format || apiFormat
|
||||
const response = await getDefaultBodyRules(apiFormat, providerType || undefined)
|
||||
const rules = response.body_rules || []
|
||||
defaultBodyRulesByFormat.value[normalized] = rules
|
||||
defaultBodyRulesByFormat.value[apiFormat] = rules
|
||||
defaultBodyRulesLoaded.value[normalized] = true
|
||||
defaultBodyRulesLoaded.value[apiFormat] = true
|
||||
defaultBodyRulesByFormat.value[cacheKey] = rules
|
||||
defaultBodyRulesLoaded.value[cacheKey] = true
|
||||
return rules
|
||||
} catch (error: unknown) {
|
||||
defaultBodyRulesByFormat.value[apiFormat] = []
|
||||
defaultBodyRulesLoaded.value[apiFormat] = true
|
||||
defaultBodyRulesByFormat.value[cacheKey] = []
|
||||
defaultBodyRulesLoaded.value[cacheKey] = true
|
||||
log.warn('加载默认请求体规则失败', apiFormat, error)
|
||||
return []
|
||||
} finally {
|
||||
loadingDefaultBodyRulesByFormat.value[apiFormat] = false
|
||||
loadingDefaultBodyRulesByFormat.value[cacheKey] = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1317,7 +1317,11 @@ function getDefaultPath(apiFormat: string, baseUrl?: string): string {
|
||||
const format = apiFormats.value.find(f => f.value === apiFormat)
|
||||
const defaultPath = format?.default_path || ''
|
||||
// Codex 端点使用 /responses 而非 /v1/responses
|
||||
if (apiFormat === 'openai:cli' && baseUrl && isCodexUrl(baseUrl)) {
|
||||
const providerType = (props.provider?.provider_type || '').toLowerCase()
|
||||
const isCodex = providerType
|
||||
? providerType === 'codex'
|
||||
: (!!baseUrl && isCodexUrl(baseUrl))
|
||||
if (apiFormat === 'openai:cli' && isCodex) {
|
||||
return '/responses'
|
||||
}
|
||||
return defaultPath
|
||||
|
||||
@@ -1274,6 +1274,8 @@ watch(
|
||||
[() => props.providerId, () => props.open],
|
||||
async ([newId, newOpen], [_oldId, oldOpen]) => {
|
||||
if (newOpen && newId) {
|
||||
// mapping-preview 较慢,不阻塞首屏渲染
|
||||
void loadMappingPreview()
|
||||
await Promise.all([
|
||||
loadProvider(),
|
||||
loadEndpoints(),
|
||||
@@ -1958,6 +1960,7 @@ async function handleBatchAssignChanged() {
|
||||
|
||||
// 处理模型映射变更
|
||||
async function handleModelMappingChanged() {
|
||||
void loadMappingPreview()
|
||||
await loadEndpoints()
|
||||
emit('refresh')
|
||||
}
|
||||
@@ -2621,18 +2624,17 @@ async function loadEndpoints() {
|
||||
const requestId = ++endpointsLoadRequestId
|
||||
|
||||
try {
|
||||
// 并行加载端点列表、Provider 级别的 keys、models 和映射预览
|
||||
const [endpointsList, providerKeysResult, modelsResult, mappingPreviewResult] = await Promise.all([
|
||||
// 并行加载端点列表、Provider 级别的 keys 和 models
|
||||
// mapping-preview 较慢,拆到 loadMappingPreview 独立加载,不阻塞首屏
|
||||
const [endpointsList, providerKeysResult, modelsResult] = await Promise.all([
|
||||
getProviderEndpoints(props.providerId),
|
||||
getProviderKeys(props.providerId).catch(() => []),
|
||||
getProviderModels(props.providerId).catch(() => []),
|
||||
getProviderMappingPreview(props.providerId).catch(() => null),
|
||||
])
|
||||
if (requestId !== endpointsLoadRequestId) return
|
||||
|
||||
providerKeys.value = providerKeysResult
|
||||
providerModels.value = modelsResult
|
||||
providerMappingPreview.value = mappingPreviewResult
|
||||
// 按 API 格式排序
|
||||
endpoints.value = endpointsList.sort((a, b) => {
|
||||
const aIdx = API_FORMAT_ORDER.indexOf(a.api_format)
|
||||
@@ -2648,6 +2650,16 @@ async function loadEndpoints() {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载映射预览(独立于 loadEndpoints,不阻塞首屏渲染)
|
||||
async function loadMappingPreview() {
|
||||
if (!props.providerId) return
|
||||
try {
|
||||
providerMappingPreview.value = await getProviderMappingPreview(props.providerId)
|
||||
} catch {
|
||||
providerMappingPreview.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 ESC 键监听
|
||||
useEscapeKey(() => {
|
||||
if (props.open) {
|
||||
|
||||
@@ -1083,9 +1083,11 @@
|
||||
<KeyFormDialog
|
||||
v-if="selectedProviderId"
|
||||
:open="keyFormDialogOpen"
|
||||
:endpoint="null"
|
||||
:provider-type="selectedProviderData?.provider_type || selectedProviderType"
|
||||
:editing-key="editingKey"
|
||||
:provider-id="selectedProviderId"
|
||||
:available-api-formats="selectedProviderData?.api_formats || []"
|
||||
@close="closeKeyFormDialog"
|
||||
@saved="handleDialogSaved"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user