mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
refactor: standardize code formatting and linting configuration
- Replace .eslintrc.js with .eslintrc.cjs for better ES module compatibility - Add .prettierrc configuration for consistent code formatting - Update package.json with new lint and format scripts - Add nodemon.json for development hot reloading configuration - Standardize code formatting across all JavaScript and Vue files - Update web admin SPA with improved linting rules and formatting - Add prettier configuration to web admin SPA 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
* 从 LiteLLM 仓库下载最新的模型价格和上下文窗口信息
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const https = require('https')
|
||||
|
||||
// 颜色输出
|
||||
const colors = {
|
||||
@@ -18,7 +18,7 @@ const colors = {
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[36m',
|
||||
magenta: '\x1b[35m'
|
||||
};
|
||||
}
|
||||
|
||||
// 日志函数
|
||||
const log = {
|
||||
@@ -26,23 +26,29 @@ const log = {
|
||||
success: (msg) => console.log(`${colors.green}[SUCCESS]${colors.reset} ${msg}`),
|
||||
error: (msg) => console.error(`${colors.red}[ERROR]${colors.reset} ${msg}`),
|
||||
warn: (msg) => console.warn(`${colors.yellow}[WARNING]${colors.reset} ${msg}`)
|
||||
};
|
||||
}
|
||||
|
||||
// 配置
|
||||
const config = {
|
||||
dataDir: path.join(process.cwd(), 'data'),
|
||||
pricingFile: path.join(process.cwd(), 'data', 'model_pricing.json'),
|
||||
pricingUrl: 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json',
|
||||
fallbackFile: path.join(process.cwd(), 'resources', 'model-pricing', 'model_prices_and_context_window.json'),
|
||||
pricingUrl:
|
||||
'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json',
|
||||
fallbackFile: path.join(
|
||||
process.cwd(),
|
||||
'resources',
|
||||
'model-pricing',
|
||||
'model_prices_and_context_window.json'
|
||||
),
|
||||
backupFile: path.join(process.cwd(), 'data', 'model_pricing.backup.json'),
|
||||
timeout: 30000 // 30秒超时
|
||||
};
|
||||
}
|
||||
|
||||
// 创建数据目录
|
||||
function ensureDataDir() {
|
||||
if (!fs.existsSync(config.dataDir)) {
|
||||
fs.mkdirSync(config.dataDir, { recursive: true });
|
||||
log.info('Created data directory');
|
||||
fs.mkdirSync(config.dataDir, { recursive: true })
|
||||
log.info('Created data directory')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,213 +56,217 @@ function ensureDataDir() {
|
||||
function backupExistingFile() {
|
||||
if (fs.existsSync(config.pricingFile)) {
|
||||
try {
|
||||
fs.copyFileSync(config.pricingFile, config.backupFile);
|
||||
log.info('Backed up existing pricing file');
|
||||
return true;
|
||||
fs.copyFileSync(config.pricingFile, config.backupFile)
|
||||
log.info('Backed up existing pricing file')
|
||||
return true
|
||||
} catch (error) {
|
||||
log.warn(`Failed to backup existing file: ${error.message}`);
|
||||
return false;
|
||||
log.warn(`Failed to backup existing file: ${error.message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// 恢复备份
|
||||
function restoreBackup() {
|
||||
if (fs.existsSync(config.backupFile)) {
|
||||
try {
|
||||
fs.copyFileSync(config.backupFile, config.pricingFile);
|
||||
log.info('Restored from backup');
|
||||
return true;
|
||||
fs.copyFileSync(config.backupFile, config.pricingFile)
|
||||
log.info('Restored from backup')
|
||||
return true
|
||||
} catch (error) {
|
||||
log.error(`Failed to restore backup: ${error.message}`);
|
||||
return false;
|
||||
log.error(`Failed to restore backup: ${error.message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// 下载价格数据
|
||||
function downloadPricingData() {
|
||||
return new Promise((resolve, reject) => {
|
||||
log.info(`Downloading model pricing data from LiteLLM...`);
|
||||
log.info(`URL: ${config.pricingUrl}`);
|
||||
|
||||
log.info('Downloading model pricing data from LiteLLM...')
|
||||
log.info(`URL: ${config.pricingUrl}`)
|
||||
|
||||
const request = https.get(config.pricingUrl, (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
||||
return;
|
||||
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`))
|
||||
return
|
||||
}
|
||||
|
||||
let data = '';
|
||||
let downloadedBytes = 0;
|
||||
|
||||
let data = ''
|
||||
let downloadedBytes = 0
|
||||
|
||||
response.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
downloadedBytes += chunk.length;
|
||||
data += chunk
|
||||
downloadedBytes += chunk.length
|
||||
// 显示下载进度
|
||||
process.stdout.write(`\rDownloading... ${Math.round(downloadedBytes / 1024)}KB`);
|
||||
});
|
||||
process.stdout.write(`\rDownloading... ${Math.round(downloadedBytes / 1024)}KB`)
|
||||
})
|
||||
|
||||
response.on('end', () => {
|
||||
process.stdout.write('\n'); // 换行
|
||||
process.stdout.write('\n') // 换行
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
|
||||
const jsonData = JSON.parse(data)
|
||||
|
||||
// 验证数据结构
|
||||
if (typeof jsonData !== 'object' || Object.keys(jsonData).length === 0) {
|
||||
throw new Error('Invalid pricing data structure');
|
||||
throw new Error('Invalid pricing data structure')
|
||||
}
|
||||
|
||||
|
||||
// 保存到文件
|
||||
fs.writeFileSync(config.pricingFile, JSON.stringify(jsonData, null, 2));
|
||||
|
||||
const modelCount = Object.keys(jsonData).length;
|
||||
const fileSize = Math.round(fs.statSync(config.pricingFile).size / 1024);
|
||||
|
||||
log.success(`Downloaded pricing data for ${modelCount} models (${fileSize}KB)`);
|
||||
|
||||
fs.writeFileSync(config.pricingFile, JSON.stringify(jsonData, null, 2))
|
||||
|
||||
const modelCount = Object.keys(jsonData).length
|
||||
const fileSize = Math.round(fs.statSync(config.pricingFile).size / 1024)
|
||||
|
||||
log.success(`Downloaded pricing data for ${modelCount} models (${fileSize}KB)`)
|
||||
|
||||
// 显示一些统计信息
|
||||
const claudeModels = Object.keys(jsonData).filter(k => k.includes('claude')).length;
|
||||
const gptModels = Object.keys(jsonData).filter(k => k.includes('gpt')).length;
|
||||
const geminiModels = Object.keys(jsonData).filter(k => k.includes('gemini')).length;
|
||||
|
||||
log.info(`Model breakdown:`);
|
||||
log.info(` - Claude models: ${claudeModels}`);
|
||||
log.info(` - GPT models: ${gptModels}`);
|
||||
log.info(` - Gemini models: ${geminiModels}`);
|
||||
log.info(` - Other models: ${modelCount - claudeModels - gptModels - geminiModels}`);
|
||||
|
||||
resolve(jsonData);
|
||||
const claudeModels = Object.keys(jsonData).filter((k) => k.includes('claude')).length
|
||||
const gptModels = Object.keys(jsonData).filter((k) => k.includes('gpt')).length
|
||||
const geminiModels = Object.keys(jsonData).filter((k) => k.includes('gemini')).length
|
||||
|
||||
log.info('Model breakdown:')
|
||||
log.info(` - Claude models: ${claudeModels}`)
|
||||
log.info(` - GPT models: ${gptModels}`)
|
||||
log.info(` - Gemini models: ${geminiModels}`)
|
||||
log.info(` - Other models: ${modelCount - claudeModels - gptModels - geminiModels}`)
|
||||
|
||||
resolve(jsonData)
|
||||
} catch (error) {
|
||||
reject(new Error(`Failed to parse pricing data: ${error.message}`));
|
||||
reject(new Error(`Failed to parse pricing data: ${error.message}`))
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
request.on('error', (error) => {
|
||||
reject(new Error(`Network error: ${error.message}`));
|
||||
});
|
||||
reject(new Error(`Network error: ${error.message}`))
|
||||
})
|
||||
|
||||
request.setTimeout(config.timeout, () => {
|
||||
request.destroy();
|
||||
reject(new Error(`Download timeout after ${config.timeout / 1000} seconds`));
|
||||
});
|
||||
});
|
||||
request.destroy()
|
||||
reject(new Error(`Download timeout after ${config.timeout / 1000} seconds`))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 使用 fallback 文件
|
||||
function useFallback() {
|
||||
log.warn('Attempting to use fallback pricing data...');
|
||||
|
||||
log.warn('Attempting to use fallback pricing data...')
|
||||
|
||||
if (!fs.existsSync(config.fallbackFile)) {
|
||||
log.error(`Fallback file not found: ${config.fallbackFile}`);
|
||||
return false;
|
||||
log.error(`Fallback file not found: ${config.fallbackFile}`)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const fallbackData = fs.readFileSync(config.fallbackFile, 'utf8');
|
||||
const jsonData = JSON.parse(fallbackData);
|
||||
|
||||
const fallbackData = fs.readFileSync(config.fallbackFile, 'utf8')
|
||||
const jsonData = JSON.parse(fallbackData)
|
||||
|
||||
// 保存到data目录
|
||||
fs.writeFileSync(config.pricingFile, JSON.stringify(jsonData, null, 2));
|
||||
|
||||
const modelCount = Object.keys(jsonData).length;
|
||||
log.warn(`Using fallback pricing data for ${modelCount} models`);
|
||||
log.info('Note: Fallback data may be outdated. Try updating again later.');
|
||||
|
||||
return true;
|
||||
fs.writeFileSync(config.pricingFile, JSON.stringify(jsonData, null, 2))
|
||||
|
||||
const modelCount = Object.keys(jsonData).length
|
||||
log.warn(`Using fallback pricing data for ${modelCount} models`)
|
||||
log.info('Note: Fallback data may be outdated. Try updating again later.')
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
log.error(`Failed to use fallback: ${error.message}`);
|
||||
return false;
|
||||
log.error(`Failed to use fallback: ${error.message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 显示当前状态
|
||||
function showCurrentStatus() {
|
||||
if (fs.existsSync(config.pricingFile)) {
|
||||
const stats = fs.statSync(config.pricingFile);
|
||||
const fileAge = Date.now() - stats.mtime.getTime();
|
||||
const ageInHours = Math.round(fileAge / (60 * 60 * 1000));
|
||||
const ageInDays = Math.floor(ageInHours / 24);
|
||||
|
||||
let ageString = '';
|
||||
const stats = fs.statSync(config.pricingFile)
|
||||
const fileAge = Date.now() - stats.mtime.getTime()
|
||||
const ageInHours = Math.round(fileAge / (60 * 60 * 1000))
|
||||
const ageInDays = Math.floor(ageInHours / 24)
|
||||
|
||||
let ageString = ''
|
||||
if (ageInDays > 0) {
|
||||
ageString = `${ageInDays} day${ageInDays > 1 ? 's' : ''} and ${ageInHours % 24} hour${(ageInHours % 24) !== 1 ? 's' : ''}`;
|
||||
ageString = `${ageInDays} day${ageInDays > 1 ? 's' : ''} and ${ageInHours % 24} hour${ageInHours % 24 !== 1 ? 's' : ''}`
|
||||
} else {
|
||||
ageString = `${ageInHours} hour${ageInHours !== 1 ? 's' : ''}`;
|
||||
ageString = `${ageInHours} hour${ageInHours !== 1 ? 's' : ''}`
|
||||
}
|
||||
|
||||
log.info(`Current pricing file age: ${ageString}`);
|
||||
|
||||
|
||||
log.info(`Current pricing file age: ${ageString}`)
|
||||
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(config.pricingFile, 'utf8'));
|
||||
log.info(`Current file contains ${Object.keys(data).length} models`);
|
||||
const data = JSON.parse(fs.readFileSync(config.pricingFile, 'utf8'))
|
||||
log.info(`Current file contains ${Object.keys(data).length} models`)
|
||||
} catch (error) {
|
||||
log.warn('Current file exists but could not be parsed');
|
||||
log.warn('Current file exists but could not be parsed')
|
||||
}
|
||||
} else {
|
||||
log.info('No existing pricing file found');
|
||||
log.info('No existing pricing file found')
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
console.log(`${colors.bright}${colors.blue}======================================${colors.reset}`);
|
||||
console.log(`${colors.bright} Model Pricing Update Tool${colors.reset}`);
|
||||
console.log(`${colors.bright}${colors.blue}======================================${colors.reset}\n`);
|
||||
|
||||
console.log(`${colors.bright}${colors.blue}======================================${colors.reset}`)
|
||||
console.log(`${colors.bright} Model Pricing Update Tool${colors.reset}`)
|
||||
console.log(
|
||||
`${colors.bright}${colors.blue}======================================${colors.reset}\n`
|
||||
)
|
||||
|
||||
// 显示当前状态
|
||||
showCurrentStatus();
|
||||
console.log('');
|
||||
|
||||
showCurrentStatus()
|
||||
console.log('')
|
||||
|
||||
// 确保数据目录存在
|
||||
ensureDataDir();
|
||||
|
||||
ensureDataDir()
|
||||
|
||||
// 备份现有文件
|
||||
const hasBackup = backupExistingFile();
|
||||
|
||||
const hasBackup = backupExistingFile()
|
||||
|
||||
try {
|
||||
// 尝试下载最新数据
|
||||
await downloadPricingData();
|
||||
|
||||
await downloadPricingData()
|
||||
|
||||
// 清理备份文件(成功下载后)
|
||||
if (hasBackup && fs.existsSync(config.backupFile)) {
|
||||
fs.unlinkSync(config.backupFile);
|
||||
log.info('Cleaned up backup file');
|
||||
fs.unlinkSync(config.backupFile)
|
||||
log.info('Cleaned up backup file')
|
||||
}
|
||||
|
||||
console.log(`\n${colors.green}✅ Model pricing updated successfully!${colors.reset}`);
|
||||
process.exit(0);
|
||||
|
||||
console.log(`\n${colors.green}✅ Model pricing updated successfully!${colors.reset}`)
|
||||
process.exit(0)
|
||||
} catch (error) {
|
||||
log.error(`Download failed: ${error.message}`);
|
||||
|
||||
log.error(`Download failed: ${error.message}`)
|
||||
|
||||
// 尝试恢复备份
|
||||
if (hasBackup) {
|
||||
if (restoreBackup()) {
|
||||
log.info('Original file restored');
|
||||
log.info('Original file restored')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 尝试使用 fallback
|
||||
if (useFallback()) {
|
||||
console.log(`\n${colors.yellow}⚠️ Using fallback data (update completed with warnings)${colors.reset}`);
|
||||
process.exit(0);
|
||||
console.log(
|
||||
`\n${colors.yellow}⚠️ Using fallback data (update completed with warnings)${colors.reset}`
|
||||
)
|
||||
process.exit(0)
|
||||
} else {
|
||||
console.log(`\n${colors.red}❌ Failed to update model pricing${colors.reset}`);
|
||||
process.exit(1);
|
||||
console.log(`\n${colors.red}❌ Failed to update model pricing${colors.reset}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理未捕获的错误
|
||||
process.on('unhandledRejection', (error) => {
|
||||
log.error(`Unhandled error: ${error.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
log.error(`Unhandled error: ${error.message}`)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
// 运行主函数
|
||||
main().catch((error) => {
|
||||
log.error(`Fatal error: ${error.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
log.error(`Fatal error: ${error.message}`)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user