mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
feat(provider): 支持 Provider 级别代理节点配置,修正配额分组匹配
- Provider 详情抽屉新增代理节点设置入口(Globe 图标),支持选择/清除代理 - 更新 Antigravity 配额分组:修正模型匹配规则以适配当前模型命名 - 简化上游元数据合并逻辑,以上游返回为准,不再保留已下架模型条目
This commit is contained in:
@@ -50,6 +50,52 @@
|
||||
<Shuffle class="w-4 h-4" />
|
||||
</Button>
|
||||
</span>
|
||||
<Popover
|
||||
v-if="provider.provider_type !== 'custom'"
|
||||
:open="providerProxyPopoverOpen"
|
||||
@update:open="handleProviderProxyPopoverToggle"
|
||||
>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
:class="provider.proxy?.node_id ? 'text-blue-500' : ''"
|
||||
:disabled="savingProviderProxy"
|
||||
:title="provider.proxy?.node_id ? `代理: ${getProviderProxyNodeName()}` : '设置代理节点'"
|
||||
>
|
||||
<Globe class="w-4 h-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
class="w-72 p-3"
|
||||
side="bottom"
|
||||
align="end"
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium">代理节点</span>
|
||||
<Button
|
||||
v-if="provider.proxy?.node_id"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="h-6 px-2 text-[10px] text-muted-foreground"
|
||||
:disabled="savingProviderProxy"
|
||||
@click="clearProviderProxy"
|
||||
>
|
||||
清除
|
||||
</Button>
|
||||
</div>
|
||||
<ProxyNodeSelect
|
||||
:model-value="provider.proxy?.node_id || ''"
|
||||
trigger-class="h-8"
|
||||
@update:model-value="setProviderProxy"
|
||||
/>
|
||||
<p class="text-[10px] text-muted-foreground">
|
||||
{{ provider.proxy?.node_id ? '当前使用独立代理' : '未设置代理节点' }}
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
@@ -1145,8 +1191,12 @@ const refreshingQuota = ref(false)
|
||||
const antigravityQuotaDialogOpen = ref(false)
|
||||
const antigravityQuotaDialogKey = ref<EndpointAPIKey | null>(null)
|
||||
|
||||
// Key 级别代理配置状态
|
||||
// Provider 级别代理配置状态
|
||||
const proxyNodesStore = useProxyNodesStore()
|
||||
const providerProxyPopoverOpen = ref(false)
|
||||
const savingProviderProxy = ref(false)
|
||||
|
||||
// Key 级别代理配置状态
|
||||
const savingProxyKeyId = ref<string | null>(null)
|
||||
const proxyPopoverOpenKeyId = ref<string | null>(null)
|
||||
|
||||
@@ -1289,6 +1339,55 @@ async function toggleFormatConversion() {
|
||||
}
|
||||
}
|
||||
|
||||
// Provider 级别代理配置
|
||||
function handleProviderProxyPopoverToggle(open: boolean) {
|
||||
providerProxyPopoverOpen.value = open
|
||||
if (open) {
|
||||
proxyNodesStore.ensureLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
function getProviderProxyNodeName(): string {
|
||||
const nodeId = provider.value?.proxy?.node_id
|
||||
if (!nodeId) return '未知节点'
|
||||
const node = proxyNodesStore.nodes.find(n => n.id === nodeId)
|
||||
return node ? node.name : `${nodeId.slice(0, 8)}...`
|
||||
}
|
||||
|
||||
async function setProviderProxy(nodeId: string) {
|
||||
if (!provider.value) return
|
||||
savingProviderProxy.value = true
|
||||
try {
|
||||
const updated = await updateProvider(provider.value.id, {
|
||||
proxy: { node_id: nodeId, enabled: true },
|
||||
})
|
||||
provider.value = updated
|
||||
providerProxyPopoverOpen.value = false
|
||||
showSuccess('代理节点已设置')
|
||||
emit('refresh')
|
||||
} catch (err: unknown) {
|
||||
showError(parseApiError(err, '设置代理失败'))
|
||||
} finally {
|
||||
savingProviderProxy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function clearProviderProxy() {
|
||||
if (!provider.value) return
|
||||
savingProviderProxy.value = true
|
||||
try {
|
||||
const updated = await updateProvider(provider.value.id, { proxy: null })
|
||||
provider.value = updated
|
||||
providerProxyPopoverOpen.value = false
|
||||
showSuccess('已清除提供商代理')
|
||||
emit('refresh')
|
||||
} catch (err: unknown) {
|
||||
showError(parseApiError(err, '清除代理失败'))
|
||||
} finally {
|
||||
savingProviderProxy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 显示端点管理对话框
|
||||
function showAddEndpointDialog() {
|
||||
endpointDialogOpen.value = true
|
||||
@@ -2269,10 +2368,10 @@ interface AntigravityQuotaGroup {
|
||||
}
|
||||
|
||||
const ANTIGRAVITY_QUOTA_GROUPS: AntigravityQuotaGroup[] = [
|
||||
{ key: 'claude-gpt', label: 'Claude 4.5', match: m => m.includes('claude') },
|
||||
{ key: 'gemini-3-pro', label: 'Gemini 3 Pro', match: m => m.includes('gemini-3-pro') && !m.includes('image') },
|
||||
{ key: 'gemini-3-flash', label: 'Gemini 3 Flash', match: m => m.includes('gemini-3-flash') },
|
||||
{ key: 'gemini-3-pro-image', label: 'Gemini 3 Pro Image', match: m => m.includes('gemini-3-pro-image') },
|
||||
{ key: 'claude', label: 'Claude', match: m => m.includes('claude') },
|
||||
{ key: 'gemini-2.5', label: 'Gemini 2.5', match: m => m.includes('gemini-2.5') || m.includes('gemini-2-5') },
|
||||
{ key: 'gemini-3', label: 'Gemini 3', match: m => m.includes('gemini-3') && !m.includes('image') },
|
||||
{ key: 'gemini-3-image', label: 'Gemini 3 Image', match: m => m.includes('gemini-3') && m.includes('image') },
|
||||
]
|
||||
|
||||
interface AntigravityQuotaSummaryItem {
|
||||
|
||||
@@ -125,9 +125,8 @@ def merge_upstream_metadata(
|
||||
) -> dict[str, Any]:
|
||||
"""合并上游元数据,对 quota_by_model 做模型级深度合并。
|
||||
|
||||
上游 API 在配额耗尽后可能不再返回该模型的 quotaInfo,因此需要:
|
||||
1. 保留旧数据中已有的 reset_time(当新数据缺少时)
|
||||
2. 保留旧数据中存在但新数据中缺失的模型条目(标记为 100% 已用)
|
||||
以上游返回为准(全量替换),不再保留上游已下架的模型。
|
||||
仅对上游返回的模型补充旧数据中的 reset_time(当新数据缺少时)。
|
||||
"""
|
||||
merged: dict[str, Any] = dict(current) if isinstance(current, dict) else {}
|
||||
for ns_key, ns_val in incoming.items():
|
||||
@@ -152,13 +151,6 @@ def merge_upstream_metadata(
|
||||
and "reset_time" not in new_info
|
||||
):
|
||||
new_info["reset_time"] = old_info["reset_time"]
|
||||
# 保留新数据中缺失但旧数据中存在的模型(配额耗尽后上游可能不返回)
|
||||
for model_id, old_info in old_qbm.items():
|
||||
if model_id not in new_qbm and isinstance(old_info, dict):
|
||||
exhausted = dict(old_info)
|
||||
exhausted["remaining_fraction"] = 0.0
|
||||
exhausted["used_percent"] = 100.0
|
||||
new_qbm[model_id] = exhausted
|
||||
merged[ns_key] = ns_val
|
||||
return merged
|
||||
|
||||
|
||||
Reference in New Issue
Block a user