From b2ce0fc9d809a98cecdc17c458dff358cbafedea Mon Sep 17 00:00:00 2001 From: Chris Lettieri Date: Mon, 12 Jan 2026 08:48:33 -0500 Subject: [PATCH] refresh to get latest open ai models --- src/services/openai-service.ts | 34 +++++++++++++++ src/types/settings.ts | 4 +- src/ui/settings-tab.ts | 77 ++++++++++++++++++++++++++++++---- 3 files changed, 106 insertions(+), 9 deletions(-) diff --git a/src/services/openai-service.ts b/src/services/openai-service.ts index 029e766..1a427e4 100644 --- a/src/services/openai-service.ts +++ b/src/services/openai-service.ts @@ -22,6 +22,40 @@ export class OpenAIService { this.model = model; } + /** + * Fetch available models from OpenAI API + * @param apiKey The OpenAI API key + * @returns Array of chat-compatible model IDs, sorted alphabetically + */ + static async fetchAvailableModels(apiKey: string): Promise { + if (!apiKey) { + throw new Error('API key is required to fetch models'); + } + + const response = await fetch('https://api.openai.com/v1/models', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${apiKey}` + } + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(`Failed to fetch models: ${errorData.error?.message || response.statusText}`); + } + + const data = await response.json(); + + // Filter for chat-compatible models + const chatModelPrefixes = ['gpt-', 'o1', 'o3', 'chatgpt-']; + const chatModels = data.data + .map((model: { id: string }) => model.id) + .filter((id: string) => chatModelPrefixes.some(prefix => id.startsWith(prefix))) + .sort((a: string, b: string) => a.localeCompare(b)); + + return chatModels; + } + /** * Extract custom context instructions from content if they exist * @param content The content to extract the context from diff --git a/src/types/settings.ts b/src/types/settings.ts index ec3f4d4..d5287b1 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -17,6 +17,7 @@ export interface OpenAugiSettings { apiKey: string; defaultModel: string; customModelOverride: string; + cachedModels: string[]; summaryFolder: string; notesFolder: string; promptsFolder: string; @@ -29,8 +30,9 @@ export interface OpenAugiSettings { export const DEFAULT_SETTINGS: OpenAugiSettings = { apiKey: '', - defaultModel: 'gpt-5', + defaultModel: 'gpt-4o', customModelOverride: '', + cachedModels: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1', 'o1-mini', 'o3-mini'], summaryFolder: 'OpenAugi/Summaries', notesFolder: 'OpenAugi/Notes', promptsFolder: 'OpenAugi/Prompts', diff --git a/src/ui/settings-tab.ts b/src/ui/settings-tab.ts index 1e3d219..9111a74 100644 --- a/src/ui/settings-tab.ts +++ b/src/ui/settings-tab.ts @@ -1,5 +1,6 @@ -import { App, PluginSettingTab, Setting, Notice } from 'obsidian'; +import { App, PluginSettingTab, Setting, Notice, DropdownComponent } from 'obsidian'; import type OpenAugiPlugin from '../types/plugin'; +import { OpenAIService } from '../services/openai-service'; export class OpenAugiSettingTab extends PluginSettingTab { plugin: OpenAugiPlugin; @@ -35,17 +36,77 @@ export class OpenAugiSettingTab extends PluginSettingTab { }) ); - new Setting(containerEl) + let modelDropdown: DropdownComponent; + + const modelSetting = new Setting(containerEl) .setName('OpenAI Model') .setDesc('Select the OpenAI model to use for processing') - .addDropdown(dropdown => dropdown - .addOption('gpt-5', 'GPT-5') - .addOption('gpt-5-mini', 'GPT-5 Mini') - .addOption('gpt-5-nano', 'GPT-5 Nano') - .setValue(this.plugin.settings.defaultModel) - .onChange(async (value) => { + .addDropdown(dropdown => { + modelDropdown = dropdown; + // Populate dropdown from cached models + const models = this.plugin.settings.cachedModels; + models.forEach(model => { + dropdown.addOption(model, model); + }); + // Ensure current selection is valid, fallback to first available + const currentModel = this.plugin.settings.defaultModel; + if (models.includes(currentModel)) { + dropdown.setValue(currentModel); + } else if (models.length > 0) { + dropdown.setValue(models[0]); + this.plugin.settings.defaultModel = models[0]; + this.plugin.saveSettings(); + } + dropdown.onChange(async (value) => { this.plugin.settings.defaultModel = value; await this.plugin.saveSettings(); + }); + }) + .addButton(button => button + .setButtonText('Refresh Models') + .setDisabled(!this.plugin.settings.apiKey) + .onClick(async () => { + if (!this.plugin.settings.apiKey) { + new Notice('Please set your API key first'); + return; + } + + button.setButtonText('Loading...'); + button.setDisabled(true); + + try { + const models = await OpenAIService.fetchAvailableModels(this.plugin.settings.apiKey); + + if (models.length === 0) { + new Notice('No chat models found'); + return; + } + + // Update cached models + this.plugin.settings.cachedModels = models; + + // Ensure current selection is still valid + if (!models.includes(this.plugin.settings.defaultModel)) { + this.plugin.settings.defaultModel = models[0]; + } + + await this.plugin.saveSettings(); + + // Rebuild dropdown options + modelDropdown.selectEl.empty(); + models.forEach(model => { + modelDropdown.addOption(model, model); + }); + modelDropdown.setValue(this.plugin.settings.defaultModel); + + new Notice(`Loaded ${models.length} models`); + } catch (error) { + console.error('Failed to fetch models:', error); + new Notice(`Failed to fetch models: ${error instanceof Error ? error.message : 'Unknown error'}`); + } finally { + button.setButtonText('Refresh Models'); + button.setDisabled(!this.plugin.settings.apiKey); + } }) );