diff --git a/main.ts b/main.ts index d7784c9..25aee30 100644 --- a/main.ts +++ b/main.ts @@ -10,6 +10,8 @@ import { ReferenceParser } from './src/references/ReferenceParser'; import 'src/css/styles.css'; +import { LLM_DEFAULTS } from 'src/constants'; + // ----------------------------------------------- Settings --------------------------------------------------------------- interface LLMAgentSettings { @@ -22,29 +24,38 @@ interface LLMAgentSettings { excludedFolders: string[]; refCacheFolder: string; + temperatureChat: number; + maxTokensChatDefault: string; + summarizationModel: string; voiceCommandModel: string; logsFolder: string; voiceInterval: number; metricsInterval: number; - voiceDisplayLimit: number; // 3000 - voiceContextLimit: number; // N2 символов для LLM + voiceDisplayLimit: number; + voiceContextLimit: number; + temperatureVoice: number; + maxTokensVoiceRegular: string; + maxTokensVoiceCommands: string; promptRegular: string; promptCommands: string; promptMetrics: string; - markerStart: string; // "старт" - markerStop: string; // "стоп" + markerStart: string; + markerStop: string; } const DEFAULT_SETTINGS: LLMAgentSettings = { apiBaseUrl: 'http://localhost:5000/api', systemPrompt: 'Ты полезный ИИ ассистент.', - defaultModel: 'gemini-2.5-flash-r', - promptsFolder: 'prompts', // Папка с промптами по умолчанию + defaultModel: LLM_DEFAULTS.MODEL, + promptsFolder: 'prompts', enableFileReferences: true, enablePromptReferences: true, - excludedFolders: ['.obsidian', 'llm-agent-backend', 'templates', 'ref-cache'], // Папка "templates" по умолчанию исключена - refCacheFolder: 'ref-cache', // По умолчанию относительно vault + excludedFolders: ['.obsidian', 'llm-agent-backend', 'templates', 'ref-cache'], + refCacheFolder: 'ref-cache', + + temperatureChat: LLM_DEFAULTS.TEMPERATURE, + maxTokensChatDefault: '', summarizationModel: '', voiceCommandModel: '', @@ -53,6 +64,9 @@ const DEFAULT_SETTINGS: LLMAgentSettings = { metricsInterval: 15, voiceDisplayLimit: 30000, voiceContextLimit: 30000, + temperatureVoice: LLM_DEFAULTS.TEMPERATURE, + maxTokensVoiceRegular: '', + maxTokensVoiceCommands: '', promptRegular: '', promptCommands: '', promptMetrics: '', @@ -116,7 +130,7 @@ export default class LLMAgentPlugin extends Plugin { } catch (error) { console.error('Ошибка загрузки доступных моделей:', error); // Fallback к базовым моделям - this.availableModels = ['gemini-2.5-flash', 'gpt-4o-mini', 'claude-3-5-sonnet-20241022']; + this.availableModels = [LLM_DEFAULTS.MODEL, 'gpt-4o-mini', 'claude-3-5-sonnet-20241022']; } } @@ -297,6 +311,38 @@ class SampleSettingTab extends PluginSettingTab { }); }); + // Этот промпт будет отправляться LLM перед каждым диалогом + this.addPromptAutocomplete(new Setting(containerEl).setName('Системный промпт'), 'systemPrompt'); + + new Setting(containerEl) + .setName('Температура чата по умолчанию') + .setDesc(`Значение от 0.0 до 2.0 (по умолчанию ${LLM_DEFAULTS.TEMPERATURE}). Влияет на креативность.`) + .addText((text) => + text + .setPlaceholder(String(LLM_DEFAULTS.TEMPERATURE)) + .setValue(String(this.plugin.settings.temperatureChat)) + .onChange(async (value) => { + const num = parseFloat(value); + this.plugin.settings.temperatureChat = isNaN(num) ? LLM_DEFAULTS.TEMPERATURE : num; + await this.plugin.saveSettings(); + await this.plugin.syncSettings(); + }) + ); + + new Setting(containerEl) + .setName('Макс. токенов чата по умолчанию') + .setDesc('Оставьте пустым для отсутствия лимита.') + .addText((text) => + text + .setPlaceholder('Лимит') + .setValue(this.plugin.settings.maxTokensChatDefault) + .onChange(async (value) => { + this.plugin.settings.maxTokensChatDefault = value; + await this.plugin.saveSettings(); + await this.plugin.syncSettings(); + }) + ); + new Setting(containerEl) .setName('API Base URL') .setDesc('Базовый URL для API LLM Agent') @@ -311,9 +357,6 @@ class SampleSettingTab extends PluginSettingTab { }) ); - // Этот промпт будет отправляться LLM перед каждым диалогом - this.addPromptAutocomplete(new Setting(containerEl).setName('Системный промпт'), 'systemPrompt'); - containerEl.createEl('h2', { text: 'Голосовое управление (Voice Service)' }); new Setting(containerEl) @@ -329,16 +372,61 @@ class SampleSettingTab extends PluginSettingTab { }); }); + // Инструкции для Промпта №1. + this.addPromptAutocomplete( + new Setting(containerEl).setName('Регулярный промпт'), + 'promptRegular' + ); + + // Инструкции для Промпта №2. + this.addPromptAutocomplete( + new Setting(containerEl).setName('Промпт команд'), + 'promptCommands' + ); + + this.addPromptAutocomplete( + new Setting(containerEl).setName('Промпт качественной аналитики'), + 'promptMetrics' + ); + new Setting(containerEl) - .setName('Папка аудио-логов') - .setDesc('Папка внутри Obsidian, куда будут сохраняться текстовые расшифровки (в формате .md). Относительно корня хранилища.') + .setName('Температура Voice') + .setDesc(`Температура для голосовых запросов (по умолчанию ${LLM_DEFAULTS.TEMPERATURE})`) .addText((text) => text - .setPlaceholder('audio-logs') - .setValue(this.plugin.settings.logsFolder) + .setPlaceholder(String(LLM_DEFAULTS.TEMPERATURE)) + .setValue(String(this.plugin.settings.temperatureVoice)) .onChange(async (value) => { - // Убираем лишние слэши для корректности пути - this.plugin.settings.logsFolder = value.replace(/^\/+|\/+$/g, '').trim(); + const num = parseFloat(value); + this.plugin.settings.temperatureVoice = isNaN(num) ? LLM_DEFAULTS.TEMPERATURE : num; + await this.plugin.saveSettings(); + await this.plugin.syncSettings(); + }) + ); + + new Setting(containerEl) + .setName('Макс. токенов Voice (Регулярный)') + .setDesc('Лимит токенов для регулярного анализа. Оставьте пустым для безлимита.') + .addText((text) => + text + .setPlaceholder('Лимит') + .setValue(this.plugin.settings.maxTokensVoiceRegular) + .onChange(async (value) => { + this.plugin.settings.maxTokensVoiceRegular = value; + await this.plugin.saveSettings(); + await this.plugin.syncSettings(); + }) + ); + + new Setting(containerEl) + .setName('Макс. токенов Voice (Команды)') + .setDesc('Лимит токенов для голосовых команд. Оставьте пустым для безлимита.') + .addText((text) => + text + .setPlaceholder('Лимит') + .setValue(this.plugin.settings.maxTokensVoiceCommands) + .onChange(async (value) => { + this.plugin.settings.maxTokensVoiceCommands = value; await this.plugin.saveSettings(); await this.plugin.syncSettings(); }) @@ -370,6 +458,21 @@ class SampleSettingTab extends PluginSettingTab { }) ); + new Setting(containerEl) + .setName('Папка аудио-логов') + .setDesc('Папка внутри Obsidian, куда будут сохраняться текстовые расшифровки (в формате .md). Относительно корня хранилища.') + .addText((text) => + text + .setPlaceholder('audio-logs') + .setValue(this.plugin.settings.logsFolder) + .onChange(async (value) => { + // Убираем лишние слэши для корректности пути + this.plugin.settings.logsFolder = value.replace(/^\/+|\/+$/g, '').trim(); + await this.plugin.saveSettings(); + await this.plugin.syncSettings(); + }) + ); + new Setting(containerEl) .setName('Объем контекста (символы)') .setDesc('Сколько последних символов транскрибации отправлять в модель.') @@ -411,23 +514,6 @@ class SampleSettingTab extends PluginSettingTab { }) ); - // Инструкции для Промпта №1. - this.addPromptAutocomplete( - new Setting(containerEl).setName('Регулярный промпт'), - 'promptRegular' - ); - - // Инструкции для Промпта №2. - this.addPromptAutocomplete( - new Setting(containerEl).setName('Промпт команд'), - 'promptCommands' - ); - - this.addPromptAutocomplete( - new Setting(containerEl).setName('Промпт качественной аналитики'), - 'promptMetrics' - ); - containerEl.createEl('h2', { text: 'Управление ссылками и файлами' }); new Setting(containerEl) diff --git a/src/components/ChatPanel.ts b/src/components/ChatPanel.ts index 5eca47d..415566f 100644 --- a/src/components/ChatPanel.ts +++ b/src/components/ChatPanel.ts @@ -16,9 +16,11 @@ import { MarkdownRenderer, setIcon, Notice, TFile, FileSystemAdapter } from 'obs import { Dialog } from 'src/utils/Dialog'; import { CacheManager } from 'src/references/CacheManager'; +import { LLM_DEFAULTS } from 'src/constants'; + interface ChatPanelProps { chatHistory: ChatHistoryItem[]; - onSendMessage: (message: string, selectedModel?: string, attachments?: Attachment[]) => void; + onSendMessage: (message: string, selectedModel?: string, attachments?: Attachment[], customMaxTokens?: number, customTemperature?: number) => void; selectedModel?: string; plugin: LLMAgentPlugin; currentGraphCustomSystemPrompt: string | null; @@ -38,6 +40,11 @@ export class ChatPanel { currentStreamingNodeId: string | null; stopButton: HTMLButtonElement; + maxTokensInput: HTMLInputElement; + maxTokensResetBtn: HTMLButtonElement; + tempInput: HTMLInputElement; + tempResetBtn: HTMLButtonElement; + // Элементы для файловых вложений attachFileButton: HTMLButtonElement; fileInput: HTMLInputElement; @@ -58,7 +65,7 @@ export class ChatPanel { this.container = container; this.props = props; this.chatHistory = props.chatHistory || []; - this.selectedModel = props.selectedModel || 'gemini-2.5-flash'; + this.selectedModel = props.selectedModel || LLM_DEFAULTS.MODEL; // Инициализация системы ссылок this.initializeReferenceSystem(); @@ -466,6 +473,16 @@ export class ChatPanel { ⚙️ +
+ Temp: + + +
+
+ Tokens: + + +