diff --git a/public/index.html b/public/index.html index 9e086407c..f4eed7b28 100644 --- a/public/index.html +++ b/public/index.html @@ -3266,12 +3266,6 @@ - -
- - -
-
diff --git a/public/scripts/extensions/shared.js b/public/scripts/extensions/shared.js index 6a73d1cf3..c68d6c575 100644 --- a/public/scripts/extensions/shared.js +++ b/public/scripts/extensions/shared.js @@ -180,13 +180,10 @@ function throwIfInvalidModel(useReverseProxy) { throw new Error('Google Vertex AI API key is not set for Express mode.'); } } else if (authMode === 'full') { - // Full mode requires Service Account JSON and project settings + // Full mode requires Service Account JSON and region settings if (!secret_state[SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]) { throw new Error('Service Account JSON is required for Vertex AI Full mode. Please validate and save your Service Account JSON.'); } - if (!secret_state[SECRET_KEYS.VERTEXAI_PROJECT_ID]) { - throw new Error('Project ID is required for Vertex AI Full mode.'); - } if (!oai_settings.vertexai_region) { throw new Error('Region is required for Vertex AI Full mode.'); } diff --git a/public/scripts/openai.js b/public/scripts/openai.js index d3c3edab0..23fbdeadd 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -3496,8 +3496,6 @@ function loadOpenAISettings(data, settings) { $('#claude_use_sysprompt').prop('checked', oai_settings.claude_use_sysprompt); $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt); $('#vertexai_auth_mode').val(oai_settings.vertexai_auth_mode); - // Don't display Project ID in input - it's stored in backend secrets - $('#vertexai_project_id').val(''); $('#vertexai_region').val(oai_settings.vertexai_region); // Don't display Service Account JSON in textarea - it's stored in backend secrets $('#vertexai_service_account_json').val(''); @@ -5015,19 +5013,7 @@ async function onConnectButtonClick(e) { } } else { // Full version - use service account - // Check if we have a saved project ID, otherwise use the input value - const savedProjectId = secret_state[SECRET_KEYS.VERTEXAI_PROJECT_ID]; - const inputProjectId = String($('#vertexai_project_id').val()).trim(); - - if (!savedProjectId && !inputProjectId) { - toastr.error(t`Project ID is required for Vertex AI full version`); - return; - } - - // Save project ID to secrets if we have an input value - if (inputProjectId.length) { - await writeSecret(SECRET_KEYS.VERTEXAI_PROJECT_ID, inputProjectId); - } + // Project ID will be extracted from the Service Account JSON // Check if service account JSON is saved in backend if (!secret_state[SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]) { diff --git a/public/scripts/secrets.js b/public/scripts/secrets.js index b72beb46b..488acb22e 100644 --- a/public/scripts/secrets.js +++ b/public/scripts/secrets.js @@ -44,7 +44,6 @@ export const SECRET_KEYS = { SERPER: 'api_key_serper', FALAI: 'api_key_falai', XAI: 'api_key_xai', - VERTEXAI_PROJECT_ID: 'vertexai_project_id', VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json', }; diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index e7b2979f8..767125af3 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -43,7 +43,7 @@ import { webTokenizers, getWebTokenizer, } from '../tokenizers.js'; -import { getVertexAIAuth } from '../google.js'; +import { getVertexAIAuth, getProjectIdFromServiceAccount } from '../google.js'; const API_OPENAI = 'https://api.openai.com/v1'; const API_CLAUDE = 'https://api.anthropic.com/v1'; @@ -528,10 +528,19 @@ async function sendMakerSuiteRequest(request, response) { url = `${apiUrl.toString().replace(/\/$/, '')}/v1/publishers/google/models/${model}:${responseType}?key=${keyParam}${stream ? '&alt=sse' : ''}`; } else if (authType === 'full') { // For Full mode (service account authentication), use project-specific URL - // Only use project ID from secrets - const projectId = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_PROJECT_ID); - if (!projectId) { - console.warn('Vertex AI project ID is missing.'); + // Get project ID from Service Account JSON + const serviceAccountJson = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT); + if (!serviceAccountJson) { + console.warn('Vertex AI Service Account JSON is missing.'); + return response.status(400).send({ error: true }); + } + + let projectId; + try { + const serviceAccount = JSON.parse(serviceAccountJson); + projectId = getProjectIdFromServiceAccount(serviceAccount); + } catch (error) { + console.error('Failed to extract project ID from Service Account JSON:', error); return response.status(400).send({ error: true }); } const region = request.body.vertexai_region || 'us-central1'; diff --git a/src/endpoints/google.js b/src/endpoints/google.js index 9d06683c0..b0c24557f 100644 --- a/src/endpoints/google.js +++ b/src/endpoints/google.js @@ -107,6 +107,25 @@ export async function getAccessToken(jwtToken) { return data.access_token; } +/** + * Extracts the project ID from a Service Account JSON object. + * @param {object} serviceAccount Service account JSON object + * @returns {string} Project ID + * @throws {Error} If project ID is not found in the service account + */ +export function getProjectIdFromServiceAccount(serviceAccount) { + if (!serviceAccount || typeof serviceAccount !== 'object') { + throw new Error('Invalid service account object'); + } + + const projectId = serviceAccount.project_id; + if (!projectId || typeof projectId !== 'string') { + throw new Error('Project ID not found in service account JSON'); + } + + return projectId; +} + export const router = express.Router(); router.post('/caption-image', async (request, response) => { @@ -133,9 +152,19 @@ router.post('/caption-image', async (request, response) => { url = `${apiUrl.origin}/v1/publishers/google/models/${model}:generateContent?key=${keyParam}`; } else if (authType === 'full') { // Full mode: use project-specific URL with Authorization header - const projectId = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_PROJECT_ID); - if (!projectId) { - console.warn('Vertex AI project ID is missing.'); + // Get project ID from Service Account JSON + const serviceAccountJson = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT); + if (!serviceAccountJson) { + console.warn('Vertex AI Service Account JSON is missing.'); + return response.status(400).send({ error: true }); + } + + let projectId; + try { + const serviceAccount = JSON.parse(serviceAccountJson); + projectId = getProjectIdFromServiceAccount(serviceAccount); + } catch (error) { + console.error('Failed to extract project ID from Service Account JSON:', error); return response.status(400).send({ error: true }); } const region = request.body.vertexai_region || 'us-central1'; diff --git a/src/endpoints/secrets.js b/src/endpoints/secrets.js index 237eda570..d1f015b42 100644 --- a/src/endpoints/secrets.js +++ b/src/endpoints/secrets.js @@ -54,7 +54,6 @@ export const SECRET_KEYS = { DEEPSEEK: 'api_key_deepseek', SERPER: 'api_key_serper', XAI: 'api_key_xai', - VERTEXAI_PROJECT_ID: 'vertexai_project_id', VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json', };