mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-05 17:22:28 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e21acd73eb | ||
|
|
702f9bc5f1 | ||
|
|
d0ce798881 |
@@ -433,11 +433,17 @@ const availableGlobalModels = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算可添加的上游模型(排除已关联的)
|
// 计算可添加的上游模型(排除已关联的,包括主模型名和映射名称)
|
||||||
const availableUpstreamModelsBase = computed(() => {
|
const availableUpstreamModelsBase = computed(() => {
|
||||||
const existingModelNames = new Set(
|
const existingModelNames = new Set<string>()
|
||||||
existingModels.value.map(m => m.provider_model_name)
|
for (const m of existingModels.value) {
|
||||||
)
|
// 主模型名
|
||||||
|
existingModelNames.add(m.provider_model_name)
|
||||||
|
// 映射名称
|
||||||
|
for (const mapping of m.provider_model_mappings ?? []) {
|
||||||
|
if (mapping.name) existingModelNames.add(mapping.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
return upstreamModels.value.filter(m => !existingModelNames.has(m.id))
|
return upstreamModels.value.filter(m => !existingModelNames.has(m.id))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -449,7 +449,17 @@ interface UpstreamModelGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const groupedAvailableUpstreamModels = computed<UpstreamModelGroup[]>(() => {
|
const groupedAvailableUpstreamModels = computed<UpstreamModelGroup[]>(() => {
|
||||||
|
// 收集当前表单已添加的名称
|
||||||
const addedNames = new Set(formData.value.aliases.map(a => a.name.trim()))
|
const addedNames = new Set(formData.value.aliases.map(a => a.name.trim()))
|
||||||
|
|
||||||
|
// 收集所有已存在的映射名称(包括主模型名和映射名称)
|
||||||
|
for (const m of props.models) {
|
||||||
|
addedNames.add(m.provider_model_name)
|
||||||
|
for (const mapping of m.provider_model_mappings ?? []) {
|
||||||
|
if (mapping.name) addedNames.add(mapping.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const availableModels = filteredUpstreamModels.value.filter(m => !addedNames.has(m.id))
|
const availableModels = filteredUpstreamModels.value.filter(m => !addedNames.has(m.id))
|
||||||
|
|
||||||
const groups = new Map<string, UpstreamModelGroup>()
|
const groups = new Map<string, UpstreamModelGroup>()
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const clearingRowAffinityKey = ref<string | null>(null)
|
|||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(20)
|
const pageSize = ref(20)
|
||||||
const currentTime = ref(Math.floor(Date.now() / 1000))
|
const currentTime = ref(Math.floor(Date.now() / 1000))
|
||||||
|
const analysisHoursSelectOpen = ref(false)
|
||||||
|
|
||||||
// ==================== 模型映射缓存 ====================
|
// ==================== 模型映射缓存 ====================
|
||||||
|
|
||||||
@@ -1056,7 +1057,7 @@ onBeforeUnmount(() => {
|
|||||||
<span class="text-xs text-muted-foreground hidden sm:inline">分析用户请求间隔,推荐合适的缓存 TTL</span>
|
<span class="text-xs text-muted-foreground hidden sm:inline">分析用户请求间隔,推荐合适的缓存 TTL</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<Select v-model="analysisHours">
|
<Select v-model="analysisHours" v-model:open="analysisHoursSelectOpen">
|
||||||
<SelectTrigger class="w-24 sm:w-28 h-8">
|
<SelectTrigger class="w-24 sm:w-28 h-8">
|
||||||
<SelectValue placeholder="时间段" />
|
<SelectValue placeholder="时间段" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
|||||||
55
src/services/cache/aware_scheduler.py
vendored
55
src/services/cache/aware_scheduler.py
vendored
@@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import random
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
|
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
|
||||||
@@ -956,7 +958,16 @@ class CacheAwareScheduler:
|
|||||||
|
|
||||||
# 获取活跃的 Key 并按 internal_priority + 负载均衡排序
|
# 获取活跃的 Key 并按 internal_priority + 负载均衡排序
|
||||||
active_keys = [key for key in endpoint.api_keys if key.is_active]
|
active_keys = [key for key in endpoint.api_keys if key.is_active]
|
||||||
keys = self._shuffle_keys_by_internal_priority(active_keys, affinity_key)
|
# 检查是否所有 Key 都是 TTL=0(轮换模式)
|
||||||
|
# 如果所有 Key 的 cache_ttl_minutes 都是 0 或 None,则使用随机排序
|
||||||
|
use_random = all(
|
||||||
|
(key.cache_ttl_minutes or 0) == 0 for key in active_keys
|
||||||
|
) if active_keys else False
|
||||||
|
if use_random and len(active_keys) > 1:
|
||||||
|
logger.debug(
|
||||||
|
f" Endpoint {endpoint.id[:8]}... 启用 Key 轮换模式 (TTL=0, {len(active_keys)} keys)"
|
||||||
|
)
|
||||||
|
keys = self._shuffle_keys_by_internal_priority(active_keys, affinity_key, use_random)
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
# Key 级别的能力检查(模型级别的能力检查已在上面完成)
|
# Key 级别的能力检查(模型级别的能力检查已在上面完成)
|
||||||
@@ -1170,6 +1181,7 @@ class CacheAwareScheduler:
|
|||||||
self,
|
self,
|
||||||
keys: List[ProviderAPIKey],
|
keys: List[ProviderAPIKey],
|
||||||
affinity_key: Optional[str] = None,
|
affinity_key: Optional[str] = None,
|
||||||
|
use_random: bool = False,
|
||||||
) -> List[ProviderAPIKey]:
|
) -> List[ProviderAPIKey]:
|
||||||
"""
|
"""
|
||||||
对 API Key 按 internal_priority 分组,同优先级内部基于 affinity_key 进行确定性打乱
|
对 API Key 按 internal_priority 分组,同优先级内部基于 affinity_key 进行确定性打乱
|
||||||
@@ -1178,10 +1190,12 @@ class CacheAwareScheduler:
|
|||||||
- 数字越小越优先使用
|
- 数字越小越优先使用
|
||||||
- 同优先级 Key 之间实现负载均衡
|
- 同优先级 Key 之间实现负载均衡
|
||||||
- 使用 affinity_key 哈希确保同一请求 Key 的请求稳定(避免破坏缓存亲和性)
|
- 使用 affinity_key 哈希确保同一请求 Key 的请求稳定(避免破坏缓存亲和性)
|
||||||
|
- 当 use_random=True 时,使用随机排序实现轮换(用于 TTL=0 的场景)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
keys: API Key 列表
|
keys: API Key 列表
|
||||||
affinity_key: 亲和性标识符(通常为 API Key ID,用于确定性打乱)
|
affinity_key: 亲和性标识符(通常为 API Key ID,用于确定性打乱)
|
||||||
|
use_random: 是否使用随机排序(TTL=0 时为 True)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
排序后的 Key 列表
|
排序后的 Key 列表
|
||||||
@@ -1198,28 +1212,35 @@ class CacheAwareScheduler:
|
|||||||
priority = key.internal_priority if key.internal_priority is not None else 999999
|
priority = key.internal_priority if key.internal_priority is not None else 999999
|
||||||
priority_groups[priority].append(key)
|
priority_groups[priority].append(key)
|
||||||
|
|
||||||
# 对每个优先级组内的 Key 进行确定性打乱
|
# 对每个优先级组内的 Key 进行打乱
|
||||||
result = []
|
result = []
|
||||||
for priority in sorted(priority_groups.keys()): # 数字小的优先级高,排前面
|
for priority in sorted(priority_groups.keys()): # 数字小的优先级高,排前面
|
||||||
group_keys = priority_groups[priority]
|
group_keys = priority_groups[priority]
|
||||||
|
|
||||||
if len(group_keys) > 1 and affinity_key:
|
if len(group_keys) > 1:
|
||||||
# 改进的哈希策略:为每个 key 计算独立的哈希值
|
if use_random:
|
||||||
import hashlib
|
# TTL=0 模式:使用随机排序实现 Key 轮换
|
||||||
|
shuffled = list(group_keys)
|
||||||
|
random.shuffle(shuffled)
|
||||||
|
result.extend(shuffled)
|
||||||
|
elif affinity_key:
|
||||||
|
# 正常模式:使用哈希确定性打乱(保持缓存亲和性)
|
||||||
|
key_scores = []
|
||||||
|
for key in group_keys:
|
||||||
|
# 使用 affinity_key + key.id 的组合哈希
|
||||||
|
hash_input = f"{affinity_key}:{key.id}"
|
||||||
|
hash_value = int(hashlib.sha256(hash_input.encode()).hexdigest()[:16], 16)
|
||||||
|
key_scores.append((hash_value, key))
|
||||||
|
|
||||||
key_scores = []
|
# 按哈希值排序
|
||||||
for key in group_keys:
|
sorted_group = [key for _, key in sorted(key_scores)]
|
||||||
# 使用 affinity_key + key.id 的组合哈希
|
result.extend(sorted_group)
|
||||||
hash_input = f"{affinity_key}:{key.id}"
|
else:
|
||||||
hash_value = int(hashlib.sha256(hash_input.encode()).hexdigest()[:16], 16)
|
# 没有 affinity_key 时按 ID 排序保持稳定性
|
||||||
key_scores.append((hash_value, key))
|
result.extend(sorted(group_keys, key=lambda k: k.id))
|
||||||
|
|
||||||
# 按哈希值排序
|
|
||||||
sorted_group = [key for _, key in sorted(key_scores)]
|
|
||||||
result.extend(sorted_group)
|
|
||||||
else:
|
else:
|
||||||
# 单个 Key 或没有 affinity_key 时保持原顺序
|
# 单个 Key 直接添加
|
||||||
result.extend(sorted(group_keys, key=lambda k: k.id))
|
result.extend(group_keys)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user