From a3b33f7cf43411cf7cf876ef37dd3db853aea492 Mon Sep 17 00:00:00 2001 From: Alastair Grant <85125580+aldo-g@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:25:49 +1100 Subject: [PATCH] fix: all providers now working --- src/services/llm.ts | 53 +++++++++++++++++++++++++++++++++++++++---- src/ui/SettingsTab.ts | 47 +++++++------------------------------- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/services/llm.ts b/src/services/llm.ts index 075d496..6ff846c 100644 --- a/src/services/llm.ts +++ b/src/services/llm.ts @@ -438,7 +438,9 @@ async function callGemini( const modelEndpoint = model === "gemini-pro" ? "gemini-pro" : model; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${modelEndpoint}:generateContent?key=${apiKey}`; - const requestBody = { + const isModernGemini = model.includes("1.5") || model.includes("gemini-3") || model.includes("gemini-2"); + + const requestBody: any = { contents: [ { parts: [ @@ -447,11 +449,15 @@ async function callGemini( } ], generationConfig: { - maxOutputTokens: maxTokens, + maxOutputTokens: Math.max(maxTokens, 2048), temperature: 0.7 } }; + if (isModernGemini) { + requestBody.generationConfig.responseMimeType = "application/json"; + } + const response = await requestUrl({ url: apiUrl, method: "POST", @@ -657,7 +663,30 @@ export async function fetchProviderModels( }); const data = response.json; return data.models - .filter((m: any) => m.supportedGenerationMethods.includes("generateContent")) + .filter((m: any) => { + const id = m.name.toLowerCase(); + const displayName = (m.displayName || "").toLowerCase(); + + // 1. Must support content generation + if (!m.supportedGenerationMethods.includes("generateContent")) return false; + + // 2. EXCLUDE specialized/incompatible models + const isSpecialized = + id.includes("nano") || displayName.includes("nano") || + id.includes("robotics") || displayName.includes("robotics") || + id.includes("computer use") || displayName.includes("computer use") || + id.includes("deep research") || displayName.includes("deep research") || + id.includes("aqa") || displayName.includes("aqa") || + id.includes("experimental") || id.includes("exp") && !id.includes("flash") && !id.includes("pro"); // Keep Pro/Flash exp + + if (isSpecialized) return false; + + // 3. ONLY keep Gemini and Gemma models + const isTargetFamily = id.includes("gemini") || id.includes("gemma"); + if (!isTargetFamily) return false; + + return true; + }) .map((m: any) => ({ id: m.name.replace("models/", ""), name: m.displayName || m.name.replace("models/", "") @@ -670,7 +699,23 @@ export async function fetchProviderModels( }); const data = response.json; return data.data - .filter((m: any) => !m.id.includes("embed") && !m.id.includes("moderation")) + .filter((m: any) => { + const id = m.id.toLowerCase(); + + // 1. MUST exclude known utility/non-chat models + if (id.includes("embed") || id.includes("moderation") || id.includes("vibe-cli")) return false; + + // 2. EXCLUDE experimental/developer/internal models + if (id.includes("devstral") || id.includes("magistral") || id.includes("labs-")) return false; + + // 3. CLEAN UP dated versions + // Many Mistral models have both a 'latest' and a dated version (e.g. -2411, -2501). + // We favor 'latest' tags and primary model names for a cleaner list. + const isDated = /-\d{4}$/.test(id); + if (isDated && !id.includes("pixtral") && !id.includes("codestral")) return false; + + return true; + }) .map((m: any) => ({ id: m.id, name: m.id })); } case "ollama": { diff --git a/src/ui/SettingsTab.ts b/src/ui/SettingsTab.ts index e0909bf..7a77850 100644 --- a/src/ui/SettingsTab.ts +++ b/src/ui/SettingsTab.ts @@ -6,7 +6,6 @@ import { fetchProviderModels, fetchCommunityModels } from "../services/llm"; export default class SettingsTab extends PluginSettingTab { plugin: MyPlugin; fetchedModels: Record> = {}; - modelFilter: string = ""; static readonly CORE_MODELS: Record> = { openai: [ @@ -33,6 +32,8 @@ export default class SettingsTab extends PluginSettingTab { { id: "deepseek-coder", name: "DeepSeek Coder" } ], gemini: [ + { id: "gemini-3-flash-preview", name: "Gemini 3 Flash (Preview)" }, + { id: "gemini-2.0-flash-exp", name: "Gemini 2.0 Flash (Experimental)" }, { id: "gemini-1.5-pro", name: "Gemini 1.5 Pro" }, { id: "gemini-1.5-flash", name: "Gemini 1.5 Flash" }, { id: "gemini-1.5-flash-8b", name: "Gemini 1.5 Flash-8B" }, @@ -62,11 +63,12 @@ export default class SettingsTab extends PluginSettingTab { .addDropdown((dropdown: DropdownComponent) => { dropdown .addOption("openai", "OpenAI") + .addOption("anthropic", "Anthropic (Claude)") .addOption("deepseek", "DeepSeek") .addOption("gemini", "Google (Gemini)") .addOption("mistral", "Mistral AI") .addOption("ollama", "Ollama (Local)") - .setValue(this.plugin.settings.llmProvider === "anthropic" ? "openai" : this.plugin.settings.llmProvider) + .setValue(this.plugin.settings.llmProvider) .onChange(async (value: LLMProvider) => { this.plugin.settings.llmProvider = value; await this.plugin.saveSettings(); @@ -79,33 +81,11 @@ export default class SettingsTab extends PluginSettingTab { const provider = this.plugin.settings.llmProvider; const apiKey = this.plugin.settings.apiKeys[provider]; - // Model Filter - new Setting(containerEl) - .setName("Filter models") - .setDesc("Type to filter the available models list") - .addSearch((search) => { - search - .setPlaceholder("Filter models...") - .setValue(this.modelFilter) - .onChange((value) => { - this.modelFilter = value.toLowerCase(); - this.display(); - }); - }) - .addExtraButton((button) => { - button - .setIcon("x-circle") - .setTooltip("Clear filter") - .onClick(() => { - this.modelFilter = ""; - this.display(); - }); - }); // Model selection const modelSetting = new Setting(containerEl) .setName(`${provider.charAt(0).toUpperCase() + provider.slice(1)} model`) - .setDesc(`Select which ${provider} model to use`); + .setDesc(`Select which ${provider} model to use. Save your API key and refresh to get latest models. Warning: not all models are compatible with this plugin.`); modelSetting.addDropdown(async (dropdown: DropdownComponent) => { const currentModel = this.plugin.settings.models[provider]; @@ -122,22 +102,11 @@ export default class SettingsTab extends PluginSettingTab { modelMap.set(currentModel, currentModel); } - // Apply filter - let visibleModelsCount = 0; modelMap.forEach((name, id) => { - if (!this.modelFilter || id.toLowerCase().includes(this.modelFilter) || name.toLowerCase().includes(this.modelFilter)) { - dropdown.addOption(id, name); - visibleModelsCount++; - } + dropdown.addOption(id, name); }); - if (visibleModelsCount === 0 && this.modelFilter) { - dropdown.addOption("", "No models match filter"); - dropdown.setDisabled(true); - } else { - dropdown.setDisabled(false); - dropdown.setValue(currentModel); - } + dropdown.setValue(currentModel); // Auto-fetch in background if we have an API key and haven't fetched yet if (fetched.length === 0 && apiKey) { @@ -264,7 +233,7 @@ export default class SettingsTab extends PluginSettingTab { // Model selection new Setting(containerEl) .setName("Ollama model") - .setDesc("Enter the name of the Ollama model to use") + .setDesc("Enter the name of the Ollama model to use. Some models may not be compatible with this plugin.") .addText((text) => text .setPlaceholder("llama3")