mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
Merge pull request #222 from AAEE86/fix/provider-mapping
fix(provider-mapping): 修复详情页映射延迟刷新并补齐 mapping-preview 缓存治理
This commit is contained in:
@@ -1136,6 +1136,7 @@ const providerModels = ref<Model[]>([]) // Provider 级别的 models
|
|||||||
const providerMappingPreview = ref<ProviderMappingPreviewResponse | null>(null) // 映射预览
|
const providerMappingPreview = ref<ProviderMappingPreviewResponse | null>(null) // 映射预览
|
||||||
let providerLoadRequestId = 0
|
let providerLoadRequestId = 0
|
||||||
let endpointsLoadRequestId = 0
|
let endpointsLoadRequestId = 0
|
||||||
|
let mappingPreviewLoadRequestId = 0
|
||||||
|
|
||||||
// 系统级格式转换配置
|
// 系统级格式转换配置
|
||||||
const systemFormatConversionEnabled = ref(false)
|
const systemFormatConversionEnabled = ref(false)
|
||||||
@@ -1349,6 +1350,7 @@ watch(
|
|||||||
// 使在途请求失效,避免关闭后旧响应回写
|
// 使在途请求失效,避免关闭后旧响应回写
|
||||||
providerLoadRequestId += 1
|
providerLoadRequestId += 1
|
||||||
endpointsLoadRequestId += 1
|
endpointsLoadRequestId += 1
|
||||||
|
mappingPreviewLoadRequestId += 1
|
||||||
|
|
||||||
// 停止倒计时定时器
|
// 停止倒计时定时器
|
||||||
stopCountdownTimer()
|
stopCountdownTimer()
|
||||||
@@ -1926,7 +1928,7 @@ async function openAntigravityQuotaDialog(key: EndpointAPIKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleKeyChanged() {
|
async function handleKeyChanged() {
|
||||||
await loadEndpoints()
|
await Promise.all([loadEndpoints(), loadMappingPreview()])
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
// 添加/修改 key 后自动获取 Antigravity 配额(新 key 的 upstream_metadata 为空)
|
// 添加/修改 key 后自动获取 Antigravity 配额(新 key 的 upstream_metadata 为空)
|
||||||
void autoRefreshQuotaInBackground()
|
void autoRefreshQuotaInBackground()
|
||||||
@@ -2015,21 +2017,20 @@ function handleBatchAssign() {
|
|||||||
|
|
||||||
// 处理批量关联完成
|
// 处理批量关联完成
|
||||||
async function handleBatchAssignChanged() {
|
async function handleBatchAssignChanged() {
|
||||||
await loadEndpoints()
|
await Promise.all([loadEndpoints(), loadMappingPreview()])
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理模型映射变更
|
// 处理模型映射变更
|
||||||
async function handleModelMappingChanged() {
|
async function handleModelMappingChanged() {
|
||||||
void loadMappingPreview()
|
await Promise.all([loadEndpoints(), loadMappingPreview()])
|
||||||
await loadEndpoints()
|
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理模型保存完成
|
// 处理模型保存完成
|
||||||
async function handleModelSaved() {
|
async function handleModelSaved() {
|
||||||
editingModel.value = null
|
editingModel.value = null
|
||||||
await loadEndpoints()
|
await Promise.all([loadEndpoints(), loadMappingPreview()])
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2719,9 +2720,13 @@ async function loadEndpoints() {
|
|||||||
// 加载映射预览(独立于 loadEndpoints,不阻塞首屏渲染)
|
// 加载映射预览(独立于 loadEndpoints,不阻塞首屏渲染)
|
||||||
async function loadMappingPreview() {
|
async function loadMappingPreview() {
|
||||||
if (!props.providerId) return
|
if (!props.providerId) return
|
||||||
|
const requestId = ++mappingPreviewLoadRequestId
|
||||||
try {
|
try {
|
||||||
providerMappingPreview.value = await getProviderMappingPreview(props.providerId)
|
const preview = await getProviderMappingPreview(props.providerId)
|
||||||
|
if (requestId !== mappingPreviewLoadRequestId) return
|
||||||
|
providerMappingPreview.value = preview
|
||||||
} catch {
|
} catch {
|
||||||
|
if (requestId !== mappingPreviewLoadRequestId) return
|
||||||
providerMappingPreview.value = null
|
providerMappingPreview.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,6 +427,12 @@ class AdminCreateGlobalModelAdapter(AdminApiAdapter):
|
|||||||
|
|
||||||
logger.info(f"GlobalModel 已创建: id={global_model.id} name={global_model.name}")
|
logger.info(f"GlobalModel 已创建: id={global_model.id} name={global_model.name}")
|
||||||
|
|
||||||
|
# 创建成功后失效缓存(避免 mapping-preview 在 TTL 内读到旧结果)
|
||||||
|
from src.services.cache.invalidation import get_cache_invalidation_service
|
||||||
|
|
||||||
|
cache_service = get_cache_invalidation_service()
|
||||||
|
await cache_service.on_global_model_changed(global_model.name, str(global_model.id))
|
||||||
|
|
||||||
return GlobalModelResponse.model_validate(global_model)
|
return GlobalModelResponse.model_validate(global_model)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1558,6 +1558,12 @@ _CACHE_CATEGORIES: list[tuple[str, str, str, str]] = [
|
|||||||
"model:provider_global:*",
|
"model:provider_global:*",
|
||||||
"Provider-GlobalModel 模型映射缓存",
|
"Provider-GlobalModel 模型映射缓存",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"provider_mapping_preview",
|
||||||
|
"映射预览",
|
||||||
|
"admin:providers:mapping-preview:*",
|
||||||
|
"Provider 详情页 mapping-preview 缓存",
|
||||||
|
),
|
||||||
("global_model", "全局模型", "global_model:*", "GlobalModel 缓存(ID/名称/解析)"),
|
("global_model", "全局模型", "global_model:*", "GlobalModel 缓存(ID/名称/解析)"),
|
||||||
("models_list", "模型列表", "models:list:*", "/v1/models 端点模型列表缓存"),
|
("models_list", "模型列表", "models:list:*", "/v1/models 端点模型列表缓存"),
|
||||||
("user", "用户", "user:*", "用户信息缓存(ID/Email)"),
|
("user", "用户", "user:*", "用户信息缓存(ID/Email)"),
|
||||||
|
|||||||
42
src/services/cache/invalidation.py
vendored
42
src/services/cache/invalidation.py
vendored
@@ -6,10 +6,15 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from src.core.logger import logger
|
from src.core.logger import logger
|
||||||
|
|
||||||
|
_PROVIDER_MAPPING_PREVIEW_CACHE_KEY_PREFIX = "admin:providers:mapping-preview:global"
|
||||||
|
_PROVIDER_MAPPING_PREVIEW_CACHE_PATTERN = f"{_PROVIDER_MAPPING_PREVIEW_CACHE_KEY_PREFIX}:v:*"
|
||||||
|
|
||||||
|
|
||||||
class CacheInvalidationService:
|
class CacheInvalidationService:
|
||||||
"""缓存失效服务"""
|
"""缓存失效服务"""
|
||||||
@@ -61,6 +66,12 @@ class CacheInvalidationService:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[CacheInvalidation] 失效 models list 缓存失败: {e}")
|
logger.error(f"[CacheInvalidation] 失效 models list 缓存失败: {e}")
|
||||||
|
|
||||||
|
# 5. 清除 Provider 映射预览缓存(GlobalModel 映射规则变化会影响所有 Provider)
|
||||||
|
try:
|
||||||
|
await self._invalidate_all_provider_mapping_preview_cache()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[CacheInvalidation] 失效 Provider 映射预览缓存失败: {e}")
|
||||||
|
|
||||||
def on_model_changed(self, provider_id: str, global_model_id: str) -> Any:
|
def on_model_changed(self, provider_id: str, global_model_id: str) -> Any:
|
||||||
"""Model 变更时的缓存失效"""
|
"""Model 变更时的缓存失效"""
|
||||||
self._refresh_provider_cache(provider_id)
|
self._refresh_provider_cache(provider_id)
|
||||||
@@ -78,6 +89,12 @@ class CacheInvalidationService:
|
|||||||
logger.info(f"[CacheInvalidation] Key allowed_models 变更: provider_id={provider_id}")
|
logger.info(f"[CacheInvalidation] Key allowed_models 变更: provider_id={provider_id}")
|
||||||
self._refresh_provider_cache(provider_id)
|
self._refresh_provider_cache(provider_id)
|
||||||
|
|
||||||
|
# 清除该 Provider 的映射预览缓存(详情页模型映射依赖)
|
||||||
|
try:
|
||||||
|
await self._invalidate_provider_mapping_preview_cache(provider_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[CacheInvalidation] 失效 Provider 映射预览缓存失败: {e}")
|
||||||
|
|
||||||
# 清除 /v1/models 列表缓存(allowed_models 变更会影响模型可用性)
|
# 清除 /v1/models 列表缓存(allowed_models 变更会影响模型可用性)
|
||||||
from src.services.cache.model_list_cache import invalidate_models_list_cache
|
from src.services.cache.model_list_cache import invalidate_models_list_cache
|
||||||
|
|
||||||
@@ -92,6 +109,31 @@ class CacheInvalidationService:
|
|||||||
|
|
||||||
ModelMapperMiddleware.refresh_cache(provider_id)
|
ModelMapperMiddleware.refresh_cache(provider_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_provider_mapping_preview_cache_key(provider_id: str) -> str:
|
||||||
|
"""构建指定 Provider 的 mapping-preview 缓存键。"""
|
||||||
|
raw = json.dumps(
|
||||||
|
{"provider_id": provider_id},
|
||||||
|
sort_keys=True,
|
||||||
|
ensure_ascii=False,
|
||||||
|
default=str,
|
||||||
|
)
|
||||||
|
vary_hash = hashlib.sha1(raw.encode("utf-8")).hexdigest()[:16]
|
||||||
|
return f"{_PROVIDER_MAPPING_PREVIEW_CACHE_KEY_PREFIX}:v:{vary_hash}"
|
||||||
|
|
||||||
|
async def _invalidate_provider_mapping_preview_cache(self, provider_id: str) -> None:
|
||||||
|
"""失效指定 Provider 的 mapping-preview 缓存。"""
|
||||||
|
from src.core.cache_service import CacheService
|
||||||
|
|
||||||
|
cache_key = self._build_provider_mapping_preview_cache_key(provider_id)
|
||||||
|
await CacheService.delete(cache_key)
|
||||||
|
|
||||||
|
async def _invalidate_all_provider_mapping_preview_cache(self) -> None:
|
||||||
|
"""失效所有 Provider 的 mapping-preview 缓存。"""
|
||||||
|
from src.core.cache_service import CacheService
|
||||||
|
|
||||||
|
await CacheService.delete_pattern(_PROVIDER_MAPPING_PREVIEW_CACHE_PATTERN)
|
||||||
|
|
||||||
def clear_all_caches(self) -> None:
|
def clear_all_caches(self) -> None:
|
||||||
"""清空所有缓存"""
|
"""清空所有缓存"""
|
||||||
from src.services.model.mapper import ModelMapperMiddleware
|
from src.services.model.mapper import ModelMapperMiddleware
|
||||||
|
|||||||
Reference in New Issue
Block a user