From b7da43f615cf574b5b7f71831abcb168a36aa71e Mon Sep 17 00:00:00 2001 From: shaw Date: Fri, 12 Sep 2025 11:41:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E8=BD=AC=E5=8F=91gemini=20api=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/geminiRoutes.js | 8 +++ src/routes/standardGeminiRoutes.js | 82 +++++++++++++++++++++++++--- src/services/geminiAccountService.js | 27 +++++++++ 3 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/routes/geminiRoutes.js b/src/routes/geminiRoutes.js index ce06d121..7aebc7ae 100644 --- a/src/routes/geminiRoutes.js +++ b/src/routes/geminiRoutes.js @@ -366,6 +366,14 @@ async function handleLoadCodeAssist(req, res) { proxyConfig ) + // 如果响应中包含 cloudaicompanionProject,保存到账户作为临时项目 ID + if (response.cloudaicompanionProject && !account.projectId) { + await geminiAccountService.updateTempProjectId(accountId, response.cloudaicompanionProject) + logger.info( + `📋 Cached temporary projectId from loadCodeAssist: ${response.cloudaicompanionProject}` + ) + } + res.json(response) } catch (error) { const version = req.path.includes('v1beta') ? 'v1beta' : 'v1internal' diff --git a/src/routes/standardGeminiRoutes.js b/src/routes/standardGeminiRoutes.js index c95c8bc6..8d049574 100644 --- a/src/routes/standardGeminiRoutes.js +++ b/src/routes/standardGeminiRoutes.js @@ -102,13 +102,46 @@ async function handleStandardGenerateContent(req, res) { const client = await geminiAccountService.getOauthClient(accessToken, refreshToken, proxyConfig) - // 使用账户的项目ID(如果有的话) - const effectiveProjectId = account.projectId || null + // 项目ID优先级:账户配置的项目ID > 临时项目ID > 尝试获取 + let effectiveProjectId = account.projectId || account.tempProjectId || null + + // 如果没有任何项目ID,尝试调用 loadCodeAssist 获取 + if (!effectiveProjectId) { + try { + logger.info('📋 No projectId available, attempting to fetch from loadCodeAssist...') + const loadResponse = await geminiAccountService.loadCodeAssist(client, null, proxyConfig) + + if (loadResponse.cloudaicompanionProject) { + effectiveProjectId = loadResponse.cloudaicompanionProject + // 保存临时项目ID + await geminiAccountService.updateTempProjectId(accountId, effectiveProjectId) + logger.info(`📋 Fetched and cached temporary projectId: ${effectiveProjectId}`) + } + } catch (loadError) { + logger.warn('Failed to fetch projectId from loadCodeAssist:', loadError.message) + } + } + + // 如果还是没有项目ID,返回错误 + if (!effectiveProjectId) { + return res.status(403).json({ + error: { + message: + 'This account requires a project ID to be configured. Please configure a project ID in the account settings.', + type: 'configuration_required' + } + }) + } logger.info('📋 Standard API 项目ID处理逻辑', { accountProjectId: account.projectId, + tempProjectId: account.tempProjectId, effectiveProjectId, - decision: account.projectId ? '使用账户配置' : '不使用项目ID' + decision: account.projectId + ? '使用账户配置' + : account.tempProjectId + ? '使用临时项目ID' + : '从loadCodeAssist获取' }) // 生成一个符合 Gemini CLI 格式的 user_prompt_id @@ -119,7 +152,7 @@ async function handleStandardGenerateContent(req, res) { client, { model, request: actualRequestData }, userPromptId, // 使用生成的 user_prompt_id - effectiveProjectId || 'oceanic-graph-cgcz4', // 如果没有项目ID,使用默认值 + effectiveProjectId, // 使用处理后的项目ID req.apiKey?.id, // 使用 API Key ID 作为 session ID proxyConfig ) @@ -288,13 +321,46 @@ async function handleStandardStreamGenerateContent(req, res) { const client = await geminiAccountService.getOauthClient(accessToken, refreshToken, proxyConfig) - // 使用账户的项目ID(如果有的话) - const effectiveProjectId = account.projectId || null + // 项目ID优先级:账户配置的项目ID > 临时项目ID > 尝试获取 + let effectiveProjectId = account.projectId || account.tempProjectId || null + + // 如果没有任何项目ID,尝试调用 loadCodeAssist 获取 + if (!effectiveProjectId) { + try { + logger.info('📋 No projectId available, attempting to fetch from loadCodeAssist...') + const loadResponse = await geminiAccountService.loadCodeAssist(client, null, proxyConfig) + + if (loadResponse.cloudaicompanionProject) { + effectiveProjectId = loadResponse.cloudaicompanionProject + // 保存临时项目ID + await geminiAccountService.updateTempProjectId(accountId, effectiveProjectId) + logger.info(`📋 Fetched and cached temporary projectId: ${effectiveProjectId}`) + } + } catch (loadError) { + logger.warn('Failed to fetch projectId from loadCodeAssist:', loadError.message) + } + } + + // 如果还是没有项目ID,返回错误 + if (!effectiveProjectId) { + return res.status(403).json({ + error: { + message: + 'This account requires a project ID to be configured. Please configure a project ID in the account settings.', + type: 'configuration_required' + } + }) + } logger.info('📋 Standard API 流式项目ID处理逻辑', { accountProjectId: account.projectId, + tempProjectId: account.tempProjectId, effectiveProjectId, - decision: account.projectId ? '使用账户配置' : '不使用项目ID' + decision: account.projectId + ? '使用账户配置' + : account.tempProjectId + ? '使用临时项目ID' + : '从loadCodeAssist获取' }) // 生成一个符合 Gemini CLI 格式的 user_prompt_id @@ -305,7 +371,7 @@ async function handleStandardStreamGenerateContent(req, res) { client, { model, request: actualRequestData }, userPromptId, // 使用生成的 user_prompt_id - effectiveProjectId || 'oceanic-graph-cgcz4', // 如果没有项目ID,使用默认值 + effectiveProjectId, // 使用处理后的项目ID req.apiKey?.id, // 使用 API Key ID 作为 session ID abortController.signal, proxyConfig diff --git a/src/services/geminiAccountService.js b/src/services/geminiAccountService.js index 63873bb5..cc28d5ac 100644 --- a/src/services/geminiAccountService.js +++ b/src/services/geminiAccountService.js @@ -394,6 +394,9 @@ async function createAccount(accountData) { // 项目 ID(Google Cloud/Workspace 账号需要) projectId: accountData.projectId || '', + // 临时项目 ID(从 loadCodeAssist 接口自动获取) + tempProjectId: accountData.tempProjectId || '', + // 支持的模型列表(可选) supportedModels: accountData.supportedModels || [], // 空数组表示支持所有模型 @@ -1426,6 +1429,29 @@ async function generateContentStream( return response.data // 返回流对象 } +// 更新账户的临时项目 ID +async function updateTempProjectId(accountId, tempProjectId) { + if (!tempProjectId) { + return + } + + try { + const account = await getAccount(accountId) + if (!account) { + logger.warn(`Account ${accountId} not found when updating tempProjectId`) + return + } + + // 只有在没有固定项目 ID 的情况下才更新临时项目 ID + if (!account.projectId && tempProjectId !== account.tempProjectId) { + await updateAccount(accountId, { tempProjectId }) + logger.info(`Updated tempProjectId for account ${accountId}: ${tempProjectId}`) + } + } catch (error) { + logger.error(`Failed to update tempProjectId for account ${accountId}:`, error) + } +} + module.exports = { generateAuthUrl, pollAuthorizationStatus, @@ -1454,6 +1480,7 @@ module.exports = { countTokens, generateContent, generateContentStream, + updateTempProjectId, OAUTH_CLIENT_ID, OAUTH_SCOPES }