mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
feat: 将费用优先调度逻辑集成到 UnifiedClaudeScheduler
- 替换 _sortAccountsByPriority 为 _sortAccountsByCost 方法 - 支持多种账户类型的费用获取(claude-official、claude-console、bedrock) - 实现智能降级机制:费用获取失败时自动回退到优先级排序 - 排序优先级:日费用 → 账户优先级 → 最后使用时间 - 添加详细的费用排名调试日志 - 确保所有 API 调用都使用费用优化的账户选择策略 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -206,8 +206,8 @@ class UnifiedClaudeScheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按优先级和最后使用时间排序
|
// 基于日费用排序账户(费用最少的优先)
|
||||||
const sortedAccounts = this._sortAccountsByPriority(availableAccounts)
|
const sortedAccounts = await this._sortAccountsByCost(availableAccounts)
|
||||||
|
|
||||||
// 选择第一个账户
|
// 选择第一个账户
|
||||||
const selectedAccount = sortedAccounts[0]
|
const selectedAccount = sortedAccounts[0]
|
||||||
@@ -482,19 +482,104 @@ class UnifiedClaudeScheduler {
|
|||||||
return availableAccounts
|
return availableAccounts
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔢 按优先级和最后使用时间排序账户
|
// 💰 基于日费用排序账户(费用最少的优先,支持多种账户类型)
|
||||||
_sortAccountsByPriority(accounts) {
|
async _sortAccountsByCost(accounts) {
|
||||||
return accounts.sort((a, b) => {
|
try {
|
||||||
// 首先按优先级排序(数字越小优先级越高)
|
// 并行获取所有账号的日费用
|
||||||
if (a.priority !== b.priority) {
|
const accountsWithCost = await Promise.all(
|
||||||
return a.priority - b.priority
|
accounts.map(async (account) => {
|
||||||
|
try {
|
||||||
|
let dailyCost = 0
|
||||||
|
|
||||||
|
// 根据账户类型获取日费用
|
||||||
|
if (account.accountType === 'claude-official') {
|
||||||
|
dailyCost = await redis.getAccountDailyCost(account.accountId || account.id)
|
||||||
|
} else if (account.accountType === 'claude-console') {
|
||||||
|
// Claude Console 账户也使用相同的费用存储机制
|
||||||
|
dailyCost = await redis.getAccountDailyCost(account.accountId || account.id)
|
||||||
|
} else if (account.accountType === 'bedrock') {
|
||||||
|
// Bedrock 账户也使用相同的费用存储机制
|
||||||
|
dailyCost = await redis.getAccountDailyCost(account.accountId || account.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...account,
|
||||||
|
_dailyCost: dailyCost
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(
|
||||||
|
`Failed to get daily cost for account ${account.accountId || account.id}: ${error.message}`
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...account,
|
||||||
|
_dailyCost: Number.MAX_SAFE_INTEGER, // 获取费用失败时,设为最高值(最低优先级)
|
||||||
|
_costError: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// 按日费用排序(费用最少的优先)
|
||||||
|
const sortedAccounts = accountsWithCost.sort((a, b) => {
|
||||||
|
// 如果费用相同,按优先级排序
|
||||||
|
if (Math.abs(a._dailyCost - b._dailyCost) < 0.000001) {
|
||||||
|
// 优先级相同时,按最后使用时间排序(最久未使用的优先)
|
||||||
|
if (a.priority === b.priority) {
|
||||||
|
const aLastUsed = new Date(a.lastUsedAt || 0).getTime()
|
||||||
|
const bLastUsed = new Date(b.lastUsedAt || 0).getTime()
|
||||||
|
return aLastUsed - bLastUsed
|
||||||
|
}
|
||||||
|
return a.priority - b.priority
|
||||||
|
}
|
||||||
|
return a._dailyCost - b._dailyCost
|
||||||
|
})
|
||||||
|
|
||||||
|
// 检查是否所有账号的费用获取都失败了
|
||||||
|
const allAccountsHaveErrors = sortedAccounts.every((account) => account._costError)
|
||||||
|
|
||||||
|
if (allAccountsHaveErrors) {
|
||||||
|
logger.warn(
|
||||||
|
'⚠️ All accounts failed to get daily cost, falling back to priority-based sorting'
|
||||||
|
)
|
||||||
|
return accounts.sort((a, b) => {
|
||||||
|
// 首先按优先级排序
|
||||||
|
if (a.priority !== b.priority) {
|
||||||
|
return a.priority - b.priority
|
||||||
|
}
|
||||||
|
// 优先级相同时,按最后使用时间排序
|
||||||
|
const aLastUsed = new Date(a.lastUsedAt || 0).getTime()
|
||||||
|
const bLastUsed = new Date(b.lastUsedAt || 0).getTime()
|
||||||
|
return aLastUsed - bLastUsed
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先级相同时,按最后使用时间排序(最久未使用的优先)
|
logger.debug('💰 Account cost ranking:')
|
||||||
const aLastUsed = new Date(a.lastUsedAt || 0).getTime()
|
sortedAccounts.forEach((account, index) => {
|
||||||
const bLastUsed = new Date(b.lastUsedAt || 0).getTime()
|
const costDisplay = account._costError ? 'ERROR' : `$${account._dailyCost.toFixed(4)}`
|
||||||
return aLastUsed - bLastUsed
|
const accountDisplayName = account.name || account.accountId || account.id
|
||||||
})
|
logger.debug(
|
||||||
|
` ${index + 1}. ${accountDisplayName} (${account.accountType}): ${costDisplay}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return sortedAccounts
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
'❌ Failed to sort accounts by cost, falling back to priority-based sorting:',
|
||||||
|
error
|
||||||
|
)
|
||||||
|
// 回退到原有的按优先级排序策略
|
||||||
|
return accounts.sort((a, b) => {
|
||||||
|
// 首先按优先级排序
|
||||||
|
if (a.priority !== b.priority) {
|
||||||
|
return a.priority - b.priority
|
||||||
|
}
|
||||||
|
// 优先级相同时,按最后使用时间排序
|
||||||
|
const aLastUsed = new Date(a.lastUsedAt || 0).getTime()
|
||||||
|
const bLastUsed = new Date(b.lastUsedAt || 0).getTime()
|
||||||
|
return aLastUsed - bLastUsed
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔍 检查账户是否可用
|
// 🔍 检查账户是否可用
|
||||||
@@ -876,8 +961,8 @@ class UnifiedClaudeScheduler {
|
|||||||
throw new Error(`No available accounts in group ${group.name}`)
|
throw new Error(`No available accounts in group ${group.name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用现有的优先级排序逻辑
|
// 使用基于费用的排序逻辑
|
||||||
const sortedAccounts = this._sortAccountsByPriority(availableAccounts)
|
const sortedAccounts = await this._sortAccountsByCost(availableAccounts)
|
||||||
|
|
||||||
// 选择第一个账户
|
// 选择第一个账户
|
||||||
const selectedAccount = sortedAccounts[0]
|
const selectedAccount = sortedAccounts[0]
|
||||||
|
|||||||
Reference in New Issue
Block a user