mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
feat(routing): make routing profiles the sole scheduler policy source
Bootstrap an enabled system-default routing group from the legacy scheduler config keys on startup, resolve the default ordering config from that group before falling back to the legacy keys, and stop merging keep_priority_on_conversion with the legacy flag when a policy is resolved. Thread the policy-derived ordering config into candidate preselection so it no longer reads system config independently. Add per-API-format key priority overrides so a key serving several formats keeps independent ordering, matching the legacy global_priority_by_format semantics. Expose keep_priority_on_conversion in the routing profile editor and read the effective policy in the model routing preview, monitoring metrics and provider page badge.
This commit is contained in:
@@ -272,6 +272,8 @@ import {
|
||||
type ProviderWithEndpointsSummary,
|
||||
} from '@/api/endpoints'
|
||||
import { adminApi } from '@/api/admin'
|
||||
import { listRoutingGroups } from '@/api/routing-profiles'
|
||||
import { normalizeRoutingGroupConfig } from '@/features/routing/utils/routingPolicy'
|
||||
import { parseApiError } from '@/utils/errorParser'
|
||||
import { useI18n } from '@/i18n'
|
||||
|
||||
@@ -533,8 +535,18 @@ const maxProviderPriority = computed(() => {
|
||||
return priorities.length > 0 ? Math.max(...priorities) : undefined
|
||||
})
|
||||
|
||||
// 加载优先级模式
|
||||
// 加载优先级模式:优先使用启用中的系统默认调度策略,旧的系统配置键仅作兜底
|
||||
async function loadPriorityMode(options: { cacheTtlMs?: number } = {}) {
|
||||
try {
|
||||
const groups = await listRoutingGroups()
|
||||
const systemDefault = groups.items.find(group => group.is_system_default && group.enabled)
|
||||
if (systemDefault) {
|
||||
priorityMode.value = normalizeRoutingGroupConfig(systemDefault.config_json).default_policy.priority_mode
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
// 路由策略不可用时继续尝试旧配置
|
||||
}
|
||||
try {
|
||||
const response = await adminApi.getSystemConfig('provider_priority_mode', {
|
||||
cacheTtlMs: options.cacheTtlMs ?? 0,
|
||||
|
||||
@@ -454,6 +454,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label
|
||||
class="flex items-start gap-3 rounded-lg border border-border/60 px-3 py-2 text-sm"
|
||||
data-testid="keep-priority-on-conversion"
|
||||
>
|
||||
<Switch
|
||||
:model-value="keepPriorityOnConversion"
|
||||
:disabled="saving"
|
||||
aria-label="格式转换时保持优先级"
|
||||
@update:model-value="updateKeepPriorityOnConversion"
|
||||
/>
|
||||
<span class="min-w-0">
|
||||
<span class="block font-medium">格式转换时保持优先级</span>
|
||||
<span class="mt-0.5 block text-xs text-muted-foreground">
|
||||
开启后,需要跨 API 格式转换的候选不会被降级到同格式候选之后。作用于本策略范围内的全部模型;Provider 自身的同名开关仍单独生效。
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<RoutingPriorityPolicyEditor
|
||||
@@ -749,6 +766,7 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Input,
|
||||
Switch,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCard,
|
||||
@@ -874,6 +892,9 @@ const firstStepSchedulingMode = computed<RoutingSchedulingMode>(() => {
|
||||
}
|
||||
return draft.value?.config_json.default_policy.scheduling_mode ?? 'cache_affinity'
|
||||
})
|
||||
const keepPriorityOnConversion = computed<boolean>(() => (
|
||||
draft.value?.config_json.default_policy.keep_priority_on_conversion ?? false
|
||||
))
|
||||
const allowedModelsLookLikeLegacyMirror = computed(() => {
|
||||
return draft.value
|
||||
? allowedModelsMirrorPerModelPolicies(draft.value.config_json)
|
||||
@@ -1190,6 +1211,17 @@ function updateFirstStepSchedulingMode(mode: RoutingSchedulingMode): void {
|
||||
})
|
||||
}
|
||||
|
||||
function updateKeepPriorityOnConversion(value: boolean): void {
|
||||
if (!draft.value) return
|
||||
updateDraftConfig({
|
||||
...draft.value.config_json,
|
||||
default_policy: {
|
||||
...draft.value.config_json.default_policy,
|
||||
keep_priority_on_conversion: value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function removePerModelPolicy(model: string): void {
|
||||
if (!draft.value) return
|
||||
if (perModelEditingActive.value && editingDirty.value) {
|
||||
|
||||
@@ -131,11 +131,31 @@ vi.mock('@/components/ui', async () => {
|
||||
},
|
||||
})
|
||||
|
||||
const Switch = defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: { type: Boolean, default: false },
|
||||
disabled: Boolean,
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { attrs, emit }) {
|
||||
return () => h('button', {
|
||||
...attrs,
|
||||
type: 'button',
|
||||
role: 'switch',
|
||||
'aria-checked': props.modelValue,
|
||||
disabled: props.disabled,
|
||||
onClick: () => emit('update:modelValue', !props.modelValue),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
Badge: wrapper(),
|
||||
Button,
|
||||
Card: wrapper('section'),
|
||||
Input,
|
||||
Switch,
|
||||
Table: wrapper('table'),
|
||||
TableBody: wrapper('tbody'),
|
||||
TableCard: wrapper(),
|
||||
|
||||
Reference in New Issue
Block a user