mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
fix: 完成调度优先级逻辑的处理
This commit is contained in:
@@ -62,8 +62,9 @@ async function handleMessagesRequest(req, res) {
|
|||||||
// 生成会话哈希用于sticky会话
|
// 生成会话哈希用于sticky会话
|
||||||
const sessionHash = sessionHelper.generateSessionHash(req.body);
|
const sessionHash = sessionHelper.generateSessionHash(req.body);
|
||||||
|
|
||||||
// 使用统一调度选择账号
|
// 使用统一调度选择账号(传递请求的模型)
|
||||||
const { accountId, accountType } = await unifiedClaudeScheduler.selectAccountForApiKey(req.apiKey, sessionHash);
|
const requestedModel = req.body.model;
|
||||||
|
const { accountId, accountType } = await unifiedClaudeScheduler.selectAccountForApiKey(req.apiKey, sessionHash, requestedModel);
|
||||||
|
|
||||||
// 根据账号类型选择对应的转发服务并调用
|
// 根据账号类型选择对应的转发服务并调用
|
||||||
if (accountType === 'claude-official') {
|
if (accountType === 'claude-official') {
|
||||||
@@ -152,16 +153,22 @@ async function handleMessagesRequest(req, res) {
|
|||||||
// 生成会话哈希用于sticky会话
|
// 生成会话哈希用于sticky会话
|
||||||
const sessionHash = sessionHelper.generateSessionHash(req.body);
|
const sessionHash = sessionHelper.generateSessionHash(req.body);
|
||||||
|
|
||||||
// 使用统一调度选择账号
|
// 使用统一调度选择账号(传递请求的模型)
|
||||||
const { accountId, accountType } = await unifiedClaudeScheduler.selectAccountForApiKey(req.apiKey, sessionHash);
|
const requestedModel = req.body.model;
|
||||||
|
const { accountId, accountType } = await unifiedClaudeScheduler.selectAccountForApiKey(req.apiKey, sessionHash, requestedModel);
|
||||||
|
|
||||||
// 根据账号类型选择对应的转发服务
|
// 根据账号类型选择对应的转发服务
|
||||||
let response;
|
let response;
|
||||||
|
logger.debug(`[DEBUG] Request query params: ${JSON.stringify(req.query)}`);
|
||||||
|
logger.debug(`[DEBUG] Request URL: ${req.url}`);
|
||||||
|
logger.debug(`[DEBUG] Request path: ${req.path}`);
|
||||||
|
|
||||||
if (accountType === 'claude-official') {
|
if (accountType === 'claude-official') {
|
||||||
// 官方Claude账号使用原有的转发服务
|
// 官方Claude账号使用原有的转发服务
|
||||||
response = await claudeRelayService.relayRequest(req.body, req.apiKey, req, res, req.headers);
|
response = await claudeRelayService.relayRequest(req.body, req.apiKey, req, res, req.headers);
|
||||||
} else {
|
} else {
|
||||||
// Claude Console账号使用Console转发服务
|
// Claude Console账号使用Console转发服务
|
||||||
|
logger.debug(`[DEBUG] Calling claudeConsoleRelayService.relayRequest with accountId: ${accountId}`);
|
||||||
response = await claudeConsoleRelayService.relayRequest(req.body, req.apiKey, req, res, req.headers, accountId);
|
response = await claudeConsoleRelayService.relayRequest(req.body, req.apiKey, req, res, req.headers, accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class ClaudeConsoleAccountService {
|
|||||||
platform: 'claude-console',
|
platform: 'claude-console',
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
apiUrl: this._encryptSensitiveData(apiUrl),
|
apiUrl: apiUrl,
|
||||||
apiKey: this._encryptSensitiveData(apiKey),
|
apiKey: this._encryptSensitiveData(apiKey),
|
||||||
priority: priority.toString(),
|
priority: priority.toString(),
|
||||||
supportedModels: JSON.stringify(supportedModels),
|
supportedModels: JSON.stringify(supportedModels),
|
||||||
@@ -64,6 +64,9 @@ class ClaudeConsoleAccountService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const client = redis.getClientSafe();
|
const client = redis.getClientSafe();
|
||||||
|
logger.debug(`[DEBUG] Saving account data to Redis with key: ${this.ACCOUNT_KEY_PREFIX}${accountId}`);
|
||||||
|
logger.debug(`[DEBUG] Account data to save: ${JSON.stringify(accountData, null, 2)}`);
|
||||||
|
|
||||||
await client.hset(
|
await client.hset(
|
||||||
`${this.ACCOUNT_KEY_PREFIX}${accountId}`,
|
`${this.ACCOUNT_KEY_PREFIX}${accountId}`,
|
||||||
accountData
|
accountData
|
||||||
@@ -111,7 +114,7 @@ class ClaudeConsoleAccountService {
|
|||||||
platform: accountData.platform,
|
platform: accountData.platform,
|
||||||
name: accountData.name,
|
name: accountData.name,
|
||||||
description: accountData.description,
|
description: accountData.description,
|
||||||
apiUrl: this._maskApiUrl(this._decryptSensitiveData(accountData.apiUrl)),
|
apiUrl: accountData.apiUrl,
|
||||||
priority: parseInt(accountData.priority) || 50,
|
priority: parseInt(accountData.priority) || 50,
|
||||||
supportedModels: JSON.parse(accountData.supportedModels || '[]'),
|
supportedModels: JSON.parse(accountData.supportedModels || '[]'),
|
||||||
userAgent: accountData.userAgent,
|
userAgent: accountData.userAgent,
|
||||||
@@ -138,18 +141,28 @@ class ClaudeConsoleAccountService {
|
|||||||
// 🔍 获取单个账户(内部使用,包含敏感信息)
|
// 🔍 获取单个账户(内部使用,包含敏感信息)
|
||||||
async getAccount(accountId) {
|
async getAccount(accountId) {
|
||||||
const client = redis.getClientSafe();
|
const client = redis.getClientSafe();
|
||||||
|
logger.debug(`[DEBUG] Getting account data for ID: ${accountId}`);
|
||||||
const accountData = await client.hgetall(`${this.ACCOUNT_KEY_PREFIX}${accountId}`);
|
const accountData = await client.hgetall(`${this.ACCOUNT_KEY_PREFIX}${accountId}`);
|
||||||
|
|
||||||
if (!accountData || Object.keys(accountData).length === 0) {
|
if (!accountData || Object.keys(accountData).length === 0) {
|
||||||
|
logger.debug(`[DEBUG] No account data found for ID: ${accountId}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解密敏感字段
|
logger.debug(`[DEBUG] Raw account data keys: ${Object.keys(accountData).join(', ')}`);
|
||||||
accountData.apiUrl = this._decryptSensitiveData(accountData.apiUrl);
|
logger.debug(`[DEBUG] Raw supportedModels value: ${accountData.supportedModels}`);
|
||||||
accountData.apiKey = this._decryptSensitiveData(accountData.apiKey);
|
|
||||||
|
// 解密敏感字段(只解密apiKey,apiUrl不加密)
|
||||||
|
const decryptedKey = this._decryptSensitiveData(accountData.apiKey);
|
||||||
|
logger.debug(`[DEBUG] URL exists: ${!!accountData.apiUrl}, Decrypted key exists: ${!!decryptedKey}`);
|
||||||
|
|
||||||
|
accountData.apiKey = decryptedKey;
|
||||||
|
|
||||||
// 解析JSON字段
|
// 解析JSON字段
|
||||||
accountData.supportedModels = JSON.parse(accountData.supportedModels || '[]');
|
const parsedModels = JSON.parse(accountData.supportedModels || '[]');
|
||||||
|
logger.debug(`[DEBUG] Parsed supportedModels: ${JSON.stringify(parsedModels)}`);
|
||||||
|
|
||||||
|
accountData.supportedModels = parsedModels;
|
||||||
accountData.priority = parseInt(accountData.priority) || 50;
|
accountData.priority = parseInt(accountData.priority) || 50;
|
||||||
accountData.rateLimitDuration = parseInt(accountData.rateLimitDuration) || 60;
|
accountData.rateLimitDuration = parseInt(accountData.rateLimitDuration) || 60;
|
||||||
accountData.isActive = accountData.isActive === 'true';
|
accountData.isActive = accountData.isActive === 'true';
|
||||||
@@ -158,6 +171,8 @@ class ClaudeConsoleAccountService {
|
|||||||
accountData.proxy = JSON.parse(accountData.proxy);
|
accountData.proxy = JSON.parse(accountData.proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug(`[DEBUG] Final account data - name: ${accountData.name}, hasApiUrl: ${!!accountData.apiUrl}, hasApiKey: ${!!accountData.apiKey}, supportedModels: ${JSON.stringify(accountData.supportedModels)}`);
|
||||||
|
|
||||||
return accountData;
|
return accountData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,12 +188,24 @@ class ClaudeConsoleAccountService {
|
|||||||
const updatedData = {};
|
const updatedData = {};
|
||||||
|
|
||||||
// 处理各个字段的更新
|
// 处理各个字段的更新
|
||||||
|
logger.debug(`[DEBUG] Update request received with fields: ${Object.keys(updates).join(', ')}`);
|
||||||
|
logger.debug(`[DEBUG] Updates content: ${JSON.stringify(updates, null, 2)}`);
|
||||||
|
|
||||||
if (updates.name !== undefined) updatedData.name = updates.name;
|
if (updates.name !== undefined) updatedData.name = updates.name;
|
||||||
if (updates.description !== undefined) updatedData.description = updates.description;
|
if (updates.description !== undefined) updatedData.description = updates.description;
|
||||||
if (updates.apiUrl !== undefined) updatedData.apiUrl = this._encryptSensitiveData(updates.apiUrl);
|
if (updates.apiUrl !== undefined) {
|
||||||
if (updates.apiKey !== undefined) updatedData.apiKey = this._encryptSensitiveData(updates.apiKey);
|
logger.debug(`[DEBUG] Updating apiUrl from frontend: ${updates.apiUrl}`);
|
||||||
|
updatedData.apiUrl = updates.apiUrl;
|
||||||
|
}
|
||||||
|
if (updates.apiKey !== undefined) {
|
||||||
|
logger.debug(`[DEBUG] Updating apiKey (length: ${updates.apiKey?.length})`);
|
||||||
|
updatedData.apiKey = this._encryptSensitiveData(updates.apiKey);
|
||||||
|
}
|
||||||
if (updates.priority !== undefined) updatedData.priority = updates.priority.toString();
|
if (updates.priority !== undefined) updatedData.priority = updates.priority.toString();
|
||||||
if (updates.supportedModels !== undefined) updatedData.supportedModels = JSON.stringify(updates.supportedModels);
|
if (updates.supportedModels !== undefined) {
|
||||||
|
logger.debug(`[DEBUG] Updating supportedModels: ${JSON.stringify(updates.supportedModels)}`);
|
||||||
|
updatedData.supportedModels = JSON.stringify(updates.supportedModels);
|
||||||
|
}
|
||||||
if (updates.userAgent !== undefined) updatedData.userAgent = updates.userAgent;
|
if (updates.userAgent !== undefined) updatedData.userAgent = updates.userAgent;
|
||||||
if (updates.rateLimitDuration !== undefined) updatedData.rateLimitDuration = updates.rateLimitDuration.toString();
|
if (updates.rateLimitDuration !== undefined) updatedData.rateLimitDuration = updates.rateLimitDuration.toString();
|
||||||
if (updates.proxy !== undefined) updatedData.proxy = updates.proxy ? JSON.stringify(updates.proxy) : '';
|
if (updates.proxy !== undefined) updatedData.proxy = updates.proxy ? JSON.stringify(updates.proxy) : '';
|
||||||
@@ -197,6 +224,9 @@ class ClaudeConsoleAccountService {
|
|||||||
|
|
||||||
updatedData.updatedAt = new Date().toISOString();
|
updatedData.updatedAt = new Date().toISOString();
|
||||||
|
|
||||||
|
logger.debug(`[DEBUG] Final updatedData to save: ${JSON.stringify(updatedData, null, 2)}`);
|
||||||
|
logger.debug(`[DEBUG] Updating Redis key: ${this.ACCOUNT_KEY_PREFIX}${accountId}`);
|
||||||
|
|
||||||
await client.hset(
|
await client.hset(
|
||||||
`${this.ACCOUNT_KEY_PREFIX}${accountId}`,
|
`${this.ACCOUNT_KEY_PREFIX}${accountId}`,
|
||||||
updatedData
|
updatedData
|
||||||
|
|||||||
@@ -21,28 +21,11 @@ class ClaudeConsoleRelayService {
|
|||||||
|
|
||||||
logger.info(`📤 Processing Claude Console API request for key: ${apiKeyData.name || apiKeyData.id}, account: ${account.name} (${accountId})`);
|
logger.info(`📤 Processing Claude Console API request for key: ${apiKeyData.name || apiKeyData.id}, account: ${account.name} (${accountId})`);
|
||||||
logger.debug(`🌐 Account API URL: ${account.apiUrl}`);
|
logger.debug(`🌐 Account API URL: ${account.apiUrl}`);
|
||||||
|
logger.debug(`🔍 Account supportedModels: ${JSON.stringify(account.supportedModels)}`);
|
||||||
|
logger.debug(`🔑 Account has apiKey: ${!!account.apiKey}`);
|
||||||
|
logger.debug(`📝 Request model: ${requestBody.model}`);
|
||||||
|
|
||||||
// 检查模型支持
|
// 模型兼容性检查已经在调度器中完成,这里不需要再检查
|
||||||
if (account.supportedModels && account.supportedModels.length > 0) {
|
|
||||||
const requestedModel = requestBody.model;
|
|
||||||
if (requestedModel && !account.supportedModels.includes(requestedModel)) {
|
|
||||||
logger.warn(`🚫 Model not supported by Claude Console account ${account.name}: ${requestedModel}`);
|
|
||||||
|
|
||||||
// 标记账户为blocked
|
|
||||||
await claudeConsoleAccountService.blockAccount(accountId, `Model ${requestedModel} not supported`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
error: {
|
|
||||||
type: 'invalid_request_error',
|
|
||||||
message: `Model ${requestedModel} is not supported by this account`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建代理agent
|
// 创建代理agent
|
||||||
const proxyAgent = claudeConsoleAccountService._createProxyAgent(account.proxy);
|
const proxyAgent = claudeConsoleAccountService._createProxyAgent(account.proxy);
|
||||||
@@ -73,6 +56,12 @@ class ClaudeConsoleRelayService {
|
|||||||
: `${cleanUrl}/v1/messages`;
|
: `${cleanUrl}/v1/messages`;
|
||||||
|
|
||||||
logger.debug(`🎯 Final API endpoint: ${apiEndpoint}`);
|
logger.debug(`🎯 Final API endpoint: ${apiEndpoint}`);
|
||||||
|
logger.debug(`[DEBUG] Options passed to relayRequest: ${JSON.stringify(options)}`);
|
||||||
|
logger.debug(`[DEBUG] Client headers received: ${JSON.stringify(clientHeaders)}`);
|
||||||
|
|
||||||
|
// 过滤客户端请求头
|
||||||
|
const filteredHeaders = this._filterClientHeaders(clientHeaders);
|
||||||
|
logger.debug(`[DEBUG] Filtered client headers: ${JSON.stringify(filteredHeaders)}`);
|
||||||
|
|
||||||
// 准备请求配置
|
// 准备请求配置
|
||||||
const requestConfig = {
|
const requestConfig = {
|
||||||
@@ -84,7 +73,7 @@ class ClaudeConsoleRelayService {
|
|||||||
'x-api-key': account.apiKey,
|
'x-api-key': account.apiKey,
|
||||||
'anthropic-version': '2023-06-01',
|
'anthropic-version': '2023-06-01',
|
||||||
'User-Agent': account.userAgent || this.defaultUserAgent,
|
'User-Agent': account.userAgent || this.defaultUserAgent,
|
||||||
...this._filterClientHeaders(clientHeaders)
|
...filteredHeaders
|
||||||
},
|
},
|
||||||
httpsAgent: proxyAgent,
|
httpsAgent: proxyAgent,
|
||||||
timeout: config.proxy.timeout || 60000,
|
timeout: config.proxy.timeout || 60000,
|
||||||
@@ -92,12 +81,18 @@ class ClaudeConsoleRelayService {
|
|||||||
validateStatus: () => true // 接受所有状态码
|
validateStatus: () => true // 接受所有状态码
|
||||||
};
|
};
|
||||||
|
|
||||||
|
logger.debug(`[DEBUG] Initial headers before beta: ${JSON.stringify(requestConfig.headers, null, 2)}`);
|
||||||
|
|
||||||
// 添加beta header如果需要
|
// 添加beta header如果需要
|
||||||
if (options.betaHeader) {
|
if (options.betaHeader) {
|
||||||
|
logger.debug(`[DEBUG] Adding beta header: ${options.betaHeader}`);
|
||||||
requestConfig.headers['anthropic-beta'] = options.betaHeader;
|
requestConfig.headers['anthropic-beta'] = options.betaHeader;
|
||||||
|
} else {
|
||||||
|
logger.debug(`[DEBUG] No beta header to add`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
|
logger.debug(`📤 Sending request to Claude Console API with headers:`, JSON.stringify(requestConfig.headers, null, 2));
|
||||||
const response = await axios(requestConfig);
|
const response = await axios(requestConfig);
|
||||||
|
|
||||||
// 移除监听器(请求成功完成)
|
// 移除监听器(请求成功完成)
|
||||||
@@ -109,6 +104,10 @@ class ClaudeConsoleRelayService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`🔗 Claude Console API response: ${response.status}`);
|
logger.debug(`🔗 Claude Console API response: ${response.status}`);
|
||||||
|
logger.debug(`[DEBUG] Response headers: ${JSON.stringify(response.headers)}`);
|
||||||
|
logger.debug(`[DEBUG] Response data type: ${typeof response.data}`);
|
||||||
|
logger.debug(`[DEBUG] Response data length: ${response.data ? (typeof response.data === 'string' ? response.data.length : JSON.stringify(response.data).length) : 0}`);
|
||||||
|
logger.debug(`[DEBUG] Response data preview: ${typeof response.data === 'string' ? response.data.substring(0, 200) : JSON.stringify(response.data).substring(0, 200)}`);
|
||||||
|
|
||||||
// 检查是否为限流错误
|
// 检查是否为限流错误
|
||||||
if (response.status === 429) {
|
if (response.status === 429) {
|
||||||
@@ -125,10 +124,13 @@ class ClaudeConsoleRelayService {
|
|||||||
// 更新最后使用时间
|
// 更新最后使用时间
|
||||||
await this._updateLastUsedTime(accountId);
|
await this._updateLastUsedTime(accountId);
|
||||||
|
|
||||||
|
const responseBody = typeof response.data === 'string' ? response.data : JSON.stringify(response.data);
|
||||||
|
logger.debug(`[DEBUG] Final response body to return: ${responseBody}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: response.status,
|
statusCode: response.status,
|
||||||
headers: response.headers,
|
headers: response.headers,
|
||||||
body: typeof response.data === 'string' ? response.data : JSON.stringify(response.data),
|
body: responseBody,
|
||||||
accountId
|
accountId
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,13 +143,7 @@ class ClaudeConsoleRelayService {
|
|||||||
|
|
||||||
logger.error('❌ Claude Console Claude relay request failed:', error.message);
|
logger.error('❌ Claude Console Claude relay request failed:', error.message);
|
||||||
|
|
||||||
// 检查是否是模型不支持导致的错误
|
// 不再因为模型不支持而block账号
|
||||||
if (error.response && error.response.data && error.response.data.error) {
|
|
||||||
const errorMessage = error.response.data.error.message || '';
|
|
||||||
if (errorMessage.includes('model') && errorMessage.includes('not supported')) {
|
|
||||||
await claudeConsoleAccountService.blockAccount(accountId, errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -165,28 +161,7 @@ class ClaudeConsoleRelayService {
|
|||||||
logger.info(`📡 Processing streaming Claude Console API request for key: ${apiKeyData.name || apiKeyData.id}, account: ${account.name} (${accountId})`);
|
logger.info(`📡 Processing streaming Claude Console API request for key: ${apiKeyData.name || apiKeyData.id}, account: ${account.name} (${accountId})`);
|
||||||
logger.debug(`🌐 Account API URL: ${account.apiUrl}`);
|
logger.debug(`🌐 Account API URL: ${account.apiUrl}`);
|
||||||
|
|
||||||
// 检查模型支持
|
// 模型兼容性检查已经在调度器中完成,这里不需要再检查
|
||||||
if (account.supportedModels && account.supportedModels.length > 0) {
|
|
||||||
const requestedModel = requestBody.model;
|
|
||||||
if (requestedModel && !account.supportedModels.includes(requestedModel)) {
|
|
||||||
logger.warn(`🚫 Model not supported by Claude Console account ${account.name}: ${requestedModel}`);
|
|
||||||
|
|
||||||
// 标记账户为blocked
|
|
||||||
await claudeConsoleAccountService.blockAccount(accountId, `Model ${requestedModel} not supported`);
|
|
||||||
|
|
||||||
// 对于流式响应,需要写入错误并结束流
|
|
||||||
const errorResponse = JSON.stringify({
|
|
||||||
error: {
|
|
||||||
type: 'invalid_request_error',
|
|
||||||
message: `Model ${requestedModel} is not supported by this account`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
responseStream.writeHead(400, { 'Content-Type': 'application/json' });
|
|
||||||
responseStream.end(errorResponse);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建代理agent
|
// 创建代理agent
|
||||||
const proxyAgent = claudeConsoleAccountService._createProxyAgent(account.proxy);
|
const proxyAgent = claudeConsoleAccountService._createProxyAgent(account.proxy);
|
||||||
@@ -355,13 +330,7 @@ class ClaudeConsoleRelayService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查错误
|
// 不再因为模型不支持而block账号
|
||||||
if (data.type === 'error' && data.error) {
|
|
||||||
const errorMessage = data.error.message || '';
|
|
||||||
if (errorMessage.includes('model') && errorMessage.includes('not supported')) {
|
|
||||||
claudeConsoleAccountService.blockAccount(accountId, errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 忽略解析错误
|
// 忽略解析错误
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class UnifiedClaudeScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 🎯 统一调度Claude账号(官方和Console)
|
// 🎯 统一调度Claude账号(官方和Console)
|
||||||
async selectAccountForApiKey(apiKeyData, sessionHash = null) {
|
async selectAccountForApiKey(apiKeyData, sessionHash = null, requestedModel = null) {
|
||||||
try {
|
try {
|
||||||
// 如果API Key绑定了专属账户,优先使用
|
// 如果API Key绑定了专属账户,优先使用
|
||||||
if (apiKeyData.claudeAccountId) {
|
if (apiKeyData.claudeAccountId) {
|
||||||
@@ -41,12 +41,17 @@ class UnifiedClaudeScheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有可用账户
|
// 获取所有可用账户(传递请求的模型进行过滤)
|
||||||
const availableAccounts = await this._getAllAvailableAccounts(apiKeyData);
|
const availableAccounts = await this._getAllAvailableAccounts(apiKeyData, requestedModel);
|
||||||
|
|
||||||
if (availableAccounts.length === 0) {
|
if (availableAccounts.length === 0) {
|
||||||
|
// 提供更详细的错误信息
|
||||||
|
if (requestedModel) {
|
||||||
|
throw new Error(`No available Claude accounts support the requested model: ${requestedModel}`);
|
||||||
|
} else {
|
||||||
throw new Error('No available Claude accounts (neither official nor console)');
|
throw new Error('No available Claude accounts (neither official nor console)');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 按优先级和最后使用时间排序
|
// 按优先级和最后使用时间排序
|
||||||
const sortedAccounts = this._sortAccountsByPriority(availableAccounts);
|
const sortedAccounts = this._sortAccountsByPriority(availableAccounts);
|
||||||
@@ -73,7 +78,7 @@ class UnifiedClaudeScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 📋 获取所有可用账户(合并官方和Console)
|
// 📋 获取所有可用账户(合并官方和Console)
|
||||||
async _getAllAvailableAccounts(apiKeyData) {
|
async _getAllAvailableAccounts(apiKeyData, requestedModel = null) {
|
||||||
const availableAccounts = [];
|
const availableAccounts = [];
|
||||||
|
|
||||||
// 如果API Key绑定了专属Claude账户,优先返回
|
// 如果API Key绑定了专属Claude账户,优先返回
|
||||||
@@ -130,6 +135,14 @@ class UnifiedClaudeScheduler {
|
|||||||
account.status === 'active' &&
|
account.status === 'active' &&
|
||||||
account.accountType === 'shared') {
|
account.accountType === 'shared') {
|
||||||
|
|
||||||
|
// 检查模型支持(如果有请求的模型)
|
||||||
|
if (requestedModel && account.supportedModels && account.supportedModels.length > 0) {
|
||||||
|
if (!account.supportedModels.includes(requestedModel)) {
|
||||||
|
logger.info(`🚫 Claude Console account ${account.name} does not support model ${requestedModel}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否被限流
|
// 检查是否被限流
|
||||||
const isRateLimited = await claudeConsoleAccountService.isAccountRateLimited(account.id);
|
const isRateLimited = await claudeConsoleAccountService.isAccountRateLimited(account.id);
|
||||||
if (!isRateLimited) {
|
if (!isRateLimited) {
|
||||||
|
|||||||
Reference in New Issue
Block a user