|
|
#!/usr/bin/env node |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const redis = require('../src/models/redis') |
|
|
const logger = require('../src/utils/logger') |
|
|
|
|
|
|
|
|
const args = process.argv.slice(2) |
|
|
const shouldClean = args.includes('--clean') |
|
|
|
|
|
|
|
|
const models = [ |
|
|
'claude-sonnet-4-20250514', |
|
|
'claude-3-5-sonnet-20241022', |
|
|
'claude-3-5-haiku-20241022', |
|
|
'claude-3-opus-20240229' |
|
|
] |
|
|
|
|
|
|
|
|
async function generateDataForDate(apiKeyId, date, dayOffset) { |
|
|
const client = redis.getClientSafe() |
|
|
const dateStr = date.toISOString().split('T')[0] |
|
|
const month = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}` |
|
|
|
|
|
|
|
|
const requestCount = Math.max(5, 20 - dayOffset * 2) |
|
|
|
|
|
logger.info(`📊 Generating ${requestCount} requests for ${dateStr}`) |
|
|
|
|
|
for (let i = 0; i < requestCount; i++) { |
|
|
|
|
|
const model = models[Math.floor(Math.random() * models.length)] |
|
|
|
|
|
|
|
|
const inputTokens = Math.floor(Math.random() * 2000) + 500 |
|
|
const outputTokens = Math.floor(Math.random() * 3000) + 1000 |
|
|
const cacheCreateTokens = Math.random() > 0.7 ? Math.floor(Math.random() * 1000) : 0 |
|
|
const cacheReadTokens = Math.random() > 0.5 ? Math.floor(Math.random() * 500) : 0 |
|
|
|
|
|
const coreTokens = inputTokens + outputTokens |
|
|
const allTokens = inputTokens + outputTokens + cacheCreateTokens + cacheReadTokens |
|
|
|
|
|
|
|
|
const totalKey = `usage:${apiKeyId}` |
|
|
const dailyKey = `usage:daily:${apiKeyId}:${dateStr}` |
|
|
const monthlyKey = `usage:monthly:${apiKeyId}:${month}` |
|
|
const modelDailyKey = `usage:model:daily:${model}:${dateStr}` |
|
|
const modelMonthlyKey = `usage:model:monthly:${model}:${month}` |
|
|
const keyModelDailyKey = `usage:${apiKeyId}:model:daily:${model}:${dateStr}` |
|
|
const keyModelMonthlyKey = `usage:${apiKeyId}:model:monthly:${model}:${month}` |
|
|
|
|
|
await Promise.all([ |
|
|
|
|
|
client.hincrby(totalKey, 'totalTokens', coreTokens), |
|
|
client.hincrby(totalKey, 'totalInputTokens', inputTokens), |
|
|
client.hincrby(totalKey, 'totalOutputTokens', outputTokens), |
|
|
client.hincrby(totalKey, 'totalCacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(totalKey, 'totalCacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(totalKey, 'totalAllTokens', allTokens), |
|
|
client.hincrby(totalKey, 'totalRequests', 1), |
|
|
|
|
|
|
|
|
client.hincrby(dailyKey, 'tokens', coreTokens), |
|
|
client.hincrby(dailyKey, 'inputTokens', inputTokens), |
|
|
client.hincrby(dailyKey, 'outputTokens', outputTokens), |
|
|
client.hincrby(dailyKey, 'cacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(dailyKey, 'cacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(dailyKey, 'allTokens', allTokens), |
|
|
client.hincrby(dailyKey, 'requests', 1), |
|
|
|
|
|
|
|
|
client.hincrby(monthlyKey, 'tokens', coreTokens), |
|
|
client.hincrby(monthlyKey, 'inputTokens', inputTokens), |
|
|
client.hincrby(monthlyKey, 'outputTokens', outputTokens), |
|
|
client.hincrby(monthlyKey, 'cacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(monthlyKey, 'cacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(monthlyKey, 'allTokens', allTokens), |
|
|
client.hincrby(monthlyKey, 'requests', 1), |
|
|
|
|
|
|
|
|
client.hincrby(modelDailyKey, 'totalInputTokens', inputTokens), |
|
|
client.hincrby(modelDailyKey, 'totalOutputTokens', outputTokens), |
|
|
client.hincrby(modelDailyKey, 'totalCacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(modelDailyKey, 'totalCacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(modelDailyKey, 'totalAllTokens', allTokens), |
|
|
client.hincrby(modelDailyKey, 'requests', 1), |
|
|
|
|
|
|
|
|
client.hincrby(modelMonthlyKey, 'totalInputTokens', inputTokens), |
|
|
client.hincrby(modelMonthlyKey, 'totalOutputTokens', outputTokens), |
|
|
client.hincrby(modelMonthlyKey, 'totalCacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(modelMonthlyKey, 'totalCacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(modelMonthlyKey, 'totalAllTokens', allTokens), |
|
|
client.hincrby(modelMonthlyKey, 'requests', 1), |
|
|
|
|
|
|
|
|
|
|
|
client.hincrby(keyModelDailyKey, 'inputTokens', inputTokens), |
|
|
client.hincrby(keyModelDailyKey, 'outputTokens', outputTokens), |
|
|
client.hincrby(keyModelDailyKey, 'cacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(keyModelDailyKey, 'cacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(keyModelDailyKey, 'allTokens', allTokens), |
|
|
client.hincrby(keyModelDailyKey, 'totalInputTokens', inputTokens), |
|
|
client.hincrby(keyModelDailyKey, 'totalOutputTokens', outputTokens), |
|
|
client.hincrby(keyModelDailyKey, 'totalCacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(keyModelDailyKey, 'totalCacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(keyModelDailyKey, 'totalAllTokens', allTokens), |
|
|
client.hincrby(keyModelDailyKey, 'requests', 1), |
|
|
|
|
|
|
|
|
client.hincrby(keyModelMonthlyKey, 'inputTokens', inputTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'outputTokens', outputTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'cacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'cacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'allTokens', allTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'totalInputTokens', inputTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'totalOutputTokens', outputTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'totalCacheCreateTokens', cacheCreateTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'totalCacheReadTokens', cacheReadTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'totalAllTokens', allTokens), |
|
|
client.hincrby(keyModelMonthlyKey, 'requests', 1) |
|
|
]) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async function cleanTestData() { |
|
|
const client = redis.getClientSafe() |
|
|
const apiKeyService = require('../src/services/apiKeyService') |
|
|
|
|
|
logger.info('🧹 Cleaning test data...') |
|
|
|
|
|
|
|
|
const allKeys = await apiKeyService.getAllApiKeys() |
|
|
|
|
|
|
|
|
const testKeys = allKeys.filter((key) => key.name && key.name.startsWith('Test API Key')) |
|
|
|
|
|
for (const testKey of testKeys) { |
|
|
const apiKeyId = testKey.id |
|
|
|
|
|
|
|
|
const patterns = [ |
|
|
`usage:${apiKeyId}`, |
|
|
`usage:daily:${apiKeyId}:*`, |
|
|
`usage:monthly:${apiKeyId}:*`, |
|
|
`usage:${apiKeyId}:model:daily:*`, |
|
|
`usage:${apiKeyId}:model:monthly:*` |
|
|
] |
|
|
|
|
|
for (const pattern of patterns) { |
|
|
const keys = await client.keys(pattern) |
|
|
if (keys.length > 0) { |
|
|
await client.del(...keys) |
|
|
logger.info(`🗑️ Deleted ${keys.length} keys matching pattern: ${pattern}`) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
await apiKeyService.deleteApiKey(apiKeyId) |
|
|
logger.info(`🗑️ Deleted test API Key: ${testKey.name} (${apiKeyId})`) |
|
|
} |
|
|
|
|
|
|
|
|
const modelPatterns = ['usage:model:daily:*', 'usage:model:monthly:*'] |
|
|
|
|
|
for (const pattern of modelPatterns) { |
|
|
const keys = await client.keys(pattern) |
|
|
if (keys.length > 0) { |
|
|
await client.del(...keys) |
|
|
logger.info(`🗑️ Deleted ${keys.length} keys matching pattern: ${pattern}`) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async function main() { |
|
|
try { |
|
|
await redis.connect() |
|
|
logger.success('✅ Connected to Redis') |
|
|
|
|
|
|
|
|
const apiKeyService = require('../src/services/apiKeyService') |
|
|
const testApiKeys = [] |
|
|
const createdKeys = [] |
|
|
|
|
|
|
|
|
logger.info('📝 Creating test API Keys...') |
|
|
|
|
|
for (let i = 1; i <= 3; i++) { |
|
|
const newKey = await apiKeyService.generateApiKey({ |
|
|
name: `Test API Key ${i}`, |
|
|
description: `Test key for historical data generation ${i}`, |
|
|
tokenLimit: 10000000, |
|
|
concurrencyLimit: 10, |
|
|
rateLimitWindow: 60, |
|
|
rateLimitRequests: 100 |
|
|
}) |
|
|
|
|
|
testApiKeys.push(newKey.id) |
|
|
createdKeys.push(newKey) |
|
|
logger.success(`✅ Created test API Key: ${newKey.name} (${newKey.id})`) |
|
|
logger.info(` 🔑 API Key: ${newKey.apiKey}`) |
|
|
} |
|
|
|
|
|
if (shouldClean) { |
|
|
await cleanTestData() |
|
|
logger.success('✅ Test data cleaned successfully') |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
const now = new Date() |
|
|
|
|
|
for (const apiKeyId of testApiKeys) { |
|
|
logger.info(`\n🔄 Generating data for API Key: ${apiKeyId}`) |
|
|
|
|
|
|
|
|
for (let dayOffset = 0; dayOffset < 30; dayOffset++) { |
|
|
const date = new Date(now) |
|
|
date.setDate(date.getDate() - dayOffset) |
|
|
|
|
|
await generateDataForDate(apiKeyId, date, dayOffset) |
|
|
} |
|
|
|
|
|
logger.success(`✅ Generated 30 days of historical data for API Key: ${apiKeyId}`) |
|
|
} |
|
|
|
|
|
|
|
|
logger.info('\n📊 Test Data Summary:') |
|
|
logger.info('='.repeat(60)) |
|
|
|
|
|
for (const apiKeyId of testApiKeys) { |
|
|
const totalKey = `usage:${apiKeyId}` |
|
|
const totalData = await redis.getClientSafe().hgetall(totalKey) |
|
|
|
|
|
if (totalData && Object.keys(totalData).length > 0) { |
|
|
logger.info(`\nAPI Key: ${apiKeyId}`) |
|
|
logger.info(` Total Requests: ${totalData.totalRequests || 0}`) |
|
|
logger.info(` Total Tokens (Core): ${totalData.totalTokens || 0}`) |
|
|
logger.info(` Total Tokens (All): ${totalData.totalAllTokens || 0}`) |
|
|
logger.info(` Input Tokens: ${totalData.totalInputTokens || 0}`) |
|
|
logger.info(` Output Tokens: ${totalData.totalOutputTokens || 0}`) |
|
|
logger.info(` Cache Create Tokens: ${totalData.totalCacheCreateTokens || 0}`) |
|
|
logger.info(` Cache Read Tokens: ${totalData.totalCacheReadTokens || 0}`) |
|
|
} |
|
|
} |
|
|
|
|
|
logger.info(`\n${'='.repeat(60)}`) |
|
|
logger.success('\n✅ Test data generation completed!') |
|
|
logger.info('\n📋 Created API Keys:') |
|
|
for (const key of createdKeys) { |
|
|
logger.info(`- ${key.name}: ${key.apiKey}`) |
|
|
} |
|
|
logger.info('\n💡 Tips:') |
|
|
logger.info('- Check the admin panel to see the different time ranges') |
|
|
logger.info('- Use --clean flag to remove all test data and API Keys') |
|
|
logger.info('- The script generates more recent data to simulate real usage patterns') |
|
|
} catch (error) { |
|
|
logger.error('❌ Error:', error) |
|
|
} finally { |
|
|
await redis.disconnect() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
main().catch((error) => { |
|
|
logger.error('💥 Unexpected error:', error) |
|
|
process.exit(1) |
|
|
}) |
|
|
|