Spaces:
Sleeping
Sleeping
| import { CONFIG } from '../core/config-manager.js'; | |
| import { serviceInstances, getServiceAdapter } from '../providers/adapter.js'; | |
| import { formatKiroUsage, formatGeminiUsage, formatAntigravityUsage } from '../services/usage-service.js'; | |
| import { readUsageCache, writeUsageCache, readProviderUsageCache, updateProviderUsageCache } from './usage-cache.js'; | |
| import path from 'path'; | |
| /** | |
| * 获取所有支持用量查询的提供商的用量信息 | |
| * @param {Object} currentConfig - 当前配置 | |
| * @param {Object} providerPoolManager - 提供商池管理器 | |
| * @returns {Promise<Object>} 所有提供商的用量信息 | |
| */ | |
| async function getAllProvidersUsage(currentConfig, providerPoolManager) { | |
| const results = { | |
| timestamp: new Date().toISOString(), | |
| providers: {} | |
| }; | |
| // 支持用量查询的提供商列表 | |
| const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity']; | |
| // 并发获取所有提供商的用量数据 | |
| const usagePromises = supportedProviders.map(async (providerType) => { | |
| try { | |
| const providerUsage = await getProviderTypeUsage(providerType, currentConfig, providerPoolManager); | |
| return { providerType, data: providerUsage, success: true }; | |
| } catch (error) { | |
| return { | |
| providerType, | |
| data: { | |
| error: error.message, | |
| instances: [] | |
| }, | |
| success: false | |
| }; | |
| } | |
| }); | |
| // 等待所有并发请求完成 | |
| const usageResults = await Promise.all(usagePromises); | |
| // 将结果整合到 results.providers 中 | |
| for (const result of usageResults) { | |
| results.providers[result.providerType] = result.data; | |
| } | |
| return results; | |
| } | |
| /** | |
| * 获取指定提供商类型的用量信息 | |
| * @param {string} providerType - 提供商类型 | |
| * @param {Object} currentConfig - 当前配置 | |
| * @param {Object} providerPoolManager - 提供商池管理器 | |
| * @returns {Promise<Object>} 提供商用量信息 | |
| */ | |
| async function getProviderTypeUsage(providerType, currentConfig, providerPoolManager) { | |
| const result = { | |
| providerType, | |
| instances: [], | |
| totalCount: 0, | |
| successCount: 0, | |
| errorCount: 0 | |
| }; | |
| // 获取提供商池中的所有实例 | |
| let providers = []; | |
| if (providerPoolManager && providerPoolManager.providerPools && providerPoolManager.providerPools[providerType]) { | |
| providers = providerPoolManager.providerPools[providerType]; | |
| } else if (currentConfig.providerPools && currentConfig.providerPools[providerType]) { | |
| providers = currentConfig.providerPools[providerType]; | |
| } | |
| result.totalCount = providers.length; | |
| // 遍历所有提供商实例获取用量 | |
| for (const provider of providers) { | |
| const providerKey = providerType + (provider.uuid || ''); | |
| let adapter = serviceInstances[providerKey]; | |
| const instanceResult = { | |
| uuid: provider.uuid || 'unknown', | |
| name: getProviderDisplayName(provider, providerType), | |
| isHealthy: provider.isHealthy !== false, | |
| isDisabled: provider.isDisabled === true, | |
| success: false, | |
| usage: null, | |
| error: null | |
| }; | |
| // First check if disabled, skip initialization for disabled providers | |
| if (provider.isDisabled) { | |
| instanceResult.error = 'Provider is disabled'; | |
| result.errorCount++; | |
| } else if (!adapter) { | |
| // Service instance not initialized, try auto-initialization | |
| try { | |
| console.log(`[Usage API] Auto-initializing service adapter for ${providerType}: ${provider.uuid}`); | |
| // Build configuration object | |
| const serviceConfig = { | |
| ...CONFIG, | |
| ...provider, | |
| MODEL_PROVIDER: providerType | |
| }; | |
| adapter = getServiceAdapter(serviceConfig); | |
| } catch (initError) { | |
| console.error(`[Usage API] Failed to initialize adapter for ${providerType}: ${provider.uuid}:`, initError.message); | |
| instanceResult.error = `Service instance initialization failed: ${initError.message}`; | |
| result.errorCount++; | |
| } | |
| } | |
| // If adapter exists (including just initialized), and no error, try to get usage | |
| if (adapter && !instanceResult.error) { | |
| try { | |
| const usage = await getAdapterUsage(adapter, providerType); | |
| instanceResult.success = true; | |
| instanceResult.usage = usage; | |
| result.successCount++; | |
| } catch (error) { | |
| instanceResult.error = error.message; | |
| result.errorCount++; | |
| } | |
| } | |
| result.instances.push(instanceResult); | |
| } | |
| return result; | |
| } | |
| /** | |
| * 从适配器获取用量信息 | |
| * @param {Object} adapter - 服务适配器 | |
| * @param {string} providerType - 提供商类型 | |
| * @returns {Promise<Object>} 用量信息 | |
| */ | |
| async function getAdapterUsage(adapter, providerType) { | |
| if (providerType === 'claude-kiro-oauth') { | |
| if (typeof adapter.getUsageLimits === 'function') { | |
| const rawUsage = await adapter.getUsageLimits(); | |
| return formatKiroUsage(rawUsage); | |
| } else if (adapter.kiroApiService && typeof adapter.kiroApiService.getUsageLimits === 'function') { | |
| const rawUsage = await adapter.kiroApiService.getUsageLimits(); | |
| return formatKiroUsage(rawUsage); | |
| } | |
| throw new Error('This adapter does not support usage query'); | |
| } | |
| if (providerType === 'gemini-cli-oauth') { | |
| if (typeof adapter.getUsageLimits === 'function') { | |
| const rawUsage = await adapter.getUsageLimits(); | |
| return formatGeminiUsage(rawUsage); | |
| } else if (adapter.geminiApiService && typeof adapter.geminiApiService.getUsageLimits === 'function') { | |
| const rawUsage = await adapter.geminiApiService.getUsageLimits(); | |
| return formatGeminiUsage(rawUsage); | |
| } | |
| throw new Error('This adapter does not support usage query'); | |
| } | |
| if (providerType === 'gemini-antigravity') { | |
| if (typeof adapter.getUsageLimits === 'function') { | |
| const rawUsage = await adapter.getUsageLimits(); | |
| return formatAntigravityUsage(rawUsage); | |
| } else if (adapter.antigravityApiService && typeof adapter.antigravityApiService.getUsageLimits === 'function') { | |
| const rawUsage = await adapter.antigravityApiService.getUsageLimits(); | |
| return formatAntigravityUsage(rawUsage); | |
| } | |
| throw new Error('This adapter does not support usage query'); | |
| } | |
| throw new Error(`Unsupported provider type: ${providerType}`); | |
| } | |
| /** | |
| * 获取提供商显示名称 | |
| * @param {Object} provider - 提供商配置 | |
| * @param {string} providerType - 提供商类型 | |
| * @returns {string} 显示名称 | |
| */ | |
| function getProviderDisplayName(provider, providerType) { | |
| // 优先使用自定义名称 | |
| if (provider.customName) { | |
| return provider.customName; | |
| } | |
| if (provider.uuid) { | |
| return provider.uuid; | |
| } | |
| // 尝试从凭据文件路径提取名称 | |
| const credPathKey = { | |
| 'claude-kiro-oauth': 'KIRO_OAUTH_CREDS_FILE_PATH', | |
| 'gemini-cli-oauth': 'GEMINI_OAUTH_CREDS_FILE_PATH', | |
| 'gemini-antigravity': 'ANTIGRAVITY_OAUTH_CREDS_FILE_PATH', | |
| 'openai-qwen-oauth': 'QWEN_OAUTH_CREDS_FILE_PATH', | |
| 'openai-iflow': 'IFLOW_TOKEN_FILE_PATH' | |
| }[providerType]; | |
| if (credPathKey && provider[credPathKey]) { | |
| const filePath = provider[credPathKey]; | |
| const fileName = path.basename(filePath); | |
| const dirName = path.basename(path.dirname(filePath)); | |
| return `${dirName}/${fileName}`; | |
| } | |
| return 'Unnamed'; | |
| } | |
| /** | |
| * 获取所有提供商的用量限制 | |
| */ | |
| export async function handleGetUsage(req, res, currentConfig, providerPoolManager) { | |
| try { | |
| // 解析查询参数,检查是否需要强制刷新 | |
| const url = new URL(req.url, `http://${req.headers.host}`); | |
| const refresh = url.searchParams.get('refresh') === 'true'; | |
| let usageResults; | |
| if (!refresh) { | |
| // 优先读取缓存 | |
| const cachedData = await readUsageCache(); | |
| if (cachedData) { | |
| console.log('[Usage API] Returning cached usage data'); | |
| usageResults = { ...cachedData, fromCache: true }; | |
| } | |
| } | |
| if (!usageResults) { | |
| // 缓存不存在或需要刷新,重新查询 | |
| console.log('[Usage API] Fetching fresh usage data'); | |
| usageResults = await getAllProvidersUsage(currentConfig, providerPoolManager); | |
| // 写入缓存 | |
| await writeUsageCache(usageResults); | |
| } | |
| res.writeHead(200, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify(usageResults)); | |
| return true; | |
| } catch (error) { | |
| console.error('[UI API] Failed to get usage:', error); | |
| res.writeHead(500, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ | |
| error: { | |
| message: 'Failed to get usage info: ' + error.message | |
| } | |
| })); | |
| return true; | |
| } | |
| } | |
| /** | |
| * 获取特定提供商类型的用量限制 | |
| */ | |
| export async function handleGetProviderUsage(req, res, currentConfig, providerPoolManager, providerType) { | |
| try { | |
| // 解析查询参数,检查是否需要强制刷新 | |
| const url = new URL(req.url, `http://${req.headers.host}`); | |
| const refresh = url.searchParams.get('refresh') === 'true'; | |
| let usageResults; | |
| if (!refresh) { | |
| // Prefer reading from cache | |
| const cachedData = await readProviderUsageCache(providerType); | |
| if (cachedData) { | |
| console.log(`[Usage API] Returning cached usage data for ${providerType}`); | |
| usageResults = cachedData; | |
| } | |
| } | |
| if (!usageResults) { | |
| // Cache does not exist or refresh required, re-query | |
| console.log(`[Usage API] Fetching fresh usage data for ${providerType}`); | |
| usageResults = await getProviderTypeUsage(providerType, currentConfig, providerPoolManager); | |
| // 更新缓存 | |
| await updateProviderUsageCache(providerType, usageResults); | |
| } | |
| res.writeHead(200, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify(usageResults)); | |
| return true; | |
| } catch (error) { | |
| console.error(`[UI API] Failed to get usage for ${providerType}:`, error); | |
| res.writeHead(500, { 'Content-Type': 'application/json' }); | |
| res.end(JSON.stringify({ | |
| error: { | |
| message: `Failed to get usage info for ${providerType}: ` + error.message | |
| } | |
| })); | |
| return true; | |
| } | |
| } |