Merge pull request #4113 from InterestingDarknessII/google-dynamic-model Implement Google AI Studio model list dynamic fetching
Signed| @@ -3236,6 +3236,7 @@ | ||
| 3236 | 3236 | <optgroup label="LearnLM"> |
| 3237 | 3237 | <option value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option> |
| 3238 | 3238 | </optgroup> |
| 3239 | + <optgroup id="google_other_models" label="Other"></optgroup> | |
| 3239 | 3240 | </select> |
| 3240 | 3241 | </div> |
| 3241 | 3242 | </form> |
| @@ -1912,6 +1912,36 @@ function saveModelList(data) { | ||
| 1912 | 1912 | |
| 1913 | 1913 | $('#model_pollinations_select').val(oai_settings.pollinations_model).trigger('change'); |
| 1914 | 1914 | } |
| 1915 | + | |
| 1916 | + if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { | |
| 1917 | + // Clear only the "Other" optgroup for dynamic models | |
| 1918 | + $('#google_other_models').empty(); | |
| 1919 | + | |
| 1920 | + // Get static model options that are already in the HTML | |
| 1921 | + const staticModels = []; | |
| 1922 | + $('#model_google_select option').each(function() { | |
| 1923 | + staticModels.push($(this).val()); | |
| 1924 | + }); | |
| 1925 | + | |
| 1926 | + // Add dynamic models to the "Other" group | |
| 1927 | + model_list.forEach((model) => { | |
| 1928 | + // Only add if not already in static list | |
| 1929 | + if (!staticModels.includes(model.id)) { | |
| 1930 | + $('#google_other_models').append( | |
| 1931 | + $('<option>', { | |
| 1932 | + value: model.id, | |
| 1933 | + text: model.id, | |
| 1934 | + })); | |
| 1935 | + } | |
| 1936 | + }); | |
| 1937 | + | |
| 1938 | + const selectedModel = model_list.find(model => model.id === oai_settings.google_model); | |
| 1939 | + if (model_list.length > 0 && (!selectedModel || !oai_settings.google_model)) { | |
| 1940 | + oai_settings.google_model = model_list[0].id; | |
| 1941 | + } | |
| 1942 | + | |
| 1943 | + $('#model_google_select').val(oai_settings.google_model).trigger('change'); | |
| 1944 | + } | |
| 1915 | 1945 | } |
| 1916 | 1946 | |
| 1917 | 1947 | function appendOpenRouterOptions(model_list, groupModels = false, sort = false) { |
| @@ -3700,7 +3730,6 @@ async function getStatusOpen() { | ||
| 3700 | 3730 | chat_completion_sources.SCALE, |
| 3701 | 3731 | chat_completion_sources.CLAUDE, |
| 3702 | 3732 | chat_completion_sources.AI21, |
| 3703 | - chat_completion_sources.MAKERSUITE, | |
| 3704 | 3733 | chat_completion_sources.VERTEXAI, |
| 3705 | 3734 | chat_completion_sources.PERPLEXITY, |
| 3706 | 3735 | chat_completion_sources.GROQ, |
| @@ -1092,6 +1092,49 @@ router.post('/status', async function (request, response_getstatus_openai) { | ||
| 1092 | 1092 | api_url = 'https://text.pollinations.ai'; |
| 1093 | 1093 | api_key_openai = 'NONE'; |
| 1094 | 1094 | headers = {}; |
| 1095 | + } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MAKERSUITE) { | |
| 1096 | + // For Google AI Studio, we need to get models from the API | |
| 1097 | + const api_key_makersuite = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE); | |
| 1098 | + const api_url = new URL(request.body.reverse_proxy || API_MAKERSUITE); | |
| 1099 | + const apiVersion = getConfigValue('gemini.apiVersion', 'v1beta'); | |
| 1100 | + let models_url = `${api_url.origin}/${apiVersion}/models?key=${api_key_makersuite}`; | |
| 1101 | + | |
| 1102 | + if (!api_key_makersuite && request.body.reverse_proxy) { | |
| 1103 | + models_url = `${api_url.origin}/${apiVersion}/models`; // For some special reverse proxy, we can't pass the API key | |
| 1104 | + } else if (!api_key_makersuite && !request.body.reverse_proxy) { | |
| 1105 | + console.warn('Google AI Studio API key is missing.'); | |
| 1106 | + return response_getstatus_openai.status(400).send({ error: true }); | |
| 1107 | + } | |
| 1108 | + | |
| 1109 | + try { | |
| 1110 | + const response = await fetch(models_url, { | |
| 1111 | + method: 'GET', | |
| 1112 | + headers: { | |
| 1113 | + 'Content-Type': 'application/json', | |
| 1114 | + }, | |
| 1115 | + }); | |
| 1116 | + | |
| 1117 | + if (response.ok) { | |
| 1118 | + /** @type {any} */ | |
| 1119 | + const data = await response.json(); | |
| 1120 | + // Transform Google AI Studio models to OpenAI format | |
| 1121 | + const models = data.models | |
| 1122 | + ?.filter(model => model.supportedGenerationMethods?.includes('generateContent')) | |
| 1123 | + ?.map(model => ({ | |
| 1124 | + id: model.name.replace('models/', ''), | |
| 1125 | + })) || []; | |
| 1126 | + | |
| 1127 | + response_getstatus_openai.send({ data: models }); | |
| 1128 | + console.info('Available Google AI Studio models:', models.map(m => m.id)); | |
| 1129 | + return; | |
| 1130 | + } else { | |
| 1131 | + console.error('Google AI Studio models endpoint failed:', response.status, response.statusText); | |
| 1132 | + return response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } }); | |
| 1133 | + } | |
| 1134 | + } catch (error) { | |
| 1135 | + console.error('Error fetching Google AI Studio models:', error); | |
| 1136 | + return response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } }); | |
| 1137 | + } | |
| 1095 | 1138 | } else { |
| 1096 | 1139 | console.warn('This chat completion source is not supported yet.'); |
| 1097 | 1140 | return response_getstatus_openai.status(400).send({ error: true }); |