Add region option for vertex express mode to access more models (#4155) * add region option for vertex express mode to access more models * add region option for vertex express mode to access more models * fix: Consolidate duplicate 'vertexai_region' input * feat: add vertex region suggestion datalist & update caption endpoint to correctly handle region * Adjust global projectless Vertex endpoint * Remove pointless slash trim * Skill issue --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

083f92b2b2518c52f2fb44004792d4cd04f4ea7c

Compass <32301767+AliceRabbit@users.noreply.github.com>

Signed
5 files changed, +88 -19Showing whitespace changes
public/index.html+57 -16
@@ -3257,13 +3257,16 @@
32573257 <!-- Express Mode Configuration -->
32583258 <div id="vertexai_express_config" class="vertexai-auth-section" data-mode="express">
32593259 <h4>
32603260 <span data-i18n="Google Vertex AI API KeyConfiguration">
32613261 Google Vertex AI API KeyConfiguration
32623262 </span>
32633263 <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview" data-i18n="(Express mode)" target="_blank" rel="noopener noreferrer">
32643264 (Express mode)
32653265 </a>
32663266 </h4>
3267+ <!-- API Key -->
3268+ <div class="flex-container flexFlowColumn">
3269+ <label for="api_key_vertexai" data-i18n="API Key">API Key:</label>
32673270 <div class="flex-container">
32683271 <input id="api_key_vertexai" name="api_key_vertexai" class="text_pole flex1" value="" type="text" autocomplete="off">
32693272 <div title="Manage API keys" data-i18n="[title]Manage API keys" class="menu_button fa-solid fa-key fa-fw manage-api-keys" data-key="api_key_vertexai"></div>
@@ -3272,6 +3275,22 @@
32723275 For privacy reasons, your API key will be hidden after you click 'Connect'.
32733276 </div>
32743277 </div>
3278+ <!-- Project ID -->
3279+ <div class="flex-container flexFlowColumn marginTop10">
3280+ <label for="vertexai_express_project_id">
3281+ <span data-i18n="Project ID">Project ID:</span>
3282+ </label>
3283+ <div class="flex-container">
3284+ <input id="vertexai_express_project_id" name="vertexai_express_project_id" class="text_pole flex1" value="" type="text" autocomplete="off" placeholder="Enter your Project ID">
3285+ </div>
3286+ <div class="toggle-description justifyLeft marginBot5">
3287+ <small data-i18n="Project ID is required when selecting regions other than the default (us-central1). You can find this in a model 404 error message.">
3288+ `Project ID` is only required when selecting regions other than the default (us-central1).<br>
3289+ You can find this in a model 404 error message.
3290+ </small>
3291+ </div>
3292+ </div>
3293+ </div>
32753294
32763295 <!-- Full Version Configuration -->
32773296 <div id="vertexai_full_config" class="vertexai-auth-section" data-mode="full">
@@ -3283,20 +3302,6 @@
32833302 <i class="fa-solid fa-circle-question"></i>
32843303 </a>
32853304 </h4>
3286-
3287- <!-- Region -->
3288- <div class="flex-container flexFlowColumn">
3289- <label for="vertexai_region">
3290- <span data-i18n="Region">
3291- Region:
3292- </span>
3293- <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations" target="_blank" rel="noopener noreferrer" title="View available regions and models" data-i18n="[title]View available regions and models" class="notes-link">
3294- <span class="fa-solid fa-circle-question note-link-span"></span>
3295- </a>
3296- </label>
3297- <input id="vertexai_region" name="vertexai_region" class="text_pole flex1" value="us-central1" type="text" autocomplete="off" placeholder="e.g., global, us-central1, europe-west1, asia-northeast1">
3298- </div>
3299-
33003305 <!-- Service Account JSON Content -->
33013306 <div class="flex-container flexFlowColumn">
33023307 <label for="vertexai_service_account_json" data-i18n="Service Account JSON Content">Service Account JSON Content:</label>
@@ -3316,12 +3321,48 @@
33163321 </div>
33173322 </div>
33183323
3324+ <!-- Region -->
3325+ <div class="flex-container flexFlowColumn">
3326+ <label for="vertexai_region">
3327+ <span data-i18n="Region">
3328+ Region:
3329+ </span>
3330+ <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations" target="_blank" rel="noopener noreferrer" title="View available regions and models" data-i18n="[title]View available regions and models" class="notes-link">
3331+ <span class="fa-solid fa-circle-question note-link-span"></span>
3332+ </a>
3333+ </label>
3334+ <input id="vertexai_region" name="vertexai_region" class="text_pole flex1" value="us-central1" type="text" autocomplete="off" placeholder="e.g., global, us-central1, europe-west1, asia-northeast1" list="vertexai_region_suggestions">
3335+
3336+ <datalist id="vertexai_region_suggestions">
3337+ <option value="global">global</option>
3338+ <option value="us-central1">us-central1</option>
3339+ <option value="us-east1">us-east1</option>
3340+ <option value="us-east4">us-east4</option>
3341+ <option value="us-west1">us-west1</option>
3342+ <option value="us-west2">us-west2</option>
3343+ <option value="us-west3">us-west3</option>
3344+ <option value="us-west4">us-west4</option>
3345+ <option value="europe-west1">europe-west1</option>
3346+ <option value="europe-west2">europe-west2</option>
3347+ <option value="europe-west3">europe-west3</option>
3348+ <option value="europe-west4">europe-west4</option>
3349+ <option value="europe-west6">europe-west6</option>
3350+ <option value="europe-central2">europe-central2</option>
3351+ <option value="asia-northeast1">asia-northeast1</option>
3352+ <option value="asia-northeast3">asia-northeast3</option>
3353+ <option value="asia-southeast1">asia-southeast1</option>
3354+ <option value="asia-south1">asia-south1</option>
3355+ <option value="australia-southeast1">australia-southeast1</option>
3356+ </datalist>
3357+ </div>
3358+
33193359 <!-- Model Selection -->
33203360 <div>
33213361 <h4 data-i18n="Google Model">Google Model</h4>
33223362 <select id="model_vertexai_select">
33233363 <!-- Express Mode Models -->
33243364 <optgroup id="vertexai_express_models" label="Express Mode Models" data-mode="express">
3365+ <option value="gemini-2.5-pro-preview-06-05">gemini-2.5-pro-preview-06-05 (global region, Project ID required)</option>
33253366 <option value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>
33263367 <option value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
33273368 <option value="gemini-2.5-flash-preview-05-20">gemini-2.5-flash-preview-05-20</option>
public/scripts/extensions/shared.js+1 -0
@@ -60,6 +60,7 @@ export async function getMultimodalCaption(base64Img, prompt) {
6060 if (extension_settings.caption.multimodal_api === 'vertexai') {
6161 requestBody.vertexai_auth_mode = oai_settings.vertexai_auth_mode;
6262 requestBody.vertexai_region = oai_settings.vertexai_region;
63+ requestBody.vertexai_express_project_id = oai_settings.vertexai_express_project_id;
6364 }
6465
6566 if (isOllama) {
public/scripts/openai.js+13 -0
@@ -240,6 +240,7 @@ const sensitiveFields = [
240240 'custom_exclude_body',
241241 'custom_include_headers',
242242 'vertexai_region',
243+ 'vertexai_express_project_id',
243244];
244245
245246/**
@@ -314,6 +315,7 @@ export const settingsToUpdate = {
314315 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false],
315316 vertexai_auth_mode: ['#vertexai_auth_mode', 'vertexai_auth_mode', false, true],
316317 vertexai_region: ['#vertexai_region', 'vertexai_region', false, true],
318+ vertexai_express_project_id: ['#vertexai_express_project_id', 'vertexai_express_project_id', false, true],
317319 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true],
318320 squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false],
319321 image_inlining: ['#openai_image_inlining', 'image_inlining', true, false],
@@ -399,6 +401,7 @@ const default_settings = {
399401 use_makersuite_sysprompt: true,
400402 vertexai_auth_mode: 'express',
401403 vertexai_region: 'us-central1',
404+ vertexai_express_project_id: '',
402405 use_alt_scale: false,
403406 squash_system_messages: false,
404407 image_inlining: false,
@@ -487,6 +490,7 @@ const oai_settings = {
487490 use_makersuite_sysprompt: true,
488491 vertexai_auth_mode: 'express',
489492 vertexai_region: 'us-central1',
493+ vertexai_express_project_id: '',
490494 use_alt_scale: false,
491495 squash_system_messages: false,
492496 image_inlining: false,
@@ -2319,6 +2323,7 @@ async function sendOpenAIRequest(type, messages, signal) {
23192323 if (isVertexAI) {
23202324 generate_data['vertexai_auth_mode'] = oai_settings.vertexai_auth_mode;
23212325 generate_data['vertexai_region'] = oai_settings.vertexai_region;
2326+ generate_data['vertexai_express_project_id'] = oai_settings.vertexai_express_project_id;
23222327 }
23232328 }
23242329
@@ -3552,6 +3557,7 @@ function loadOpenAISettings(data, settings) {
35523557 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
35533558 oai_settings.video_inlining = settings.video_inlining ?? default_settings.video_inlining;
35543559 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;
3560+ oai_settings.vertexai_express_project_id = settings.vertexai_express_project_id ?? default_settings.vertexai_express_project_id;
35553561 oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;
35563562 oai_settings.reasoning_effort = settings.reasoning_effort ?? default_settings.reasoning_effort;
35573563 oai_settings.enable_web_search = settings.enable_web_search ?? default_settings.enable_web_search;
@@ -3589,6 +3595,7 @@ function loadOpenAISettings(data, settings) {
35893595 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
35903596 if (settings.vertexai_auth_mode !== undefined) oai_settings.vertexai_auth_mode = settings.vertexai_auth_mode;
35913597 if (settings.vertexai_region !== undefined) oai_settings.vertexai_region = settings.vertexai_region;
3598+ if (settings.vertexai_express_project_id !== undefined) oai_settings.vertexai_express_project_id = settings.vertexai_express_project_id;
35923599 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
35933600 $('#stream_toggle').prop('checked', oai_settings.stream_openai);
35943601 $('#api_url_scale').val(oai_settings.api_url_scale);
@@ -3650,6 +3657,7 @@ function loadOpenAISettings(data, settings) {
36503657 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
36513658 $('#vertexai_auth_mode').val(oai_settings.vertexai_auth_mode);
36523659 $('#vertexai_region').val(oai_settings.vertexai_region);
3660+ $('#vertexai_express_project_id').val(oai_settings.vertexai_express_project_id);
36533661 // Don't display Service Account JSON in textarea - it's stored in backend secrets
36543662 $('#vertexai_service_account_json').val('');
36553663 updateVertexAIServiceAccountStatus();
@@ -3980,6 +3988,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
39803988 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
39813989 vertexai_auth_mode: settings.vertexai_auth_mode,
39823990 vertexai_region: settings.vertexai_region,
3991+ vertexai_express_project_id: settings.vertexai_express_project_id,
39833992 use_alt_scale: settings.use_alt_scale,
39843993 squash_system_messages: settings.squash_system_messages,
39853994 image_inlining: settings.image_inlining,
@@ -6378,6 +6387,10 @@ export function initOpenAI() {
63786387 oai_settings.vertexai_region = String($(this).val());
63796388 saveSettingsDebounced();
63806389 });
6390+ $('#vertexai_express_project_id').on('input', function () {
6391+ oai_settings.vertexai_express_project_id = String($(this).val());
6392+ saveSettingsDebounced();
6393+ });
63816394 $('#vertexai_service_account_json').on('input', onVertexAIServiceAccountJsonChange);
63826395 $('#vertexai_validate_service_account').on('click', onVertexAIValidateServiceAccount);
63836396 $('#vertexai_clear_service_account').on('click', onVertexAIClearServiceAccount);
src/endpoints/backends/chat-completions.js+8 -1
@@ -493,7 +493,14 @@ async function sendMakerSuiteRequest(request, response) {
493493 if (authType === 'express') {
494494 // For Express mode (API key authentication), use the key parameter
495495 const keyParam = authHeader.replace('Bearer ', '');
496- url = `${apiUrl.toString().replace(/\/$/, '')}/v1/publishers/google/models/${model}:${responseType}?key=${keyParam}${stream ? '&alt=sse' : ''}`;
496+ const region = request.body.vertexai_region || 'us-central1';
497+ const projectId = request.body.vertexai_express_project_id;
498+ const baseUrl = region === 'global'
499+ ? 'https://aiplatform.googleapis.com'
500+ : `https://${region}-aiplatform.googleapis.com`;
501+ url = projectId
502+ ? `https://aiplatform.googleapis.com/v1/projects/${projectId}/locations/${region}/publishers/google/models/${model}:generateContent?key=${keyParam}${stream ? '&alt=sse' : ''}`
503+ : `${baseUrl}/v1/publishers/google/models/${model}:generateContent?key=${keyParam}${stream ? '&alt=sse' : ''}`;
497504 } else if (authType === 'full') {
498505 // For Full mode (service account authentication), use project-specific URL
499506 // Get project ID from Service Account JSON
src/endpoints/google.js+9 -2
@@ -104,6 +104,7 @@ export async function getAccessToken(jwtToken) {
104104 throw new Error(`Failed to get access token: ${error}`);
105105 }
106106
107+ /** @type {any} */
107108 const data = await response.json();
108109 return data.access_token;
109110}
@@ -149,8 +150,14 @@ router.post('/caption-image', async (request, response) => {
149150 if (authType === 'express') {
150151 // Express mode: use API key parameter
151152 const keyParam = authHeader.replace('Bearer ', '');
152153 const apiUrlregion = trimTrailingSlash(request.body.reverse_proxyvertexai_region || API_VERTEX_AI)'us-central1';
153- url = `${apiUrl}/v1/publishers/google/models/${model}:generateContent?key=${keyParam}`;
154+ const projectId = request.body.vertexai_express_project_id;
155+ const baseUrl = region === 'global'
156+ ? 'https://aiplatform.googleapis.com'
157+ : `https://${region}-aiplatform.googleapis.com`;
158+ url = projectId
159+ ? `https://aiplatform.googleapis.com/v1/projects/${projectId}/locations/${region}/publishers/google/models/${model}:generateContent?key=${keyParam}`
160+ : `${baseUrl}/v1/publishers/google/models/${model}:generateContent?key=${keyParam}`;
154161 } else if (authType === 'full') {
155162 // Full mode: use project-specific URL with Authorization header
156163 // Get project ID from Service Account JSON