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

106
main.ts
View File

@ -18,12 +18,13 @@ interface LLMAgentSettings {
excludedFolders: string[];
refCacheFolder: string;
voiceModel: string;
summarizationModel: string;
voiceCommandModel: string;
voiceInterval: number;
voiceDisplayLimit: number; // 3000
voiceContextLimit: number; // N2 символов для LLM
promptRegularPath: string;
promptCommandsPath: string;
promptRegular: string;
promptCommands: string;
markerStart: string; // "старт"
markerStop: string; // "стоп"
}
@ -42,8 +43,8 @@ const DEFAULT_SETTINGS: LLMAgentSettings = {
voiceInterval: 1,
voiceDisplayLimit: 3000,
voiceContextLimit: 3000,
promptRegularPath: '',
promptCommandsPath: '',
promptRegular: '',
promptCommands: '',
markerStart: 'старт',
markerStop: 'стоп'
};
@ -55,8 +56,9 @@ export default class LLMAgentPlugin extends Plugin {
eventBus: EventBus;
webSocketService: WebSocketService;
activeStreamControllers: Map<string, AbortController>;
availableModels: string[];
async syncVoiceSettings() {
async syncSettings() {
try {
const response = await fetch(`${this.settings.apiBaseUrl}/settings/sync`, {
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() {
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.addSettingTab(new SampleSettingTab(this.app, this));
await this.fetchAvailableModels();
// Вызываем один раз при запуске
await this.syncVoiceSettings();
await this.syncSettings();
}
onunload() {
@ -144,6 +164,7 @@ export default class LLMAgentPlugin extends Plugin {
async saveSettings() {
await this.saveData(this.settings);
this.eventBus.emit('settings-changed');
}
// ----------------------------------------------- View Management ---------------------------------------------------------------
@ -206,6 +227,32 @@ class SampleSettingTab extends PluginSettingTab {
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)
.setName('API Base URL')
.setDesc('Базовый URL для API LLM Agent')
@ -216,6 +263,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.apiBaseUrl = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
})
);
@ -229,11 +277,25 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.systemPrompt = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
})
);
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)
.setName('Интервал регулярного анализа (мин)')
.setDesc('Как часто LLM анализирует последние фразы (Prompt 1).')
@ -243,7 +305,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.voiceInterval = Number(value);
await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings();
await this.plugin.syncSettings();
})
);
@ -256,7 +318,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.voiceContextLimit = Number(value);
await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings();
await this.plugin.syncSettings();
})
);
@ -270,7 +332,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.markerStart = value;
await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings();
await this.plugin.syncSettings();
})
);
@ -284,7 +346,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.markerStop = value;
await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings();
await this.plugin.syncSettings();
})
);
@ -294,11 +356,11 @@ class SampleSettingTab extends PluginSettingTab {
.addTextArea((text) =>
text
// .setPlaceholder('prompts/regular_analysis.md')
.setValue(this.plugin.settings.promptRegularPath)
.setValue(this.plugin.settings.promptRegular)
.onChange(async (value) => {
this.plugin.settings.promptRegularPath = value;
this.plugin.settings.promptRegular = value;
await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings();
await this.plugin.syncSettings();
})
);
@ -308,11 +370,11 @@ class SampleSettingTab extends PluginSettingTab {
.addTextArea((text) =>
text
// .setPlaceholder('prompts/voice_commands.md')
.setValue(this.plugin.settings.promptCommandsPath)
.setValue(this.plugin.settings.promptCommands)
.onChange(async (value) => {
this.plugin.settings.promptCommandsPath = value;
this.plugin.settings.promptCommands = value;
await this.plugin.saveSettings();
await this.plugin.syncVoiceSettings();
await this.plugin.syncSettings();
})
);
@ -328,6 +390,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.promptsFolder = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
})
);
@ -341,6 +404,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.refCacheFolder = value;
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) => {
this.plugin.settings.enableFileReferences = value;
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) => {
this.plugin.settings.enablePromptReferences = value;
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) => {
this.plugin.settings.enableFileReferences = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
})
);
@ -388,6 +455,7 @@ class SampleSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.excludedFolders[index] = value;
await this.plugin.saveSettings();
await this.plugin.syncSettings();
// Очищаем кэш автокомплита при изменении настроек
this.plugin.eventBus.emit('settings-changed');
});
@ -399,6 +467,7 @@ class SampleSettingTab extends PluginSettingTab {
.onClick(async () => {
this.plugin.settings.excludedFolders.splice(index, 1);
await this.plugin.saveSettings();
await this.plugin.syncSettings();
this.display(); // Перерисовать настройки для обновления списка
// Очищаем кэш автокомплита при изменении настроек
this.plugin.eventBus.emit('settings-changed');
@ -413,6 +482,7 @@ class SampleSettingTab extends PluginSettingTab {
.onClick(async () => {
this.plugin.settings.excludedFolders.push(''); // Добавляем пустое поле
await this.plugin.saveSettings();
await this.plugin.syncSettings();
this.display(); // Перерисовать настройки для обновления списка
});
});

View File

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

View File

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

View File

@ -19,7 +19,6 @@ export class ChatView extends ItemView {
currentNode: string | null;
chatContainer: HTMLDivElement;
chatPanel: ChatPanel | null;
availableModels: string[];
currentGraphCustomSystemPrompt: string | null;
currentStreamingNodeId: string | null = null;
@ -30,7 +29,6 @@ export class ChatView extends ItemView {
this.selectedGraphId = null;
this.currentNode = null;
this.chatPanel = null;
this.availableModels = [];
this.currentGraphCustomSystemPrompt = null;
}
@ -54,19 +52,19 @@ export class ChatView extends ItemView {
this.chatContainer = container.createDiv() as HTMLDivElement;
this.chatContainer.addClass('llm-agent-chat-container');
// Загружаем доступные модели
await this.fetchAvailableModels();
// Загружаем доступные модели (TODO: проверить, что здесь это нужно, что не сделали этого ранее)
// await this.plugin.fetchAvailableModels();
// Инициализируем только чат панель с передачей plugin
this.chatPanel = new ChatPanel(this.chatContainer, {
chatHistory: this.chatHistory,
onSendMessage: this.handleSendMessage.bind(this),
availableModels: this.availableModels,
selectedModel: this.plugin.settings.defaultModel || 'gemini-2.5-flash',
plugin: this.plugin,
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('new-chat', this.handleNewChat.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 = null;
}
this.plugin.eventBus.off('settings-changed', this.handleSettingsChanged);
this.plugin.eventBus.off('node-selected', this.handleNodeSelected);
this.plugin.eventBus.off('new-chat', this.handleNewChat);
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 ---------------------------------------------------------------
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);
}
}
/**
* Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла
* и обновляет панель чата.