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 @@
3257 <!-- Express Mode Configuration -->3257 <!-- Express Mode Configuration -->
3258 <div id="vertexai_express_config" class="vertexai-auth-section" data-mode="express">3258 <div id="vertexai_express_config" class="vertexai-auth-section" data-mode="express">
3259 <h4>3259 <h4>
3260 <span data-i18n="Google Vertex AI API Key">3260 <span data-i18n="Google Vertex AI Configuration">
3261 Google Vertex AI API Key3261 Google Vertex AI Configuration
3262 </span>3262 </span>
3263 <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview" data-i18n="(Express mode)" target="_blank" rel="noopener noreferrer">3263 <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview" data-i18n="(Express mode)" target="_blank" rel="noopener noreferrer">
3264 (Express mode)3264 (Express mode)
3265 </a>3265 </a>
3266 </h4>3266 </h4>
3267 <!-- API Key -->
3268 <div class="flex-container flexFlowColumn">
3269 <label for="api_key_vertexai" data-i18n="API Key">API Key:</label>
3267 <div class="flex-container">3270 <div class="flex-container">
3268 <input id="api_key_vertexai" name="api_key_vertexai" class="text_pole flex1" value="" type="text" autocomplete="off">3271 <input id="api_key_vertexai" name="api_key_vertexai" class="text_pole flex1" value="" type="text" autocomplete="off">
3269 <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 <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 @@
3272 For privacy reasons, your API key will be hidden after you click 'Connect'.3275 For privacy reasons, your API key will be hidden after you click 'Connect'.
3273 </div>3276 </div>
3274 </div>3277 </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
3276 <!-- Full Version Configuration -->3295 <!-- Full Version Configuration -->
3277 <div id="vertexai_full_config" class="vertexai-auth-section" data-mode="full">3296 <div id="vertexai_full_config" class="vertexai-auth-section" data-mode="full">
@@ -3283,20 +3302,6 @@
3283 <i class="fa-solid fa-circle-question"></i>3302 <i class="fa-solid fa-circle-question"></i>
3284 </a>3303 </a>
3285 </h4>3304 </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
3300 <!-- Service Account JSON Content -->3305 <!-- Service Account JSON Content -->
3301 <div class="flex-container flexFlowColumn">3306 <div class="flex-container flexFlowColumn">
3302 <label for="vertexai_service_account_json" data-i18n="Service Account JSON Content">Service Account JSON Content:</label>3307 <label for="vertexai_service_account_json" data-i18n="Service Account JSON Content">Service Account JSON Content:</label>
@@ -3316,12 +3321,48 @@
3316 </div>3321 </div>
3317 </div>3322 </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
3319 <!-- Model Selection -->3359 <!-- Model Selection -->
3320 <div>3360 <div>
3321 <h4 data-i18n="Google Model">Google Model</h4>3361 <h4 data-i18n="Google Model">Google Model</h4>
3322 <select id="model_vertexai_select">3362 <select id="model_vertexai_select">
3323 <!-- Express Mode Models -->3363 <!-- Express Mode Models -->
3324 <optgroup id="vertexai_express_models" label="Express Mode Models" data-mode="express">3364 <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>
3325 <option value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>3366 <option value="gemini-2.5-pro-preview-05-06">gemini-2.5-pro-preview-05-06</option>
3326 <option value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>3367 <option value="gemini-2.5-pro-preview-03-25">gemini-2.5-pro-preview-03-25</option>
3327 <option value="gemini-2.5-flash-preview-05-20">gemini-2.5-flash-preview-05-20</option>3368 <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) {
60 if (extension_settings.caption.multimodal_api === 'vertexai') {60 if (extension_settings.caption.multimodal_api === 'vertexai') {
61 requestBody.vertexai_auth_mode = oai_settings.vertexai_auth_mode;61 requestBody.vertexai_auth_mode = oai_settings.vertexai_auth_mode;
62 requestBody.vertexai_region = oai_settings.vertexai_region;62 requestBody.vertexai_region = oai_settings.vertexai_region;
63 requestBody.vertexai_express_project_id = oai_settings.vertexai_express_project_id;
63 }64 }
6465
65 if (isOllama) {66 if (isOllama) {
public/scripts/openai.js+13 -0
@@ -240,6 +240,7 @@ const sensitiveFields = [
240 'custom_exclude_body',240 'custom_exclude_body',
241 'custom_include_headers',241 'custom_include_headers',
242 'vertexai_region',242 'vertexai_region',
243 'vertexai_express_project_id',
243];244];
244245
245/**246/**
@@ -314,6 +315,7 @@ export const settingsToUpdate = {
314 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false],315 use_makersuite_sysprompt: ['#use_makersuite_sysprompt', 'use_makersuite_sysprompt', true, false],
315 vertexai_auth_mode: ['#vertexai_auth_mode', 'vertexai_auth_mode', false, true],316 vertexai_auth_mode: ['#vertexai_auth_mode', 'vertexai_auth_mode', false, true],
316 vertexai_region: ['#vertexai_region', 'vertexai_region', false, true],317 vertexai_region: ['#vertexai_region', 'vertexai_region', false, true],
318 vertexai_express_project_id: ['#vertexai_express_project_id', 'vertexai_express_project_id', false, true],
317 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true],319 use_alt_scale: ['#use_alt_scale', 'use_alt_scale', true, true],
318 squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false],320 squash_system_messages: ['#squash_system_messages', 'squash_system_messages', true, false],
319 image_inlining: ['#openai_image_inlining', 'image_inlining', true, false],321 image_inlining: ['#openai_image_inlining', 'image_inlining', true, false],
@@ -399,6 +401,7 @@ const default_settings = {
399 use_makersuite_sysprompt: true,401 use_makersuite_sysprompt: true,
400 vertexai_auth_mode: 'express',402 vertexai_auth_mode: 'express',
401 vertexai_region: 'us-central1',403 vertexai_region: 'us-central1',
404 vertexai_express_project_id: '',
402 use_alt_scale: false,405 use_alt_scale: false,
403 squash_system_messages: false,406 squash_system_messages: false,
404 image_inlining: false,407 image_inlining: false,
@@ -487,6 +490,7 @@ const oai_settings = {
487 use_makersuite_sysprompt: true,490 use_makersuite_sysprompt: true,
488 vertexai_auth_mode: 'express',491 vertexai_auth_mode: 'express',
489 vertexai_region: 'us-central1',492 vertexai_region: 'us-central1',
493 vertexai_express_project_id: '',
490 use_alt_scale: false,494 use_alt_scale: false,
491 squash_system_messages: false,495 squash_system_messages: false,
492 image_inlining: false,496 image_inlining: false,
@@ -2319,6 +2323,7 @@ async function sendOpenAIRequest(type, messages, signal) {
2319 if (isVertexAI) {2323 if (isVertexAI) {
2320 generate_data['vertexai_auth_mode'] = oai_settings.vertexai_auth_mode;2324 generate_data['vertexai_auth_mode'] = oai_settings.vertexai_auth_mode;
2321 generate_data['vertexai_region'] = oai_settings.vertexai_region;2325 generate_data['vertexai_region'] = oai_settings.vertexai_region;
2326 generate_data['vertexai_express_project_id'] = oai_settings.vertexai_express_project_id;
2322 }2327 }
2323 }2328 }
23242329
@@ -3552,6 +3557,7 @@ function loadOpenAISettings(data, settings) {
3552 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;3557 oai_settings.inline_image_quality = settings.inline_image_quality ?? default_settings.inline_image_quality;
3553 oai_settings.video_inlining = settings.video_inlining ?? default_settings.video_inlining;3558 oai_settings.video_inlining = settings.video_inlining ?? default_settings.video_inlining;
3554 oai_settings.bypass_status_check = settings.bypass_status_check ?? default_settings.bypass_status_check;3559 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;
3555 oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;3561 oai_settings.show_thoughts = settings.show_thoughts ?? default_settings.show_thoughts;
3556 oai_settings.reasoning_effort = settings.reasoning_effort ?? default_settings.reasoning_effort;3562 oai_settings.reasoning_effort = settings.reasoning_effort ?? default_settings.reasoning_effort;
3557 oai_settings.enable_web_search = settings.enable_web_search ?? default_settings.enable_web_search;3563 oai_settings.enable_web_search = settings.enable_web_search ?? default_settings.enable_web_search;
@@ -3589,6 +3595,7 @@ function loadOpenAISettings(data, settings) {
3589 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;3595 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
3590 if (settings.vertexai_auth_mode !== undefined) oai_settings.vertexai_auth_mode = settings.vertexai_auth_mode;3596 if (settings.vertexai_auth_mode !== undefined) oai_settings.vertexai_auth_mode = settings.vertexai_auth_mode;
3591 if (settings.vertexai_region !== undefined) oai_settings.vertexai_region = settings.vertexai_region;3597 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;
3592 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }3599 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
3593 $('#stream_toggle').prop('checked', oai_settings.stream_openai);3600 $('#stream_toggle').prop('checked', oai_settings.stream_openai);
3594 $('#api_url_scale').val(oai_settings.api_url_scale);3601 $('#api_url_scale').val(oai_settings.api_url_scale);
@@ -3650,6 +3657,7 @@ function loadOpenAISettings(data, settings) {
3650 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);3657 $('#use_makersuite_sysprompt').prop('checked', oai_settings.use_makersuite_sysprompt);
3651 $('#vertexai_auth_mode').val(oai_settings.vertexai_auth_mode);3658 $('#vertexai_auth_mode').val(oai_settings.vertexai_auth_mode);
3652 $('#vertexai_region').val(oai_settings.vertexai_region);3659 $('#vertexai_region').val(oai_settings.vertexai_region);
3660 $('#vertexai_express_project_id').val(oai_settings.vertexai_express_project_id);
3653 // Don't display Service Account JSON in textarea - it's stored in backend secrets3661 // Don't display Service Account JSON in textarea - it's stored in backend secrets
3654 $('#vertexai_service_account_json').val('');3662 $('#vertexai_service_account_json').val('');
3655 updateVertexAIServiceAccountStatus();3663 updateVertexAIServiceAccountStatus();
@@ -3980,6 +3988,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3980 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,3988 use_makersuite_sysprompt: settings.use_makersuite_sysprompt,
3981 vertexai_auth_mode: settings.vertexai_auth_mode,3989 vertexai_auth_mode: settings.vertexai_auth_mode,
3982 vertexai_region: settings.vertexai_region,3990 vertexai_region: settings.vertexai_region,
3991 vertexai_express_project_id: settings.vertexai_express_project_id,
3983 use_alt_scale: settings.use_alt_scale,3992 use_alt_scale: settings.use_alt_scale,
3984 squash_system_messages: settings.squash_system_messages,3993 squash_system_messages: settings.squash_system_messages,
3985 image_inlining: settings.image_inlining,3994 image_inlining: settings.image_inlining,
@@ -6378,6 +6387,10 @@ export function initOpenAI() {
6378 oai_settings.vertexai_region = String($(this).val());6387 oai_settings.vertexai_region = String($(this).val());
6379 saveSettingsDebounced();6388 saveSettingsDebounced();
6380 });6389 });
6390 $('#vertexai_express_project_id').on('input', function () {
6391 oai_settings.vertexai_express_project_id = String($(this).val());
6392 saveSettingsDebounced();
6393 });
6381 $('#vertexai_service_account_json').on('input', onVertexAIServiceAccountJsonChange);6394 $('#vertexai_service_account_json').on('input', onVertexAIServiceAccountJsonChange);
6382 $('#vertexai_validate_service_account').on('click', onVertexAIValidateServiceAccount);6395 $('#vertexai_validate_service_account').on('click', onVertexAIValidateServiceAccount);
6383 $('#vertexai_clear_service_account').on('click', onVertexAIClearServiceAccount);6396 $('#vertexai_clear_service_account').on('click', onVertexAIClearServiceAccount);
src/endpoints/backends/chat-completions.js+8 -1
@@ -493,7 +493,14 @@ async function sendMakerSuiteRequest(request, response) {
493 if (authType === 'express') {493 if (authType === 'express') {
494 // For Express mode (API key authentication), use the key parameter494 // For Express mode (API key authentication), use the key parameter
495 const keyParam = authHeader.replace('Bearer ', '');495 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' : ''}`;
497 } else if (authType === 'full') {504 } else if (authType === 'full') {
498 // For Full mode (service account authentication), use project-specific URL505 // For Full mode (service account authentication), use project-specific URL
499 // Get project ID from Service Account JSON506 // Get project ID from Service Account JSON
src/endpoints/google.js+9 -2
@@ -104,6 +104,7 @@ export async function getAccessToken(jwtToken) {
104 throw new Error(`Failed to get access token: ${error}`);104 throw new Error(`Failed to get access token: ${error}`);
105 }105 }
106106
107 /** @type {any} */
107 const data = await response.json();108 const data = await response.json();
108 return data.access_token;109 return data.access_token;
109}110}
@@ -149,8 +150,14 @@ router.post('/caption-image', async (request, response) => {
149 if (authType === 'express') {150 if (authType === 'express') {
150 // Express mode: use API key parameter151 // Express mode: use API key parameter
151 const keyParam = authHeader.replace('Bearer ', '');152 const keyParam = authHeader.replace('Bearer ', '');
152 const apiUrl = trimTrailingSlash(request.body.reverse_proxy || API_VERTEX_AI);153 const region = request.body.vertexai_region || '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}`;
154 } else if (authType === 'full') {161 } else if (authType === 'full') {
155 // Full mode: use project-specific URL with Authorization header162 // Full mode: use project-specific URL with Authorization header
156 // Get project ID from Service Account JSON163 // Get project ID from Service Account JSON