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>

09d72828cb22bb7fe5e189eff322b5632babc513

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
4 files changed, +81 -26Ignore whitespace
public/index.html+2 -0
@@ -3373,6 +3373,8 @@
33733373 <option value="gemini-2.0-flash-lite">gemini-2.0-flash-lite</option>
33743374 </optgroup>
33753375 <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>
33763378 <option value="gemma-3n-e4b-it">gemma-3n-e4b-it</option>
33773379 <option value="gemma-3n-e2b-it">gemma-3n-e2b-it</option>
33783380 <option value="gemma-3-27b-it">gemma-3-27b-it</option>
public/scripts/extensions/caption/settings.html+5 -0
@@ -153,6 +153,11 @@
153153 <option data-type="google" value="gemini-2.0-flash-lite-preview">gemini-2.0-flash-lite-preview</option>
154154 <option data-type="google" value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option>
155155 <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>
156161 <option data-type="vertexai" value="gemini-3.1-pro-preview">gemini-3.1-pro-preview</option>
157162 <option data-type="vertexai" value="gemini-3.1-flash-lite-preview">gemini-3.1-flash-lite-preview</option>
158163 <option data-type="vertexai" value="gemini-3.1-flash-image-preview">gemini-3.1-flash-image-preview</option>
public/scripts/openai.js+69 -22
@@ -4934,6 +4934,65 @@ function getMaxContextOpenAI(value) {
49344934}
49354935
49364936/**
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+/**
49374996 * Get the maximum context size for the Mistral model
49384997 * @param {string} model Model identifier
49394998 * @param {boolean} isUnlocked Whether context limits are unlocked
@@ -5443,28 +5502,11 @@ async function onModelChange() {
54435502 }
54445503
54455504 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
54465505 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');
54685510 oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
54695511 $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
54705512 }
@@ -6040,6 +6082,10 @@ export function isImageInliningSupported() {
60406082 'gemini-exp-1206',
60416083 'learnlm',
60426084 'gemini-robotics',
6085+ 'gemma-3-27b',
6086+ 'gemma-3-12b',
6087+ 'gemma-3-4b',
6088+ 'gemma-4',
60436089 // MistralAI
60446090 'mistral-small-2503',
60456091 'mistral-small-2506',
@@ -6144,6 +6190,7 @@ export function isVideoInliningSupported() {
61446190 'gemini-2.5',
61456191 'gemini-exp-1206',
61466192 'gemini-3',
6193+ 'gemma-4',
61476194 // Z.AI (GLM)
61486195 'glm-4.5v',
61496196 'glm-4.6v',
src/endpoints/backends/chat-completions.js+5 -4
@@ -459,7 +459,7 @@ async function sendMakerSuiteRequest(request, response) {
459459 const includeReasoning = Boolean(request.body.include_reasoning);
460460 const aspectRatio = String(request.body.request_image_aspect_ratio);
461461 const imageSize = String(request.body.request_image_resolution);
462462 const isGemmaisGemma3 = model/gemma-3/.includestest('gemma'model);
463463 const isLearnLM = model.includes('learnlm');
464464
465465 const responseMimeType = request.body.responseMimeType ?? (request.body.json_schema ? 'application/json' : undefined);
@@ -519,13 +519,13 @@ async function sendMakerSuiteRequest(request, response) {
519519 }
520520 }
521521
522522 const useSystemPrompt = !enableImageModality && !isGemmaisGemma3 && request.body.use_sysprompt;
523523
524524 const tools = [];
525525 const prompt = convertGooglePrompt(request.body.messages, model, useSystemPrompt, getPromptNames(request));
526526 const safetySettings = [...GEMINI_SAFETY, ...(useVertexAi ? VERTEX_SAFETY : [])];
527527
528528 if (Array.isArray(request.body.tools) && request.body.tools.length > 0 && !enableImageModality && !isGemmaisGemma3) {
529529 const functionDeclarations = [];
530530 const customTools = [];
531531 for (const tool of request.body.tools) {
@@ -550,7 +550,7 @@ async function sendMakerSuiteRequest(request, response) {
550550 }
551551 }
552552
553553 if (enableWebSearch && !enableImageModality && !isGemmaisGemma3 && !isLearnLM && !noSearchModels.includes(model)) {
554554 // Tool use with function calling is unsupported
555555 if (!tools.some(t => t.function_declarations)) {
556556 tools.push({ google_search: {} });
@@ -1832,6 +1832,7 @@ router.post('/status', async function (request, statusResponse) {
18321832 const models = data.models
18331833 ?.filter(model => model.supportedGenerationMethods?.includes('generateContent'))
18341834 ?.map(model => ({
1835+ ...model,
18351836 id: model.name.replace('models/', ''),
18361837 })) || [];
18371838