mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 18:59:50 +08:00
feat(keys): 新增密钥自动获取模型功能
- 添加 auto_fetch_models 字段支持定时从上游 API 获取可用模型 - 添加 locked_models 字段支持锁定模型,刷新时不会被删除 - 新增 ModelFetchScheduler 调度器定期执行模型获取任务 - 前端 KeyFormDialog 添加自动获取模型开关 - 前端 KeyAllowedModelsEditDialog 支持模型锁定操作 - ProviderDetailDrawer 显示密钥同步状态 - 数据库迁移添加新字段
This commit is contained in:
@@ -17,6 +17,7 @@ interface CacheEntry {
|
||||
type FetchResult = { models: UpstreamModel[]; error?: string }
|
||||
|
||||
// 全局缓存(模块级别,所有组件共享)
|
||||
// 支持两种 key: providerId 或 providerId:apiKeyId
|
||||
const cache = new Map<string, CacheEntry>()
|
||||
const CACHE_TTL = 5 * 60 * 1000 // 5分钟
|
||||
|
||||
@@ -26,39 +27,50 @@ const pendingRequests = new Map<string, Promise<FetchResult>>()
|
||||
// 请求状态
|
||||
const loadingMap = ref<Map<string, boolean>>(new Map())
|
||||
|
||||
/**
|
||||
* 生成缓存 key
|
||||
*/
|
||||
function getCacheKey(providerId: string, apiKeyId?: string): string {
|
||||
return apiKeyId ? `${providerId}:${apiKeyId}` : providerId
|
||||
}
|
||||
|
||||
export function useUpstreamModelsCache() {
|
||||
/**
|
||||
* 获取上游模型列表
|
||||
* @param providerId 提供商ID
|
||||
* @param apiKeyId 可选的 API Key ID(用于获取特定 Key 支持的模型)
|
||||
* @param forceRefresh 是否强制刷新
|
||||
* @returns 模型列表或 null(如果请求失败)
|
||||
*/
|
||||
async function fetchModels(
|
||||
providerId: string,
|
||||
apiKeyId?: string,
|
||||
forceRefresh = false
|
||||
): Promise<FetchResult> {
|
||||
const cacheKey = getCacheKey(providerId, apiKeyId)
|
||||
|
||||
// 检查缓存
|
||||
if (!forceRefresh) {
|
||||
const cached = cache.get(providerId)
|
||||
const cached = cache.get(cacheKey)
|
||||
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||
return { models: cached.models }
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有进行中的请求(非强制刷新时复用)
|
||||
if (!forceRefresh && pendingRequests.has(providerId)) {
|
||||
return pendingRequests.get(providerId)!
|
||||
if (!forceRefresh && pendingRequests.has(cacheKey)) {
|
||||
return pendingRequests.get(cacheKey)!
|
||||
}
|
||||
|
||||
// 创建新请求
|
||||
const requestPromise = (async (): Promise<FetchResult> => {
|
||||
try {
|
||||
loadingMap.value.set(providerId, true)
|
||||
const response = await adminApi.queryProviderModels(providerId)
|
||||
loadingMap.value.set(cacheKey, true)
|
||||
const response = await adminApi.queryProviderModels(providerId, apiKeyId)
|
||||
|
||||
if (response.success && response.data?.models) {
|
||||
// 存入缓存
|
||||
cache.set(providerId, {
|
||||
cache.set(cacheKey, {
|
||||
models: response.data.models,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
@@ -73,20 +85,21 @@ export function useUpstreamModelsCache() {
|
||||
const rawError = err.response?.data?.detail || err.message || '获取上游模型失败'
|
||||
return { models: [], error: parseUpstreamModelError(rawError) }
|
||||
} finally {
|
||||
loadingMap.value.set(providerId, false)
|
||||
pendingRequests.delete(providerId)
|
||||
loadingMap.value.set(cacheKey, false)
|
||||
pendingRequests.delete(cacheKey)
|
||||
}
|
||||
})()
|
||||
|
||||
pendingRequests.set(providerId, requestPromise)
|
||||
pendingRequests.set(cacheKey, requestPromise)
|
||||
return requestPromise
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存的模型(不发起请求)
|
||||
*/
|
||||
function getCachedModels(providerId: string): UpstreamModel[] | null {
|
||||
const cached = cache.get(providerId)
|
||||
function getCachedModels(providerId: string, apiKeyId?: string): UpstreamModel[] | null {
|
||||
const cacheKey = getCacheKey(providerId, apiKeyId)
|
||||
const cached = cache.get(cacheKey)
|
||||
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||
return cached.models
|
||||
}
|
||||
@@ -94,17 +107,19 @@ export function useUpstreamModelsCache() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定提供商的缓存
|
||||
* 清除指定提供商/Key的缓存
|
||||
*/
|
||||
function clearCache(providerId: string) {
|
||||
cache.delete(providerId)
|
||||
function clearCache(providerId: string, apiKeyId?: string) {
|
||||
const cacheKey = getCacheKey(providerId, apiKeyId)
|
||||
cache.delete(cacheKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否正在加载
|
||||
*/
|
||||
function isLoading(providerId: string): boolean {
|
||||
return loadingMap.value.get(providerId) || false
|
||||
function isLoading(providerId: string, apiKeyId?: string): boolean {
|
||||
const cacheKey = getCacheKey(providerId, apiKeyId)
|
||||
return loadingMap.value.get(cacheKey) || false
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user