优化页面交互并增强 Provider 配置能力
- 表单下拉框不再用 label 包裹,改为 div + aria-label,避免点击后 下拉面板瞬间关闭 - Provider 卡片新增「从上游获取模型」和「修改」入口,支持编辑 base URL、API Key 与 api_mode,凭据写入对应的环境变量 - 服务端抽出 provider-model-discovery 统一不同 api_mode 的模型发现, 旧的 custom_providers 与新的 providers 字典结构都能正确更新 - Agent Bridge 改为后台启动,HTTP 接口在 Python 运行时就绪前即可用; worker 连接被拒时按类型重试,不重放可能已产生副作用的请求 - 断线重连后恢复 profiles、models 与会话列表,健康检查改为自调度 的 setTimeout,离线时 2 秒后重试 - 批量删除确认文案改用 Popconfirm content,避免变成侧栏触发器文本
This commit is contained in:
@@ -194,23 +194,35 @@ export async function create(ctx: any) {
|
||||
|
||||
export async function update(ctx: any) {
|
||||
const poolKey = decodeURIComponent(ctx.params.poolKey)
|
||||
const { name, base_url, api_key, model, api_mode } = ctx.request.body as {
|
||||
name?: string; base_url?: string; api_key?: string; model?: string; api_mode?: ProviderApiMode
|
||||
const { name, base_url, api_key, model, api_mode, source, providerKey } = ctx.request.body as {
|
||||
name?: string; base_url?: string; api_key?: string; model?: string; api_mode?: ProviderApiMode; source?: string; providerKey?: string
|
||||
}
|
||||
const customApiMode = normalizeApiMode(api_mode)
|
||||
if (base_url !== undefined) {
|
||||
try {
|
||||
const parsed = new URL(base_url)
|
||||
if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error('Invalid protocol')
|
||||
} catch {
|
||||
ctx.status = 400; ctx.body = { error: 'base_url must be a valid HTTP or HTTPS URL' }; return
|
||||
}
|
||||
}
|
||||
try {
|
||||
const profile = requestedProfile(ctx)
|
||||
const isCustom = poolKey.startsWith('custom:')
|
||||
if (isCustom) {
|
||||
const found = await updateConfigYamlForProfile(profile, (config) => {
|
||||
if (!Array.isArray(config.custom_providers)) return { data: config, result: false, write: false }
|
||||
const entry = (config.custom_providers as any[]).find((e: any) => {
|
||||
return `custom:${e.name.trim().toLowerCase().replace(/ /g, '-')}` === poolKey
|
||||
})
|
||||
if (!entry) return { data: config, result: false, write: false }
|
||||
const found = await updateConfigYamlForProfile(profile, async (config) => {
|
||||
const legacyIndex = source === 'providers' ? -1 : findLegacyCustomProviderIndex(config, poolKey)
|
||||
const dictKey = source === 'custom_providers' || legacyIndex !== -1
|
||||
? '' : findProviderDictKey(config, poolKey, providerKey)
|
||||
const entry = legacyIndex !== -1 ? config.custom_providers[legacyIndex] : dictKey ? config.providers[dictKey] : null
|
||||
const normalized = normalizeCustomProviderEntry(entry, dictKey, dictKey ? 'providers' : 'custom_providers')
|
||||
if (!entry || !normalized || providerKeyForCustomName(normalized.name) !== poolKey) return { data: config, result: false, write: false }
|
||||
if (name !== undefined) entry.name = name
|
||||
if (base_url !== undefined) entry.base_url = base_url
|
||||
if (api_key !== undefined) entry.api_key = api_key
|
||||
if (base_url !== undefined) entry.base_url = base_url.trim()
|
||||
if (api_key !== undefined) {
|
||||
if (normalized.key_env) await saveEnvValueForProfile(profile, normalized.key_env, api_key)
|
||||
entry.api_key = api_key
|
||||
}
|
||||
if (model !== undefined) entry.model = model
|
||||
if (customApiMode !== undefined) entry.api_mode = customApiMode
|
||||
return { data: config, result: true }
|
||||
@@ -220,10 +232,17 @@ export async function update(ctx: any) {
|
||||
}
|
||||
} else {
|
||||
const envMapping = PROVIDER_ENV_MAP[poolKey]
|
||||
if (!envMapping?.api_key_env) {
|
||||
if (!envMapping || (!envMapping.api_key_env && !envMapping.base_url_env)) {
|
||||
ctx.status = 400; ctx.body = { error: `Cannot update credentials for "${poolKey}"` }; return
|
||||
}
|
||||
if (api_key !== undefined) { await saveEnvValueForProfile(profile, envMapping.api_key_env, api_key) }
|
||||
if (base_url !== undefined && !envMapping.base_url_env) {
|
||||
ctx.status = 400; ctx.body = { error: `Cannot update base URL for "${poolKey}"` }; return
|
||||
}
|
||||
if (api_key !== undefined && !envMapping.api_key_env) {
|
||||
ctx.status = 400; ctx.body = { error: `Cannot update API key for "${poolKey}"` }; return
|
||||
}
|
||||
if (api_key !== undefined) await saveEnvValueForProfile(profile, envMapping.api_key_env, api_key)
|
||||
if (base_url !== undefined) await saveEnvValueForProfile(profile, envMapping.base_url_env, base_url.trim())
|
||||
}
|
||||
// TODO: Test if provider works without gateway restart
|
||||
// try { await hermesCli.restartGateway() } catch (e: any) { logger.error(e, 'Gateway restart failed') }
|
||||
|
||||
Reference in New Issue
Block a user