Merge pull request #4113 from InterestingDarknessII/google-dynamic-model Implement Google AI Studio model list dynamic fetching
Signed| @@ -3236,6 +3236,7 @@ | |||
| 3236 | <optgroup label="LearnLM"> | 3236 | <optgroup label="LearnLM"> |
| 3237 | <option value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option> | 3237 | <option value="learnlm-2.0-flash-experimental">learnlm-2.0-flash-experimental</option> |
| 3238 | </optgroup> | 3238 | </optgroup> |
| 3239 | <optgroup id="google_other_models" label="Other"></optgroup> | ||
| 3239 | </select> | 3240 | </select> |
| 3240 | </div> | 3241 | </div> |
| 3241 | </form> | 3242 | </form> |
| @@ -1912,6 +1912,36 @@ function saveModelList(data) { | |||
| 1912 | 1912 | ||
| 1913 | $('#model_pollinations_select').val(oai_settings.pollinations_model).trigger('change'); | 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 | function appendOpenRouterOptions(model_list, groupModels = false, sort = false) { | 1947 | function appendOpenRouterOptions(model_list, groupModels = false, sort = false) { |
| @@ -3700,7 +3730,6 @@ async function getStatusOpen() { | |||
| 3700 | chat_completion_sources.SCALE, | 3730 | chat_completion_sources.SCALE, |
| 3701 | chat_completion_sources.CLAUDE, | 3731 | chat_completion_sources.CLAUDE, |
| 3702 | chat_completion_sources.AI21, | 3732 | chat_completion_sources.AI21, |
| 3703 | chat_completion_sources.MAKERSUITE, | ||
| 3704 | chat_completion_sources.VERTEXAI, | 3733 | chat_completion_sources.VERTEXAI, |
| 3705 | chat_completion_sources.PERPLEXITY, | 3734 | chat_completion_sources.PERPLEXITY, |
| 3706 | chat_completion_sources.GROQ, | 3735 | chat_completion_sources.GROQ, |
| @@ -1092,6 +1092,49 @@ router.post('/status', async function (request, response_getstatus_openai) { | |||
| 1092 | api_url = 'https://text.pollinations.ai'; | 1092 | api_url = 'https://text.pollinations.ai'; |
| 1093 | api_key_openai = 'NONE'; | 1093 | api_key_openai = 'NONE'; |
| 1094 | headers = {}; | 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 | } else { | 1138 | } else { |
| 1096 | console.warn('This chat completion source is not supported yet.'); | 1139 | console.warn('This chat completion source is not supported yet.'); |
| 1097 | return response_getstatus_openai.status(400).send({ error: true }); | 1140 | return response_getstatus_openai.status(400).send({ error: true }); |