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>
This commit is contained in:
Cohee
2026-04-25 22:22:55 +03:00
committed by GitHub
parent 09bb7622ed
commit 09d72828cb
4 changed files with 81 additions and 26 deletions
+2
View File
@@ -3373,6 +3373,8 @@
<option value="gemini-2.0-flash-lite">gemini-2.0-flash-lite</option>
</optgroup>
<optgroup label="Gemma">
<option value="gemma-4-31b-it">gemma-4-31b-it</option>
<option value="gemma-4-26b-a4b-it">gemma-4-26b-a4b-it</option>
<option value="gemma-3n-e4b-it">gemma-3n-e4b-it</option>
<option value="gemma-3n-e2b-it">gemma-3n-e2b-it</option>
<option value="gemma-3-27b-it">gemma-3-27b-it</option>
@@ -153,6 +153,11 @@
<option data-type="google" value="gemini-2.0-flash-lite-preview">gemini-2.0-flash-lite-preview</option>
<option data-type="google" value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option>
<option data-type="google" value="gemini-robotics-er-1.5-preview">gemini-robotics-er-1.5-preview</option>
<option data-type="google" value="gemma-4-31b-it">gemma-4-31b-it</option>
<option data-type="google" value="gemma-4-26b-a4b-it">gemma-4-26b-a4b-it</option>
<option data-type="google" value="gemma-3-27b-it">gemma-3-27b-it</option>
<option data-type="google" value="gemma-3-12b-it">gemma-3-12b-it</option>
<option data-type="google" value="gemma-3-4b-it">gemma-3-4b-it</option>
<option data-type="vertexai" value="gemini-3.1-pro-preview">gemini-3.1-pro-preview</option>
<option data-type="vertexai" value="gemini-3.1-flash-lite-preview">gemini-3.1-flash-lite-preview</option>
<option data-type="vertexai" value="gemini-3.1-flash-image-preview">gemini-3.1-flash-image-preview</option>
+69 -22
View File
@@ -4933,6 +4933,65 @@ function getMaxContextOpenAI(value) {
return max_128k;
}
/**
* Get the maximum context size for Gemini models based on model identifier and optional model list.
* @param {string} model Model identifier
* @param {boolean} isUnlocked Whether context limits are unlocked
* @returns {number} Maximum context size in tokens
*/
function getGeminiMaxContext(model, isUnlocked) {
if (isUnlocked) {
return unlocked_max;
}
if (Array.isArray(model_list) && model_list.length > 0) {
const contextLength = model_list.find((record) => record.id === model)?.inputTokenLimit;
if (Number.isFinite(contextLength) && contextLength > 0) {
return contextLength;
}
}
/** @type {[RegExp, number][]} */
const contextMap = [
[/gemini-2\.5-flash-image/, max_32k],
[/gemini-3-pro-image/, max_64k],
[/gemini-(?:3[.\d]*|2\.(?:5|0))-(pro|flash)/, max_1mil],
[/(gemini-exp|learnlm-2\.0-flash|gemini-robotics)/, max_1mil],
[/gemma-3-27b-it/, max_128k],
[/gemma-3n-e4b-it/, max_8k],
[/gemma-3/, max_32k],
[/gemma-4/, max_256k],
];
for (const [regex, max] of contextMap) {
if (regex.test(model)) {
return max;
}
}
return max_128k;
}
/**
* Get the maximum temperature for Gemini models based on model identifier and optional model list.
* @param {string} model Model identifier
* @returns {number} Maximum temperature for Gemini models
*/
function getGeminiMaxTemp(model) {
if (Array.isArray(model_list) && model_list.length > 0) {
const temp = model_list.find((record) => record.id === model)?.temperature;
if (Number.isFinite(temp) && temp > 0) {
return temp;
}
}
if (/(vision|ultra|gemma)/.test(model)) {
return 1.0;
}
return 2.0;
}
/**
* Get the maximum context size for the Mistral model
* @param {string} model Model identifier
@@ -5443,28 +5502,11 @@ async function onModelChange() {
}
if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', max_2mil);
} else if (value.includes('gemini-2.5-flash-image')) {
$('#openai_max_context').attr('max', max_32k);
} else if (value.includes('gemini-3-pro-image')) {
$('#openai_max_context').attr('max', max_64k);
} else if (/gemini-3[.\d]*-(pro|flash)/.test(value) || /gemini-2.5-(pro|flash)/.test(value) || /gemini-2.0-(pro|flash)/.test(value)) {
$('#openai_max_context').attr('max', max_1mil);
} else if (value.includes('gemini-exp') || value.includes('learnlm-2.0-flash') || value.includes('gemini-robotics')) {
$('#openai_max_context').attr('max', max_1mil);
} else if (value.includes('gemma-3-27b-it')) {
$('#openai_max_context').attr('max', max_128k);
} else if (value.includes('gemma-3n-e4b-it')) {
$('#openai_max_context').attr('max', max_8k);
} else if (value.includes('gemma-3')) {
$('#openai_max_context').attr('max', max_32k);
} else {
$('#openai_max_context').attr('max', max_32k);
}
let makersuite_max_temp = (value.includes('vision') || value.includes('ultra') || value.includes('gemma')) ? 1.0 : 2.0;
oai_settings.temp_openai = Math.min(makersuite_max_temp, oai_settings.temp_openai);
$('#temp_openai').attr('max', makersuite_max_temp).val(oai_settings.temp_openai).trigger('input');
const contextSize = getGeminiMaxContext(value, oai_settings.max_context_unlocked);
const maxTemp = getGeminiMaxTemp(value);
$('#openai_max_context').attr('max', contextSize);
oai_settings.temp_openai = Math.min(maxTemp, oai_settings.temp_openai);
$('#temp_openai').attr('max', maxTemp).val(oai_settings.temp_openai).trigger('input');
oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
}
@@ -6040,6 +6082,10 @@ export function isImageInliningSupported() {
'gemini-exp-1206',
'learnlm',
'gemini-robotics',
'gemma-3-27b',
'gemma-3-12b',
'gemma-3-4b',
'gemma-4',
// MistralAI
'mistral-small-2503',
'mistral-small-2506',
@@ -6144,6 +6190,7 @@ export function isVideoInliningSupported() {
'gemini-2.5',
'gemini-exp-1206',
'gemini-3',
'gemma-4',
// Z.AI (GLM)
'glm-4.5v',
'glm-4.6v',
+5 -4
View File
@@ -459,7 +459,7 @@ async function sendMakerSuiteRequest(request, response) {
const includeReasoning = Boolean(request.body.include_reasoning);
const aspectRatio = String(request.body.request_image_aspect_ratio);
const imageSize = String(request.body.request_image_resolution);
const isGemma = model.includes('gemma');
const isGemma3 = /gemma-3/.test(model);
const isLearnLM = model.includes('learnlm');
const responseMimeType = request.body.responseMimeType ?? (request.body.json_schema ? 'application/json' : undefined);
@@ -519,13 +519,13 @@ async function sendMakerSuiteRequest(request, response) {
}
}
const useSystemPrompt = !enableImageModality && !isGemma && request.body.use_sysprompt;
const useSystemPrompt = !enableImageModality && !isGemma3 && request.body.use_sysprompt;
const tools = [];
const prompt = convertGooglePrompt(request.body.messages, model, useSystemPrompt, getPromptNames(request));
const safetySettings = [...GEMINI_SAFETY, ...(useVertexAi ? VERTEX_SAFETY : [])];
if (Array.isArray(request.body.tools) && request.body.tools.length > 0 && !enableImageModality && !isGemma) {
if (Array.isArray(request.body.tools) && request.body.tools.length > 0 && !enableImageModality && !isGemma3) {
const functionDeclarations = [];
const customTools = [];
for (const tool of request.body.tools) {
@@ -550,7 +550,7 @@ async function sendMakerSuiteRequest(request, response) {
}
}
if (enableWebSearch && !enableImageModality && !isGemma && !isLearnLM && !noSearchModels.includes(model)) {
if (enableWebSearch && !enableImageModality && !isGemma3 && !isLearnLM && !noSearchModels.includes(model)) {
// Tool use with function calling is unsupported
if (!tools.some(t => t.function_declarations)) {
tools.push({ google_search: {} });
@@ -1832,6 +1832,7 @@ router.post('/status', async function (request, statusResponse) {
const models = data.models
?.filter(model => model.supportedGenerationMethods?.includes('generateContent'))
?.map(model => ({
...model,
id: model.name.replace('models/', ''),
})) || [];