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