feat: add gemma 4 for AI studio (#5493) * feat: add gemma 4 for AI studio * fix: update max context return value for gemma-3n-e4b-it model * refactor: iterate array of [regex, number] * gemma4: enable tool calling and sysprompt Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com>
Signed| @@ -3373,6 +3373,8 @@ | ||
| 3373 | 3373 | <option value="gemini-2.0-flash-lite">gemini-2.0-flash-lite</option> |
| 3374 | 3374 | </optgroup> |
| 3375 | 3375 | <optgroup label="Gemma"> |
| 3376 | + <option value="gemma-4-31b-it">gemma-4-31b-it</option> | |
| 3377 | + <option value="gemma-4-26b-a4b-it">gemma-4-26b-a4b-it</option> | |
| 3376 | 3378 | <option value="gemma-3n-e4b-it">gemma-3n-e4b-it</option> |
| 3377 | 3379 | <option value="gemma-3n-e2b-it">gemma-3n-e2b-it</option> |
| 3378 | 3380 | <option value="gemma-3-27b-it">gemma-3-27b-it</option> |
| @@ -153,6 +153,11 @@ | ||
| 153 | 153 | <option data-type="google" value="gemini-2.0-flash-lite-preview">gemini-2.0-flash-lite-preview</option> |
| 154 | 154 | <option data-type="google" value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option> |
| 155 | 155 | <option data-type="google" value="gemini-robotics-er-1.5-preview">gemini-robotics-er-1.5-preview</option> |
| 156 | + <option data-type="google" value="gemma-4-31b-it">gemma-4-31b-it</option> | |
| 157 | + <option data-type="google" value="gemma-4-26b-a4b-it">gemma-4-26b-a4b-it</option> | |
| 158 | + <option data-type="google" value="gemma-3-27b-it">gemma-3-27b-it</option> | |
| 159 | + <option data-type="google" value="gemma-3-12b-it">gemma-3-12b-it</option> | |
| 160 | + <option data-type="google" value="gemma-3-4b-it">gemma-3-4b-it</option> | |
| 156 | 161 | <option data-type="vertexai" value="gemini-3.1-pro-preview">gemini-3.1-pro-preview</option> |
| 157 | 162 | <option data-type="vertexai" value="gemini-3.1-flash-lite-preview">gemini-3.1-flash-lite-preview</option> |
| 158 | 163 | <option data-type="vertexai" value="gemini-3.1-flash-image-preview">gemini-3.1-flash-image-preview</option> |
| @@ -4934,6 +4934,65 @@ function getMaxContextOpenAI(value) { | ||
| 4934 | 4934 | } |
| 4935 | 4935 | |
| 4936 | 4936 | /** |
| 4937 | + * Get the maximum context size for Gemini models based on model identifier and optional model list. | |
| 4938 | + * @param {string} model Model identifier | |
| 4939 | + * @param {boolean} isUnlocked Whether context limits are unlocked | |
| 4940 | + * @returns {number} Maximum context size in tokens | |
| 4941 | + */ | |
| 4942 | +function getGeminiMaxContext(model, isUnlocked) { | |
| 4943 | + if (isUnlocked) { | |
| 4944 | + return unlocked_max; | |
| 4945 | + } | |
| 4946 | + | |
| 4947 | + if (Array.isArray(model_list) && model_list.length > 0) { | |
| 4948 | + const contextLength = model_list.find((record) => record.id === model)?.inputTokenLimit; | |
| 4949 | + if (Number.isFinite(contextLength) && contextLength > 0) { | |
| 4950 | + return contextLength; | |
| 4951 | + } | |
| 4952 | + } | |
| 4953 | + | |
| 4954 | + /** @type {[RegExp, number][]} */ | |
| 4955 | + const contextMap = [ | |
| 4956 | + [/gemini-2\.5-flash-image/, max_32k], | |
| 4957 | + [/gemini-3-pro-image/, max_64k], | |
| 4958 | + [/gemini-(?:3[.\d]*|2\.(?:5|0))-(pro|flash)/, max_1mil], | |
| 4959 | + [/(gemini-exp|learnlm-2\.0-flash|gemini-robotics)/, max_1mil], | |
| 4960 | + [/gemma-3-27b-it/, max_128k], | |
| 4961 | + [/gemma-3n-e4b-it/, max_8k], | |
| 4962 | + [/gemma-3/, max_32k], | |
| 4963 | + [/gemma-4/, max_256k], | |
| 4964 | + ]; | |
| 4965 | + | |
| 4966 | + for (const [regex, max] of contextMap) { | |
| 4967 | + if (regex.test(model)) { | |
| 4968 | + return max; | |
| 4969 | + } | |
| 4970 | + } | |
| 4971 | + | |
| 4972 | + return max_128k; | |
| 4973 | +} | |
| 4974 | + | |
| 4975 | +/** | |
| 4976 | + * Get the maximum temperature for Gemini models based on model identifier and optional model list. | |
| 4977 | + * @param {string} model Model identifier | |
| 4978 | + * @returns {number} Maximum temperature for Gemini models | |
| 4979 | + */ | |
| 4980 | +function getGeminiMaxTemp(model) { | |
| 4981 | + if (Array.isArray(model_list) && model_list.length > 0) { | |
| 4982 | + const temp = model_list.find((record) => record.id === model)?.temperature; | |
| 4983 | + if (Number.isFinite(temp) && temp > 0) { | |
| 4984 | + return temp; | |
| 4985 | + } | |
| 4986 | + } | |
| 4987 | + | |
| 4988 | + if (/(vision|ultra|gemma)/.test(model)) { | |
| 4989 | + return 1.0; | |
| 4990 | + } | |
| 4991 | + | |
| 4992 | + return 2.0; | |
| 4993 | +} | |
| 4994 | + | |
| 4995 | +/** | |
| 4937 | 4996 | * Get the maximum context size for the Mistral model |
| 4938 | 4997 | * @param {string} model Model identifier |
| 4939 | 4998 | * @param {boolean} isUnlocked Whether context limits are unlocked |
| @@ -5443,28 +5502,11 @@ async function onModelChange() { | ||
| 5443 | 5502 | } |
| 5444 | 5503 | |
| 5445 | 5504 | if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) { |
| 5446 | 5505 | ifconst contextSize = getGeminiMaxContext(value, oai_settings.max_context_unlocked) {; |
| 5447 | - $('#openai_max_context').attr('max', max_2mil); | |
| 5506 | + const maxTemp = getGeminiMaxTemp(value); | |
| 5448 | - } else if (value.includes('gemini-2.5-flash-image')) { | |
| 5507 | + $('#openai_max_context').attr('max', contextSize); | |
| 5449 | - $('#openai_max_context').attr('max', max_32k); | |
| 5508 | + oai_settings.temp_openai = Math.min(maxTemp, oai_settings.temp_openai); | |
| 5450 | - } else if (value.includes('gemini-3-pro-image')) { | |
| 5509 | + $('#temp_openai').attr('max', maxTemp).val(oai_settings.temp_openai).trigger('input'); | |
| 5451 | - $('#openai_max_context').attr('max', max_64k); | |
| 5452 | - } else if (/gemini-3[.\d]*-(pro|flash)/.test(value) || /gemini-2.5-(pro|flash)/.test(value) || /gemini-2.0-(pro|flash)/.test(value)) { | |
| 5453 | - $('#openai_max_context').attr('max', max_1mil); | |
| 5454 | - } else if (value.includes('gemini-exp') || value.includes('learnlm-2.0-flash') || value.includes('gemini-robotics')) { | |
| 5455 | - $('#openai_max_context').attr('max', max_1mil); | |
| 5456 | - } else if (value.includes('gemma-3-27b-it')) { | |
| 5457 | - $('#openai_max_context').attr('max', max_128k); | |
| 5458 | - } else if (value.includes('gemma-3n-e4b-it')) { | |
| 5459 | - $('#openai_max_context').attr('max', max_8k); | |
| 5460 | - } else if (value.includes('gemma-3')) { | |
| 5461 | - $('#openai_max_context').attr('max', max_32k); | |
| 5462 | - } else { | |
| 5463 | - $('#openai_max_context').attr('max', max_32k); | |
| 5464 | - } | |
| 5465 | - let makersuite_max_temp = (value.includes('vision') || value.includes('ultra') || value.includes('gemma')) ? 1.0 : 2.0; | |
| 5466 | - oai_settings.temp_openai = Math.min(makersuite_max_temp, oai_settings.temp_openai); | |
| 5467 | - $('#temp_openai').attr('max', makersuite_max_temp).val(oai_settings.temp_openai).trigger('input'); | |
| 5468 | 5510 | oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); |
| 5469 | 5511 | $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); |
| 5470 | 5512 | } |
| @@ -6040,6 +6082,10 @@ export function isImageInliningSupported() { | ||
| 6040 | 6082 | 'gemini-exp-1206', |
| 6041 | 6083 | 'learnlm', |
| 6042 | 6084 | 'gemini-robotics', |
| 6085 | + 'gemma-3-27b', | |
| 6086 | + 'gemma-3-12b', | |
| 6087 | + 'gemma-3-4b', | |
| 6088 | + 'gemma-4', | |
| 6043 | 6089 | // MistralAI |
| 6044 | 6090 | 'mistral-small-2503', |
| 6045 | 6091 | 'mistral-small-2506', |
| @@ -6144,6 +6190,7 @@ export function isVideoInliningSupported() { | ||
| 6144 | 6190 | 'gemini-2.5', |
| 6145 | 6191 | 'gemini-exp-1206', |
| 6146 | 6192 | 'gemini-3', |
| 6193 | + 'gemma-4', | |
| 6147 | 6194 | // Z.AI (GLM) |
| 6148 | 6195 | 'glm-4.5v', |
| 6149 | 6196 | 'glm-4.6v', |
| @@ -459,7 +459,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 459 | 459 | const includeReasoning = Boolean(request.body.include_reasoning); |
| 460 | 460 | const aspectRatio = String(request.body.request_image_aspect_ratio); |
| 461 | 461 | const imageSize = String(request.body.request_image_resolution); |
| 462 | 462 | const isGemmaisGemma3 = model/gemma-3/.includestest('gemma'model); |
| 463 | 463 | const isLearnLM = model.includes('learnlm'); |
| 464 | 464 | |
| 465 | 465 | const responseMimeType = request.body.responseMimeType ?? (request.body.json_schema ? 'application/json' : undefined); |
| @@ -519,13 +519,13 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 519 | 519 | } |
| 520 | 520 | } |
| 521 | 521 | |
| 522 | 522 | const useSystemPrompt = !enableImageModality && !isGemmaisGemma3 && request.body.use_sysprompt; |
| 523 | 523 | |
| 524 | 524 | const tools = []; |
| 525 | 525 | const prompt = convertGooglePrompt(request.body.messages, model, useSystemPrompt, getPromptNames(request)); |
| 526 | 526 | const safetySettings = [...GEMINI_SAFETY, ...(useVertexAi ? VERTEX_SAFETY : [])]; |
| 527 | 527 | |
| 528 | 528 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0 && !enableImageModality && !isGemmaisGemma3) { |
| 529 | 529 | const functionDeclarations = []; |
| 530 | 530 | const customTools = []; |
| 531 | 531 | for (const tool of request.body.tools) { |
| @@ -550,7 +550,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 550 | 550 | } |
| 551 | 551 | } |
| 552 | 552 | |
| 553 | 553 | if (enableWebSearch && !enableImageModality && !isGemmaisGemma3 && !isLearnLM && !noSearchModels.includes(model)) { |
| 554 | 554 | // Tool use with function calling is unsupported |
| 555 | 555 | if (!tools.some(t => t.function_declarations)) { |
| 556 | 556 | tools.push({ google_search: {} }); |
| @@ -1832,6 +1832,7 @@ router.post('/status', async function (request, statusResponse) { | ||
| 1832 | 1832 | const models = data.models |
| 1833 | 1833 | ?.filter(model => model.supportedGenerationMethods?.includes('generateContent')) |
| 1834 | 1834 | ?.map(model => ({ |
| 1835 | + ...model, | |
| 1835 | 1836 | id: model.name.replace('models/', ''), |
| 1836 | 1837 | })) || []; |
| 1837 | 1838 | |