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} 所有提供商的用量信息 */ 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} 提供商用量信息 */ 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} 用量信息 */ 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; } }