Add direct Google API fallback for model discovery and graceful non-JSON handling

This commit is contained in:
Chris 2026-11-01 01:29:15 -05:00
parent 423334db4d
commit ca6d9b2db6

View file

@ -2061,8 +2061,49 @@ function populateModelDropdown(models, selectedModel) {
}
}
async function fetchModelsDirectFromGoogle(key) {
if (!key || key === 'PASTE_YOUR_GEMINI_API_KEY_HERE') {
throw new Error('Please enter a valid Gemini API key first.');
}
const googleRes = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${key}`);
const googleData = await googleRes.json().catch(() => ({}));
if (!googleRes.ok) {
throw new Error(googleData.error?.message || `Google API returned status ${googleRes.status}`);
}
if (!Array.isArray(googleData.models)) {
throw new Error('Unexpected response format from Google API.');
}
const models = googleData.models
.filter(m => Array.isArray(m.supportedGenerationMethods) && m.supportedGenerationMethods.includes('generateContent'))
.filter(m => !m.name.includes('embedding') && !m.name.includes('aqa') && !m.name.includes('imagen'))
.map(m => {
const modelId = m.name.replace(/^models\//, '');
return {
id: modelId,
displayName: m.displayName || modelId,
description: m.description || ''
};
});
models.sort((a, b) => {
const aIsFlash = a.id.toLowerCase().includes('flash');
const bIsFlash = b.id.toLowerCase().includes('flash');
if (aIsFlash && !bIsFlash) return -1;
if (!aIsFlash && bIsFlash) return 1;
return a.id.localeCompare(b.id);
});
return models;
}
async function handleDiscoverModels(showNotice = true) {
const key = elements.settingsGeminiKeyInput ? elements.settingsGeminiKeyInput.value.trim() : '';
if (!key || key === 'PASTE_YOUR_GEMINI_API_KEY_HERE') {
if (showNotice) {
alert('Please enter your Google Gemini API key first.');
}
return;
}
if (elements.modelsStatusHint) {
elements.modelsStatusHint.textContent = 'Discovering available models from Google API...';
elements.modelsStatusHint.style.color = 'var(--primary)';
@ -2071,6 +2112,10 @@ async function handleDiscoverModels(showNotice = true) {
elements.btnDiscoverModels.disabled = true;
}
try {
let models = [];
// 1. Attempt backend endpoint first
try {
const res = await fetch('/api/settings/discover-models', {
method: 'POST',
@ -2081,16 +2126,29 @@ async function handleDiscoverModels(showNotice = true) {
body: JSON.stringify({ apiKey: key })
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || 'Failed to discover models.');
if (res.ok) {
const text = await res.text();
try {
const data = JSON.parse(text);
if (Array.isArray(data.models)) {
models = data.models;
}
} catch (parseErr) {}
}
} catch (serverErr) {
// Backend may not have route if server hasn't restarted yet
}
if (data.models && data.models.length > 0) {
// 2. Fallback to direct client-side call to Google API if backend returned HTML / 404
if (models.length === 0) {
models = await fetchModelsDirectFromGoogle(key);
}
if (models.length > 0) {
const currentSelected = elements.settingsGeminiModelSelect ? elements.settingsGeminiModelSelect.value : 'gemini-2.0-flash';
populateModelDropdown(data.models, currentSelected);
populateModelDropdown(models, currentSelected);
if (elements.modelsStatusHint) {
elements.modelsStatusHint.textContent = `Discovered ${data.models.length} compatible models.`;
elements.modelsStatusHint.textContent = `Discovered ${models.length} compatible models.`;
elements.modelsStatusHint.style.color = 'var(--text-muted)';
}
} else {