Fix available models checking and models' choice settings

This commit is contained in:
dimitrievgs 2026-05-01 22:43:00 +03:00
parent 37bbc44741
commit fde784afbf
4 changed files with 104 additions and 52 deletions

110
main.ts
View File

@ -18,12 +18,13 @@ interface LLMAgentSettings {
excludedFolders: string[]; excludedFolders: string[];
refCacheFolder: string; refCacheFolder: string;
voiceModel: string; summarizationModel: string;
voiceCommandModel: string;
voiceInterval: number; voiceInterval: number;
voiceDisplayLimit: number; // 3000 voiceDisplayLimit: number; // 3000
voiceContextLimit: number; // N2 символов для LLM voiceContextLimit: number; // N2 символов для LLM
promptRegularPath: string; promptRegular: string;
promptCommandsPath: string; promptCommands: string;
markerStart: string; // "старт" markerStart: string; // "старт"
markerStop: string; // "стоп" markerStop: string; // "стоп"
} }
@ -42,8 +43,8 @@ const DEFAULT_SETTINGS: LLMAgentSettings = {
voiceInterval: 1, voiceInterval: 1,
voiceDisplayLimit: 3000, voiceDisplayLimit: 3000,
voiceContextLimit: 3000, voiceContextLimit: 3000,
promptRegularPath: '', promptRegular: '',
promptCommandsPath: '', promptCommands: '',
markerStart: 'старт', markerStart: 'старт',
markerStop: 'стоп' markerStop: 'стоп'
}; };
@ -55,8 +56,9 @@ export default class LLMAgentPlugin extends Plugin {
eventBus: EventBus; eventBus: EventBus;
webSocketService: WebSocketService; webSocketService: WebSocketService;
activeStreamControllers: Map<string, AbortController>; activeStreamControllers: Map<string, AbortController>;
availableModels: string[];
async syncVoiceSettings() { async syncSettings() {
try { try {
const response = await fetch(`${this.settings.apiBaseUrl}/settings/sync`, { const response = await fetch(`${this.settings.apiBaseUrl}/settings/sync`, {
method: 'POST', method: 'POST',
@ -71,6 +73,24 @@ export default class LLMAgentPlugin extends Plugin {
} }
} }
/**
* Загружает список доступных моделей с бэкенда.
* @returns {Promise<void>}
*/
async fetchAvailableModels(): Promise<void> {
try {
const response = await fetch(`${this.settings.apiBaseUrl}/models`);
if (!response.ok) {
throw new Error(`Ошибка загрузки моделей: ${response.status}`);
}
this.availableModels = await response.json();
} catch (error) {
console.error('Ошибка загрузки доступных моделей:', error);
// Fallback к базовым моделям
this.availableModels = ['gemini-2.5-flash', 'gpt-4o-mini', 'claude-3-5-sonnet-20241022'];
}
}
async onload() { async onload() {
await this.loadSettings(); await this.loadSettings();
@ -118,9 +138,9 @@ export default class LLMAgentPlugin extends Plugin {
// This adds a settings tab so the user can configure various aspects of the plugin // This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this)); this.addSettingTab(new SampleSettingTab(this.app, this));
await this.fetchAvailableModels();
// Вызываем один раз при запуске // Вызываем один раз при запуске
await this.syncVoiceSettings(); await this.syncSettings();
} }
onunload() { onunload() {
@ -144,6 +164,7 @@ export default class LLMAgentPlugin extends Plugin {
async saveSettings() { async saveSettings() {
await this.saveData(this.settings); await this.saveData(this.settings);
this.eventBus.emit('settings-changed');
} }
// ----------------------------------------------- View Management --------------------------------------------------------------- // ----------------------------------------------- View Management ---------------------------------------------------------------
@ -206,6 +227,32 @@ class SampleSettingTab extends PluginSettingTab {
containerEl.createEl('h2', { text: 'Основные настройки' }); containerEl.createEl('h2', { text: 'Основные настройки' });
new Setting(containerEl)
.setName('Модель по умолчанию')
.setDesc('Выберите модель для основного чата.')
.addDropdown((dropdown) => {
this.plugin.availableModels.forEach(model => dropdown.addOption(model, model));
dropdown.setValue(this.plugin.settings.defaultModel)
.onChange(async (value) => {
this.plugin.settings.defaultModel = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
});
});
new Setting(containerEl)
.setName('Модель суммаризации')
.setDesc('Выберите модель для суммаризации.')
.addDropdown((dropdown) => {
this.plugin.availableModels.forEach(model => dropdown.addOption(model, model));
dropdown.setValue(this.plugin.settings.summarizationModel)
.onChange(async (value) => {
this.plugin.settings.summarizationModel = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
});
});
new Setting(containerEl) new Setting(containerEl)
.setName('API Base URL') .setName('API Base URL')
.setDesc('Базовый URL для API LLM Agent') .setDesc('Базовый URL для API LLM Agent')
@ -216,6 +263,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.apiBaseUrl = value; this.plugin.settings.apiBaseUrl = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
@ -229,11 +277,25 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.systemPrompt = value; this.plugin.settings.systemPrompt = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
containerEl.createEl('h2', { text: 'Голосовое управление (Voice Service)' }); containerEl.createEl('h2', { text: 'Голосовое управление (Voice Service)' });
new Setting(containerEl)
.setName('Модель команд')
.setDesc('LLM для обработки голосовых команд.')
.addDropdown((dropdown) => {
this.plugin.availableModels.forEach(model => dropdown.addOption(model, model));
dropdown.setValue(this.plugin.settings.voiceCommandModel)
.onChange(async (value) => {
this.plugin.settings.voiceCommandModel = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
});
});
new Setting(containerEl) new Setting(containerEl)
.setName('Интервал регулярного анализа (мин)') .setName('Интервал регулярного анализа (мин)')
.setDesc('Как часто LLM анализирует последние фразы (Prompt 1).') .setDesc('Как часто LLM анализирует последние фразы (Prompt 1).')
@ -243,7 +305,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.voiceInterval = Number(value); this.plugin.settings.voiceInterval = Number(value);
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings(); await this.plugin.syncSettings();
}) })
); );
@ -256,7 +318,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.voiceContextLimit = Number(value); this.plugin.settings.voiceContextLimit = Number(value);
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings(); await this.plugin.syncSettings();
}) })
); );
@ -270,7 +332,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.markerStart = value; this.plugin.settings.markerStart = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings(); await this.plugin.syncSettings();
}) })
); );
@ -284,7 +346,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.markerStop = value; this.plugin.settings.markerStop = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings(); await this.plugin.syncSettings();
}) })
); );
@ -294,11 +356,11 @@ class SampleSettingTab extends PluginSettingTab {
.addTextArea((text) => .addTextArea((text) =>
text text
// .setPlaceholder('prompts/regular_analysis.md') // .setPlaceholder('prompts/regular_analysis.md')
.setValue(this.plugin.settings.promptRegularPath) .setValue(this.plugin.settings.promptRegular)
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.promptRegularPath = value; this.plugin.settings.promptRegular = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings(); await this.plugin.syncSettings();
}) })
); );
@ -308,11 +370,11 @@ class SampleSettingTab extends PluginSettingTab {
.addTextArea((text) => .addTextArea((text) =>
text text
// .setPlaceholder('prompts/voice_commands.md') // .setPlaceholder('prompts/voice_commands.md')
.setValue(this.plugin.settings.promptCommandsPath) .setValue(this.plugin.settings.promptCommands)
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.promptCommandsPath = value; this.plugin.settings.promptCommands = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings(); await this.plugin.syncSettings();
}) })
); );
@ -328,6 +390,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.promptsFolder = value; this.plugin.settings.promptsFolder = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
@ -341,6 +404,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.refCacheFolder = value; this.plugin.settings.refCacheFolder = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
@ -352,6 +416,7 @@ class SampleSettingTab extends PluginSettingTab {
toggle.setValue(this.plugin.settings.enableFileReferences).onChange(async (value) => { toggle.setValue(this.plugin.settings.enableFileReferences).onChange(async (value) => {
this.plugin.settings.enableFileReferences = value; this.plugin.settings.enableFileReferences = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
@ -363,6 +428,7 @@ class SampleSettingTab extends PluginSettingTab {
toggle.setValue(this.plugin.settings.enablePromptReferences).onChange(async (value) => { toggle.setValue(this.plugin.settings.enablePromptReferences).onChange(async (value) => {
this.plugin.settings.enablePromptReferences = value; this.plugin.settings.enablePromptReferences = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
@ -374,6 +440,7 @@ class SampleSettingTab extends PluginSettingTab {
toggle.setValue(this.plugin.settings.enableFileReferences).onChange(async (value) => { toggle.setValue(this.plugin.settings.enableFileReferences).onChange(async (value) => {
this.plugin.settings.enableFileReferences = value; this.plugin.settings.enableFileReferences = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
}) })
); );
@ -386,8 +453,9 @@ class SampleSettingTab extends PluginSettingTab {
text.setPlaceholder('Путь к папке (например, templates/)' + index) text.setPlaceholder('Путь к папке (например, templates/)' + index)
.setValue(folder) .setValue(folder)
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.excludedFolders[index] = value; this.plugin.settings.excludedFolders[index] = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
// Очищаем кэш автокомплита при изменении настроек // Очищаем кэш автокомплита при изменении настроек
this.plugin.eventBus.emit('settings-changed'); this.plugin.eventBus.emit('settings-changed');
}); });
@ -399,6 +467,7 @@ class SampleSettingTab extends PluginSettingTab {
.onClick(async () => { .onClick(async () => {
this.plugin.settings.excludedFolders.splice(index, 1); this.plugin.settings.excludedFolders.splice(index, 1);
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
this.display(); // Перерисовать настройки для обновления списка this.display(); // Перерисовать настройки для обновления списка
// Очищаем кэш автокомплита при изменении настроек // Очищаем кэш автокомплита при изменении настроек
this.plugin.eventBus.emit('settings-changed'); this.plugin.eventBus.emit('settings-changed');
@ -413,6 +482,7 @@ class SampleSettingTab extends PluginSettingTab {
.onClick(async () => { .onClick(async () => {
this.plugin.settings.excludedFolders.push(''); // Добавляем пустое поле this.plugin.settings.excludedFolders.push(''); // Добавляем пустое поле
await this.plugin.saveSettings(); await this.plugin.saveSettings();
await this.plugin.syncSettings();
this.display(); // Перерисовать настройки для обновления списка this.display(); // Перерисовать настройки для обновления списка
}); });
}); });

View File

@ -19,7 +19,6 @@ import { CacheManager } from 'src/references/CacheManager';
interface ChatPanelProps { interface ChatPanelProps {
chatHistory: ChatHistoryItem[]; chatHistory: ChatHistoryItem[];
onSendMessage: (message: string, selectedModel?: string, attachments?: Attachment[]) => void; onSendMessage: (message: string, selectedModel?: string, attachments?: Attachment[]) => void;
availableModels?: string[];
selectedModel?: string; selectedModel?: string;
plugin: LLMAgentPlugin; plugin: LLMAgentPlugin;
currentGraphCustomSystemPrompt: string | null; currentGraphCustomSystemPrompt: string | null;
@ -133,9 +132,9 @@ export class ChatPanel {
} }
updateModelDropdown(): void { updateModelDropdown(): void {
if (!this.props.availableModels) return; if (!this.props.plugin.availableModels) return;
this.modelDropdown.innerHTML = this.props.availableModels this.modelDropdown.innerHTML = this.props.plugin.availableModels
.map( .map(
(model) => ` (model) => `
<div class="model-option" data-model="${model}"> <div class="model-option" data-model="${model}">
@ -410,11 +409,6 @@ export class ChatPanel {
await this.renderHistory(); await this.renderHistory();
} }
updateAvailableModels(models: string[]): void {
this.props.availableModels = models;
this.updateModelDropdown();
}
destroy(): void { destroy(): void {
if (this.referenceAutocomplete) { if (this.referenceAutocomplete) {
this.referenceAutocomplete.destroy(); this.referenceAutocomplete.destroy();

View File

@ -66,7 +66,7 @@ export class VoicePanel {
if (!body) return; if (!body) return;
if (data.obsidian_settings_fetched == false) // если это не сделать, то на беке останутся пустые настройки, если обсидиан запущен раньше бека if (data.obsidian_settings_fetched == false) // если это не сделать, то на беке останутся пустые настройки, если обсидиан запущен раньше бека
{ {
this.plugin.syncVoiceSettings(); this.plugin.syncSettings();
} }
if (this.activeTab === 'trans') body.textContent = data.transcription; if (this.activeTab === 'trans') body.textContent = data.transcription;
if (this.activeTab === 'res1') body.textContent = data.response1; if (this.activeTab === 'res1') body.textContent = data.response1;

View File

@ -19,7 +19,6 @@ export class ChatView extends ItemView {
currentNode: string | null; currentNode: string | null;
chatContainer: HTMLDivElement; chatContainer: HTMLDivElement;
chatPanel: ChatPanel | null; chatPanel: ChatPanel | null;
availableModels: string[];
currentGraphCustomSystemPrompt: string | null; currentGraphCustomSystemPrompt: string | null;
currentStreamingNodeId: string | null = null; currentStreamingNodeId: string | null = null;
@ -30,7 +29,6 @@ export class ChatView extends ItemView {
this.selectedGraphId = null; this.selectedGraphId = null;
this.currentNode = null; this.currentNode = null;
this.chatPanel = null; this.chatPanel = null;
this.availableModels = [];
this.currentGraphCustomSystemPrompt = null; this.currentGraphCustomSystemPrompt = null;
} }
@ -54,19 +52,19 @@ export class ChatView extends ItemView {
this.chatContainer = container.createDiv() as HTMLDivElement; this.chatContainer = container.createDiv() as HTMLDivElement;
this.chatContainer.addClass('llm-agent-chat-container'); this.chatContainer.addClass('llm-agent-chat-container');
// Загружаем доступные модели // Загружаем доступные модели (TODO: проверить, что здесь это нужно, что не сделали этого ранее)
await this.fetchAvailableModels(); // await this.plugin.fetchAvailableModels();
// Инициализируем только чат панель с передачей plugin // Инициализируем только чат панель с передачей plugin
this.chatPanel = new ChatPanel(this.chatContainer, { this.chatPanel = new ChatPanel(this.chatContainer, {
chatHistory: this.chatHistory, chatHistory: this.chatHistory,
onSendMessage: this.handleSendMessage.bind(this), onSendMessage: this.handleSendMessage.bind(this),
availableModels: this.availableModels,
selectedModel: this.plugin.settings.defaultModel || 'gemini-2.5-flash', selectedModel: this.plugin.settings.defaultModel || 'gemini-2.5-flash',
plugin: this.plugin, plugin: this.plugin,
currentGraphCustomSystemPrompt: this.currentGraphCustomSystemPrompt currentGraphCustomSystemPrompt: this.currentGraphCustomSystemPrompt
}); });
this.plugin.eventBus.on('settings-changed', this.handleSettingsChanged.bind(this));
this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this)); this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this));
this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this));
this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this)); this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this));
@ -84,6 +82,7 @@ export class ChatView extends ItemView {
this.chatPanel.destroy(); this.chatPanel.destroy();
this.chatPanel = null; this.chatPanel = null;
} }
this.plugin.eventBus.off('settings-changed', this.handleSettingsChanged);
this.plugin.eventBus.off('node-selected', this.handleNodeSelected); this.plugin.eventBus.off('node-selected', this.handleNodeSelected);
this.plugin.eventBus.off('new-chat', this.handleNewChat); this.plugin.eventBus.off('new-chat', this.handleNewChat);
this.plugin.eventBus.off('graph-selected', this.handleGraphSelected); this.plugin.eventBus.off('graph-selected', this.handleGraphSelected);
@ -154,24 +153,6 @@ export class ChatView extends ItemView {
} }
} }
/**
* Загружает список доступных моделей с бэкенда.
* @returns {Promise<void>}
*/
async fetchAvailableModels(): Promise<void> {
try {
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/models`);
if (!response.ok) {
throw new Error(`Ошибка загрузки моделей: ${response.status}`);
}
this.availableModels = await response.json();
} catch (error) {
console.error('Ошибка загрузки доступных моделей:', error);
// Fallback к базовым моделям
this.availableModels = ['gemini-2.5-flash', 'gpt-4o-mini', 'claude-3-5-sonnet-20241022'];
}
}
// ----------------------------------------------- Event Handlers --------------------------------------------------------------- // ----------------------------------------------- Event Handlers ---------------------------------------------------------------
async handleSendMessage(message: string, selectedModel?: string, attachments?: Attachment[]): Promise<void> { async handleSendMessage(message: string, selectedModel?: string, attachments?: Attachment[]): Promise<void> {
@ -227,6 +208,13 @@ export class ChatView extends ItemView {
} }
} }
handleSettingsChanged(): void {
if (this.chatPanel)
{
this.chatPanel.setSelectedModel(this.plugin.settings.defaultModel);
}
}
/** /**
* Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла * Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла
* и обновляет панель чата. * и обновляет панель чата.