mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat(proxy): 节点状态简化、连接事件记录、可靠性指标与批量删除
- 移除 UNHEALTHY 中间状态,节点状态简化为 ONLINE/OFFLINE - 新增 proxy_node_events 表记录 tunnel 连接/断开/错误事件 - 新增 failed_requests/dns_failures/stream_errors 可靠性指标(增量累加) - tunnel 重连改为固定 1s 延迟,移除指数退避逻辑 - resolver/service 改为以 TunnelManager 内存状态判断节点可用性,避免 DB 竞态 - 修正 Claude cache_control 字段格式,使用 ttl 字段控制缓存时长 - 移除前端手动勾选 capability 的 UI,改为从价格配置自动推断 - 新增全局模型批量删除 API,替换前端并行单个删除
This commit is contained in:
@@ -179,31 +179,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Key 能力配置 -->
|
||||
<section
|
||||
v-if="availableCapabilities.length > 0"
|
||||
class="space-y-2"
|
||||
>
|
||||
<h4 class="font-medium text-sm">
|
||||
模型偏好
|
||||
</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label
|
||||
v-for="cap in availableCapabilities"
|
||||
:key="cap.name"
|
||||
class="flex items-center gap-2 px-2.5 py-1 rounded-md border bg-muted/30 cursor-pointer text-sm"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="form.supported_capabilities?.includes(cap.name)"
|
||||
class="rounded"
|
||||
@change="toggleCapability(cap.name)"
|
||||
>
|
||||
<span>{{ cap.display_name }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 价格配置 -->
|
||||
<section class="space-y-3">
|
||||
<h4 class="font-medium text-sm">
|
||||
@@ -212,7 +187,7 @@
|
||||
<TieredPricingEditor
|
||||
ref="tieredPricingEditorRef"
|
||||
v-model="tieredPricing"
|
||||
:show-cache1h="form.supported_capabilities?.includes('cache_1h')"
|
||||
:show-cache1h="true"
|
||||
/>
|
||||
<div class="flex items-center gap-3 pt-2 border-t">
|
||||
<Label class="text-xs whitespace-nowrap">按次计费</Label>
|
||||
@@ -353,7 +328,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import {
|
||||
Loader2, Layers, SquarePen,
|
||||
Search, ChevronRight, Plus, Trash2
|
||||
@@ -378,7 +353,6 @@ import {
|
||||
type GlobalModelUpdate,
|
||||
} from '@/api/global-models'
|
||||
import type { TieredPricingConfig } from '@/api/endpoints/types'
|
||||
import { getAllCapabilities, type CapabilityDefinition } from '@/api/endpoints'
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
@@ -665,34 +639,6 @@ function fillVideoResolutionPricePreset(preset: 'common' | 'sora' | 'veo') {
|
||||
}))
|
||||
}
|
||||
|
||||
// Key 能力选项
|
||||
const availableCapabilities = ref<CapabilityDefinition[]>([])
|
||||
|
||||
// 加载可用能力列表
|
||||
async function loadCapabilities() {
|
||||
try {
|
||||
availableCapabilities.value = await getAllCapabilities()
|
||||
} catch (err) {
|
||||
log.error('Failed to load capabilities:', err)
|
||||
}
|
||||
}
|
||||
|
||||
// 切换能力
|
||||
function toggleCapability(capName: string) {
|
||||
if (!form.value.supported_capabilities) {
|
||||
form.value.supported_capabilities = []
|
||||
}
|
||||
const index = form.value.supported_capabilities.indexOf(capName)
|
||||
if (index >= 0) {
|
||||
form.value.supported_capabilities.splice(index, 1)
|
||||
} else {
|
||||
form.value.supported_capabilities.push(capName)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCapabilities()
|
||||
})
|
||||
|
||||
// 加载模型列表
|
||||
async function loadModels() {
|
||||
@@ -829,6 +775,19 @@ async function handleSubmit() {
|
||||
// Apply billing (video) pricing into config before cleaning/submitting.
|
||||
applyVideoPricingToConfig()
|
||||
|
||||
// Auto-infer supported_capabilities from tiered pricing config
|
||||
const caps = new Set(form.value.supported_capabilities || [])
|
||||
const has1hPricing = finalTieredPricing?.tiers?.some(
|
||||
(t: Record<string, unknown>) => Array.isArray(t.cache_ttl_pricing)
|
||||
&& (t.cache_ttl_pricing as Array<Record<string, unknown>>).some(c => c.ttl_minutes === 60)
|
||||
)
|
||||
if (has1hPricing) {
|
||||
caps.add('cache_1h')
|
||||
} else {
|
||||
caps.delete('cache_1h')
|
||||
}
|
||||
form.value.supported_capabilities = caps.size > 0 ? [...caps] : []
|
||||
|
||||
// 清理空的 config
|
||||
const cleanConfig = form.value.config && Object.keys(form.value.config).length > 0
|
||||
? form.value.config
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
v-if="getFirst1hCachePrice(model.default_tiered_pricing) !== '-'"
|
||||
class="flex items-center gap-3 p-3 rounded-lg border bg-muted/20"
|
||||
>
|
||||
<Label class="text-xs text-muted-foreground whitespace-nowrap">1h 缓存创建</Label>
|
||||
<Label class="text-xs text-muted-foreground whitespace-nowrap">1h 缓存</Label>
|
||||
<span class="text-sm font-mono">{{ getFirst1hCachePrice(model.default_tiered_pricing) }}</span>
|
||||
</div>
|
||||
<!-- 按次计费 -->
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
v-if="showCache1h"
|
||||
class="space-y-1"
|
||||
>
|
||||
<Label class="text-xs text-muted-foreground">1h 缓存创建</Label>
|
||||
<Label class="text-xs text-muted-foreground">1h 缓存</Label>
|
||||
<Input
|
||||
:model-value="getCache1hDisplay(index)"
|
||||
type="number"
|
||||
@@ -209,26 +209,6 @@ watch(
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 监听 showCache1h 变化
|
||||
watch(
|
||||
() => props.showCache1h,
|
||||
(newValue, oldValue) => {
|
||||
if (oldValue === true && newValue === false) {
|
||||
// 取消勾选时,清除本地的 1h 缓存数据和手动设置标记
|
||||
localTiers.value.forEach((tier, i) => {
|
||||
tier.cache_ttl_pricing = undefined
|
||||
if (cacheManuallySet[i]) {
|
||||
cacheManuallySet[i].cache1h = false
|
||||
}
|
||||
})
|
||||
syncToParent()
|
||||
} else if (oldValue === false && newValue === true) {
|
||||
// 勾选时,同步自动计算的价格到父组件
|
||||
syncToParent()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 验证错误
|
||||
const validationError = computed(() => {
|
||||
if (localTiers.value.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user