Add support of temperature and max tokens parameters
This commit is contained in:
parent
43d855a27d
commit
9c5706bd86
156
main.ts
156
main.ts
|
|
@ -10,6 +10,8 @@ import { ReferenceParser } from './src/references/ReferenceParser';
|
||||||
|
|
||||||
import 'src/css/styles.css';
|
import 'src/css/styles.css';
|
||||||
|
|
||||||
|
import { LLM_DEFAULTS } from 'src/constants';
|
||||||
|
|
||||||
// ----------------------------------------------- Settings ---------------------------------------------------------------
|
// ----------------------------------------------- Settings ---------------------------------------------------------------
|
||||||
|
|
||||||
interface LLMAgentSettings {
|
interface LLMAgentSettings {
|
||||||
|
|
@ -22,29 +24,38 @@ interface LLMAgentSettings {
|
||||||
excludedFolders: string[];
|
excludedFolders: string[];
|
||||||
refCacheFolder: string;
|
refCacheFolder: string;
|
||||||
|
|
||||||
|
temperatureChat: number;
|
||||||
|
maxTokensChatDefault: string;
|
||||||
|
|
||||||
summarizationModel: string;
|
summarizationModel: string;
|
||||||
voiceCommandModel: string;
|
voiceCommandModel: string;
|
||||||
logsFolder: string;
|
logsFolder: string;
|
||||||
voiceInterval: number;
|
voiceInterval: number;
|
||||||
metricsInterval: number;
|
metricsInterval: number;
|
||||||
voiceDisplayLimit: number; // 3000
|
voiceDisplayLimit: number;
|
||||||
voiceContextLimit: number; // N2 символов для LLM
|
voiceContextLimit: number;
|
||||||
|
temperatureVoice: number;
|
||||||
|
maxTokensVoiceRegular: string;
|
||||||
|
maxTokensVoiceCommands: string;
|
||||||
promptRegular: string;
|
promptRegular: string;
|
||||||
promptCommands: string;
|
promptCommands: string;
|
||||||
promptMetrics: string;
|
promptMetrics: string;
|
||||||
markerStart: string; // "старт"
|
markerStart: string;
|
||||||
markerStop: string; // "стоп"
|
markerStop: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: LLMAgentSettings = {
|
const DEFAULT_SETTINGS: LLMAgentSettings = {
|
||||||
apiBaseUrl: 'http://localhost:5000/api',
|
apiBaseUrl: 'http://localhost:5000/api',
|
||||||
systemPrompt: 'Ты полезный ИИ ассистент.',
|
systemPrompt: 'Ты полезный ИИ ассистент.',
|
||||||
defaultModel: 'gemini-2.5-flash-r',
|
defaultModel: LLM_DEFAULTS.MODEL,
|
||||||
promptsFolder: 'prompts', // Папка с промптами по умолчанию
|
promptsFolder: 'prompts',
|
||||||
enableFileReferences: true,
|
enableFileReferences: true,
|
||||||
enablePromptReferences: true,
|
enablePromptReferences: true,
|
||||||
excludedFolders: ['.obsidian', 'llm-agent-backend', 'templates', 'ref-cache'], // Папка "templates" по умолчанию исключена
|
excludedFolders: ['.obsidian', 'llm-agent-backend', 'templates', 'ref-cache'],
|
||||||
refCacheFolder: 'ref-cache', // По умолчанию относительно vault
|
refCacheFolder: 'ref-cache',
|
||||||
|
|
||||||
|
temperatureChat: LLM_DEFAULTS.TEMPERATURE,
|
||||||
|
maxTokensChatDefault: '',
|
||||||
|
|
||||||
summarizationModel: '',
|
summarizationModel: '',
|
||||||
voiceCommandModel: '',
|
voiceCommandModel: '',
|
||||||
|
|
@ -53,6 +64,9 @@ const DEFAULT_SETTINGS: LLMAgentSettings = {
|
||||||
metricsInterval: 15,
|
metricsInterval: 15,
|
||||||
voiceDisplayLimit: 30000,
|
voiceDisplayLimit: 30000,
|
||||||
voiceContextLimit: 30000,
|
voiceContextLimit: 30000,
|
||||||
|
temperatureVoice: LLM_DEFAULTS.TEMPERATURE,
|
||||||
|
maxTokensVoiceRegular: '',
|
||||||
|
maxTokensVoiceCommands: '',
|
||||||
promptRegular: '',
|
promptRegular: '',
|
||||||
promptCommands: '',
|
promptCommands: '',
|
||||||
promptMetrics: '',
|
promptMetrics: '',
|
||||||
|
|
@ -116,7 +130,7 @@ export default class LLMAgentPlugin extends Plugin {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки доступных моделей:', error);
|
console.error('Ошибка загрузки доступных моделей:', error);
|
||||||
// Fallback к базовым моделям
|
// 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)
|
new Setting(containerEl)
|
||||||
.setName('API Base URL')
|
.setName('API Base URL')
|
||||||
.setDesc('Базовый URL для API LLM Agent')
|
.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)' });
|
containerEl.createEl('h2', { text: 'Голосовое управление (Voice Service)' });
|
||||||
|
|
||||||
new Setting(containerEl)
|
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)
|
new Setting(containerEl)
|
||||||
.setName('Папка аудио-логов')
|
.setName('Температура Voice')
|
||||||
.setDesc('Папка внутри Obsidian, куда будут сохраняться текстовые расшифровки (в формате .md). Относительно корня хранилища.')
|
.setDesc(`Температура для голосовых запросов (по умолчанию ${LLM_DEFAULTS.TEMPERATURE})`)
|
||||||
.addText((text) =>
|
.addText((text) =>
|
||||||
text
|
text
|
||||||
.setPlaceholder('audio-logs')
|
.setPlaceholder(String(LLM_DEFAULTS.TEMPERATURE))
|
||||||
.setValue(this.plugin.settings.logsFolder)
|
.setValue(String(this.plugin.settings.temperatureVoice))
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
// Убираем лишние слэши для корректности пути
|
const num = parseFloat(value);
|
||||||
this.plugin.settings.logsFolder = value.replace(/^\/+|\/+$/g, '').trim();
|
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.saveSettings();
|
||||||
await this.plugin.syncSettings();
|
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)
|
new Setting(containerEl)
|
||||||
.setName('Объем контекста (символы)')
|
.setName('Объем контекста (символы)')
|
||||||
.setDesc('Сколько последних символов транскрибации отправлять в модель.')
|
.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: 'Управление ссылками и файлами' });
|
containerEl.createEl('h2', { text: 'Управление ссылками и файлами' });
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,11 @@ import { MarkdownRenderer, setIcon, Notice, TFile, FileSystemAdapter } from 'obs
|
||||||
import { Dialog } from 'src/utils/Dialog';
|
import { Dialog } from 'src/utils/Dialog';
|
||||||
import { CacheManager } from 'src/references/CacheManager';
|
import { CacheManager } from 'src/references/CacheManager';
|
||||||
|
|
||||||
|
import { LLM_DEFAULTS } from 'src/constants';
|
||||||
|
|
||||||
interface ChatPanelProps {
|
interface ChatPanelProps {
|
||||||
chatHistory: ChatHistoryItem[];
|
chatHistory: ChatHistoryItem[];
|
||||||
onSendMessage: (message: string, selectedModel?: string, attachments?: Attachment[]) => void;
|
onSendMessage: (message: string, selectedModel?: string, attachments?: Attachment[], customMaxTokens?: number, customTemperature?: number) => void;
|
||||||
selectedModel?: string;
|
selectedModel?: string;
|
||||||
plugin: LLMAgentPlugin;
|
plugin: LLMAgentPlugin;
|
||||||
currentGraphCustomSystemPrompt: string | null;
|
currentGraphCustomSystemPrompt: string | null;
|
||||||
|
|
@ -38,6 +40,11 @@ export class ChatPanel {
|
||||||
currentStreamingNodeId: string | null;
|
currentStreamingNodeId: string | null;
|
||||||
stopButton: HTMLButtonElement;
|
stopButton: HTMLButtonElement;
|
||||||
|
|
||||||
|
maxTokensInput: HTMLInputElement;
|
||||||
|
maxTokensResetBtn: HTMLButtonElement;
|
||||||
|
tempInput: HTMLInputElement;
|
||||||
|
tempResetBtn: HTMLButtonElement;
|
||||||
|
|
||||||
// Элементы для файловых вложений
|
// Элементы для файловых вложений
|
||||||
attachFileButton: HTMLButtonElement;
|
attachFileButton: HTMLButtonElement;
|
||||||
fileInput: HTMLInputElement;
|
fileInput: HTMLInputElement;
|
||||||
|
|
@ -58,7 +65,7 @@ export class ChatPanel {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.props = props;
|
this.props = props;
|
||||||
this.chatHistory = props.chatHistory || [];
|
this.chatHistory = props.chatHistory || [];
|
||||||
this.selectedModel = props.selectedModel || 'gemini-2.5-flash';
|
this.selectedModel = props.selectedModel || LLM_DEFAULTS.MODEL;
|
||||||
|
|
||||||
// Инициализация системы ссылок
|
// Инициализация системы ссылок
|
||||||
this.initializeReferenceSystem();
|
this.initializeReferenceSystem();
|
||||||
|
|
@ -466,6 +473,16 @@ export class ChatPanel {
|
||||||
⚙️
|
⚙️
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="param-select-container" title="Температура">
|
||||||
|
<span class="param-icon">Temp: </span>
|
||||||
|
<input type="number" id="temp-input" class="param-input" placeholder="${LLM_DEFAULTS.TEMPERATURE}" min="0" max="2" step="0.1">
|
||||||
|
<button id="temp-reset" class="param-reset-btn" title="Сбросить до дефолтной">↺</button>
|
||||||
|
</div>
|
||||||
|
<div class="param-select-container" title="Максимальное количество токенов в ответе">
|
||||||
|
<span class="param-icon">Tokens: </span>
|
||||||
|
<input type="number" id="max-tokens-input" class="param-input" placeholder="Лимит" min="1" step="100">
|
||||||
|
<button id="max-tokens-reset" class="param-reset-btn" title="Сбросить до лимита из настроек">↺</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-buttons-right">
|
<div class="control-buttons-right">
|
||||||
<button class="stop-button" id="stop-button" style="display: none;">
|
<button class="stop-button" id="stop-button" style="display: none;">
|
||||||
|
|
@ -491,6 +508,20 @@ export class ChatPanel {
|
||||||
|
|
||||||
this.attachedFilesContainer = this.container.querySelector('#attached-files-container') as HTMLDivElement;
|
this.attachedFilesContainer = this.container.querySelector('#attached-files-container') as HTMLDivElement;
|
||||||
|
|
||||||
|
this.tempInput = this.container.querySelector('#temp-input') as HTMLInputElement;
|
||||||
|
this.tempResetBtn = this.container.querySelector('#temp-reset') as HTMLButtonElement;
|
||||||
|
this.maxTokensInput = this.container.querySelector('#max-tokens-input') as HTMLInputElement;
|
||||||
|
this.maxTokensResetBtn = this.container.querySelector('#max-tokens-reset') as HTMLButtonElement;
|
||||||
|
|
||||||
|
const defaultTemp = (this.props.plugin.settings as any).temperatureChat ?? DEFAULT_TEMPERATURE;
|
||||||
|
this.tempInput.value = defaultTemp.toString();
|
||||||
|
|
||||||
|
// Устанавливаем дефолтное значение для токенов
|
||||||
|
const defaultTokens = (this.props.plugin.settings as any).maxTokensChatDefault;
|
||||||
|
if (defaultTokens) {
|
||||||
|
this.maxTokensInput.value = defaultTokens.toString();
|
||||||
|
}
|
||||||
|
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
this.setupReferenceSystem();
|
this.setupReferenceSystem();
|
||||||
this.renderHistory();
|
this.renderHistory();
|
||||||
|
|
@ -560,6 +591,16 @@ export class ChatPanel {
|
||||||
|
|
||||||
this.stopButton.addEventListener('click', () => this.handleStopGeneration());
|
this.stopButton.addEventListener('click', () => this.handleStopGeneration());
|
||||||
|
|
||||||
|
this.maxTokensResetBtn.addEventListener('click', () => {
|
||||||
|
const def = (this.props.plugin.settings as any).maxTokensChatDefault;
|
||||||
|
this.maxTokensInput.value = def ? def.toString() : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tempResetBtn.addEventListener('click', () => {
|
||||||
|
const def = (this.props.plugin.settings as any).temperatureChat ?? DEFAULT_TEMPERATURE;
|
||||||
|
this.tempInput.value = def.toString();
|
||||||
|
});
|
||||||
|
|
||||||
// Close dropdown when clicking outside
|
// Close dropdown when clicking outside
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
if (!this.modelSelectButton.contains(e.target as Node) && !this.modelDropdown.contains(e.target as Node)) {
|
if (!this.modelSelectButton.contains(e.target as Node) && !this.modelDropdown.contains(e.target as Node)) {
|
||||||
|
|
@ -904,9 +945,15 @@ export class ChatPanel {
|
||||||
// Объединяем вложения из ссылок и текущие (из attached-files-container)
|
// Объединяем вложения из ссылок и текущие (из attached-files-container)
|
||||||
const allAttachments = [...attachments, ...this.currentAttachments];
|
const allAttachments = [...attachments, ...this.currentAttachments];
|
||||||
|
|
||||||
// Отправляем оригинальный текст и все вложения
|
// Отправляем оригинальный текст и все вложения, включая лимит токенов
|
||||||
if (this.props.onSendMessage) {
|
if (this.props.onSendMessage) {
|
||||||
this.props.onSendMessage(originalText || '', this.selectedModel, allAttachments);
|
const valTokens = this.maxTokensInput?.value;
|
||||||
|
const parsedMaxTokens = valTokens ? parseInt(valTokens, 10) : undefined;
|
||||||
|
|
||||||
|
const valTemp = this.tempInput?.value;
|
||||||
|
const parsedTemp = valTemp ? parseFloat(valTemp) : undefined;
|
||||||
|
|
||||||
|
this.props.onSendMessage(originalText || '', this.selectedModel, allAttachments, parsedMaxTokens, parsedTemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearInput();
|
this.clearInput();
|
||||||
|
|
@ -938,7 +985,13 @@ export class ChatPanel {
|
||||||
handleSend(): void {
|
handleSend(): void {
|
||||||
const message = this.messageInput.textContent?.trim() || '';
|
const message = this.messageInput.textContent?.trim() || '';
|
||||||
if (message && this.props.onSendMessage) {
|
if (message && this.props.onSendMessage) {
|
||||||
this.props.onSendMessage(message, this.selectedModel, this.currentAttachments);
|
const valTokens = this.maxTokensInput?.value;
|
||||||
|
const parsedMaxTokens = valTokens ? parseInt(valTokens, 10) : undefined;
|
||||||
|
|
||||||
|
const valTemp = this.tempInput?.value;
|
||||||
|
const parsedTemp = valTemp ? parseFloat(valTemp) : undefined;
|
||||||
|
|
||||||
|
this.props.onSendMessage(message, this.selectedModel, this.currentAttachments, parsedMaxTokens, parsedTemp);
|
||||||
this.clearInput();
|
this.clearInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1019,8 +1072,19 @@ export class ChatPanel {
|
||||||
const regenerateIconContainer = regenerateButton.createSpan({ cls: 'flex items-center justify-center touch:w-10 h-8 w-8' });
|
const regenerateIconContainer = regenerateButton.createSpan({ cls: 'flex items-center justify-center touch:w-10 h-8 w-8' });
|
||||||
setIcon(regenerateIconContainer, 'refresh-cw');
|
setIcon(regenerateIconContainer, 'refresh-cw');
|
||||||
regenerateButton.addEventListener('click', async () => {
|
regenerateButton.addEventListener('click', async () => {
|
||||||
// Передаем выбранную модель при регенерации
|
const valTokens = this.maxTokensInput?.value;
|
||||||
this.props.plugin.eventBus.emit('regenerate-message', { graphId, nodeId, model: this.selectedModel });
|
const parsedMaxTokens = valTokens ? parseInt(valTokens, 10) : undefined;
|
||||||
|
|
||||||
|
const valTemp = this.tempInput?.value;
|
||||||
|
const parsedTemp = valTemp ? parseFloat(valTemp) : undefined;
|
||||||
|
|
||||||
|
this.props.plugin.eventBus.emit('regenerate-message', {
|
||||||
|
graphId,
|
||||||
|
nodeId,
|
||||||
|
model: this.selectedModel,
|
||||||
|
maxTokens: parsedMaxTokens,
|
||||||
|
temperature: parsedTemp
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Кнопка "Начать новую ветку отсюда"
|
// Кнопка "Начать новую ветку отсюда"
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import ReactFlow, {
|
||||||
import { App, setIcon } from 'obsidian';
|
import { App, setIcon } from 'obsidian';
|
||||||
import { Dialog } from 'src/utils/Dialog';
|
import { Dialog } from 'src/utils/Dialog';
|
||||||
|
|
||||||
|
import { LLM_DEFAULTS } from 'src/constants';
|
||||||
|
|
||||||
// <-- ИСПРАВЛЕНО: Это ключевой момент. Убедитесь, что esbuild настроен на обработку CSS импортов.
|
// <-- ИСПРАВЛЕНО: Это ключевой момент. Убедитесь, что esbuild настроен на обработку CSS импортов.
|
||||||
// Обычно, при использовании esbuild для React-проектов, вы импортируете CSS прямо в JS/TS файл.
|
// Обычно, при использовании esbuild для React-проектов, вы импортируете CSS прямо в JS/TS файл.
|
||||||
// esbuild должен быть настроен для копирования или встраивания этого CSS.
|
// esbuild должен быть настроен для копирования или встраивания этого CSS.
|
||||||
|
|
@ -98,10 +100,18 @@ const BaseNodeWrapper: React.FC<BaseNodeWrapperProps> = ({ data, isConnectable,
|
||||||
// Используем EventBus вместо workspace.trigger
|
// Используем EventBus вместо workspace.trigger
|
||||||
const eventBus = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.eventBus;
|
const eventBus = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.eventBus;
|
||||||
if (eventBus) {
|
if (eventBus) {
|
||||||
|
let model = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.settings?.defaultModel || LLM_DEFAULTS.MODEL;
|
||||||
|
// TODO: Возможно, брать в данном случае данные из настроек плагина не вполне корректно, но вариант брать его из чат панели
|
||||||
|
// или перелопачивать код и хранить в узлах и бд настройки, использованные при генерации, как будто, ещё хуже
|
||||||
|
let maxTokens = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.settings?.maxTokensChatDefault || '';
|
||||||
|
let temperature = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.settings?.temperatureChat || LLM_DEFAULTS.TEMPERATURE;
|
||||||
|
|
||||||
eventBus.emit('regenerate-message', {
|
eventBus.emit('regenerate-message', {
|
||||||
graphId: data.graphId,
|
graphId: data.graphId,
|
||||||
nodeId: data.nodeId,
|
nodeId: data.nodeId,
|
||||||
model: (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.settings?.defaultModel || 'gemini-2.5-flash' // НОВОЕ: Добавляем модель
|
model: model, // НОВОЕ: Добавляем модель
|
||||||
|
maxTokens: maxTokens,
|
||||||
|
temperature: temperature
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
42
src/constants.ts
Normal file
42
src/constants.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
export const LLM_DEFAULTS = {
|
||||||
|
TEMPERATURE: 1.0,
|
||||||
|
MODEL: 'gemini-2.5-flash-r',
|
||||||
|
// Сюда можно добавить и другие константы
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Температурные режимы LLM моделей
|
||||||
|
Google (Gemini 2.0, 2.5, 3.x)
|
||||||
|
|
||||||
|
Минимум: 0.0
|
||||||
|
По умолчанию: 1.0
|
||||||
|
Максимум: 2.0
|
||||||
|
Примечание: Для новых моделей 3.0+ Google рекомендует строго 1.0 для корректной логики.
|
||||||
|
|
||||||
|
OpenAI (GPT-4o, GPT-4 Turbo)
|
||||||
|
|
||||||
|
Минимум: 0.0
|
||||||
|
По умолчанию: 1.0
|
||||||
|
Максимум: 2.0
|
||||||
|
Примечание: В API стандарт 1.0, хотя в веб-версии ChatGPT часто используется 0.7.
|
||||||
|
|
||||||
|
Mistral AI (Mistral Small, Large)
|
||||||
|
|
||||||
|
Минимум: 0.0
|
||||||
|
По умолчанию: 0.7
|
||||||
|
Максимум: 1.5
|
||||||
|
Примечание: Для задач на логику и код в моделях Small часто выставляют 0.15–0.3.
|
||||||
|
|
||||||
|
Anthropic (Claude 3.5, 4.5)
|
||||||
|
|
||||||
|
Минимум: 0.0
|
||||||
|
По умолчанию: 1.0
|
||||||
|
Максимум: 1.0
|
||||||
|
Примечание: У Claude диапазон ограничен единицей, значения выше 1.0 API не принимает.
|
||||||
|
|
||||||
|
Краткая шпаргалка по значениям:
|
||||||
|
|
||||||
|
0.0 - 0.2: Код, JSON, математика, строгая логика.
|
||||||
|
0.7: Баланс для обычного чата и суммаризации.
|
||||||
|
1.0: Творчество, идеи, копирайтинг (стандарт для Gemini и Claude).
|
||||||
|
*/
|
||||||
|
|
@ -1450,3 +1450,58 @@
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------- Params UI Styles (Temp & Tokens) --------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.param-select-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
/*background-color: var(--button-secondary-background);
|
||||||
|
border: 1px solid var(--border-color-dark);*/
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-icon {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
/*font-weight: bold;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-input {
|
||||||
|
width: 48px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-normal);
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-input::-webkit-inner-spin-button,
|
||||||
|
.param-input::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-reset-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-muted) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 1px 4px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: auto !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-reset-btn:hover {
|
||||||
|
color: var(--text-normal) !important;
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,8 @@ import { TemplateEngine } from '../references/TemplateEngine';
|
||||||
|
|
||||||
export const CHAT_VIEW_TYPE = 'llm-agent-chat-view';
|
export const CHAT_VIEW_TYPE = 'llm-agent-chat-view';
|
||||||
|
|
||||||
|
import { LLM_DEFAULTS } from 'src/constants';
|
||||||
|
|
||||||
// ----------------------------------------------- Chat View ---------------------------------------------------------------
|
// ----------------------------------------------- Chat View ---------------------------------------------------------------
|
||||||
|
|
||||||
export class ChatView extends ItemView {
|
export class ChatView extends ItemView {
|
||||||
|
|
@ -60,7 +62,7 @@ export class ChatView extends ItemView {
|
||||||
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),
|
||||||
selectedModel: this.plugin.settings.defaultModel || 'gemini-2.5-flash',
|
selectedModel: this.plugin.settings.defaultModel || LLM_DEFAULTS.MODEL,
|
||||||
plugin: this.plugin,
|
plugin: this.plugin,
|
||||||
currentGraphCustomSystemPrompt: this.currentGraphCustomSystemPrompt
|
currentGraphCustomSystemPrompt: this.currentGraphCustomSystemPrompt
|
||||||
});
|
});
|
||||||
|
|
@ -156,7 +158,7 @@ export class ChatView extends ItemView {
|
||||||
|
|
||||||
// ----------------------------------------------- Event Handlers ---------------------------------------------------------------
|
// ----------------------------------------------- Event Handlers ---------------------------------------------------------------
|
||||||
|
|
||||||
async handleSendMessage(message: string, selectedModel?: string, attachments?: Attachment[]): Promise<void> {
|
async handleSendMessage(message: string, selectedModel?: string, attachments?: Attachment[], customMaxTokens?: number, customTemperature?: number): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!this.selectedGraphId) {
|
if (!this.selectedGraphId) {
|
||||||
new Notice('Граф не выбран или не инициализирован. Пожалуйста, начните новый чат или выберите существующий.', 3000);
|
new Notice('Граф не выбран или не инициализирован. Пожалуйста, начните новый чат или выберите существующий.', 3000);
|
||||||
|
|
@ -199,7 +201,7 @@ export class ChatView extends ItemView {
|
||||||
const systemPromptToUse =
|
const systemPromptToUse =
|
||||||
this.currentGraphCustomSystemPrompt !== null ? this.currentGraphCustomSystemPrompt : this.plugin.settings.systemPrompt;
|
this.currentGraphCustomSystemPrompt !== null ? this.currentGraphCustomSystemPrompt : this.plugin.settings.systemPrompt;
|
||||||
|
|
||||||
await this.streamLLMResponse(this.selectedGraphId, userNodeId, selectedModel, null, systemPromptToUse, cacheFolder);
|
await this.streamLLMResponse(this.selectedGraphId, userNodeId, selectedModel, null, systemPromptToUse, cacheFolder, customMaxTokens, customTemperature);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Graph ID не определён после отправки сообщения');
|
throw new Error('Graph ID не определён после отправки сообщения');
|
||||||
}
|
}
|
||||||
|
|
@ -274,21 +276,29 @@ export class ChatView extends ItemView {
|
||||||
selectedModel?: string,
|
selectedModel?: string,
|
||||||
existingAssistantNodeId: string | null = null,
|
existingAssistantNodeId: string | null = null,
|
||||||
systemPrompt: string | null = null,
|
systemPrompt: string | null = null,
|
||||||
cacheFolder?: string
|
cacheFolder?: string,
|
||||||
|
customMaxTokens?: number,
|
||||||
|
customTemperature?: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Разворачиваем системный промпт (например, если там передано "#my_system_instructions")
|
// Разворачиваем системный промпт
|
||||||
let finalSystemPrompt = systemPrompt;
|
let finalSystemPrompt = systemPrompt;
|
||||||
if (systemPrompt) {
|
if (systemPrompt) {
|
||||||
const templateEngine = new TemplateEngine(this.plugin);
|
const templateEngine = new TemplateEngine(this.plugin);
|
||||||
finalSystemPrompt = await templateEngine.processRawSettingValue(systemPrompt);
|
finalSystemPrompt = await templateEngine.processRawSettingValue(systemPrompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const temperature = customTemperature !== undefined
|
||||||
|
? customTemperature
|
||||||
|
: ((this.plugin.settings as any).temperatureChat !== undefined ? (this.plugin.settings as any).temperatureChat : LLM_DEFAULTS.TEMPERATURE);
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
graph_id: graphId,
|
graph_id: graphId,
|
||||||
user_node_id: userNodeId,
|
user_node_id: userNodeId,
|
||||||
system_prompt: finalSystemPrompt,
|
system_prompt: finalSystemPrompt,
|
||||||
model: selectedModel || this.plugin.settings.defaultModel,
|
model: selectedModel || this.plugin.settings.defaultModel,
|
||||||
cache_folder: cacheFolder
|
cache_folder: cacheFolder,
|
||||||
|
temperature: temperature,
|
||||||
|
max_tokens: customMaxTokens
|
||||||
};
|
};
|
||||||
|
|
||||||
if (existingAssistantNodeId) {
|
if (existingAssistantNodeId) {
|
||||||
|
|
@ -461,13 +471,15 @@ export class ChatView extends ItemView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleRegenerateMessage(data: { graphId: string; nodeId: string; model: string }): Promise<void> {
|
async handleRegenerateMessage(data: { graphId: string; nodeId: string; model: string, maxTokens?: number, temperature?: number }): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Определяем системный промпт для отправки: кастомный для графа, если есть, иначе глобальный
|
|
||||||
const systemPromptToUse =
|
const systemPromptToUse =
|
||||||
this.currentGraphCustomSystemPrompt !== null ? this.currentGraphCustomSystemPrompt : this.plugin.settings.systemPrompt;
|
this.currentGraphCustomSystemPrompt !== null ? this.currentGraphCustomSystemPrompt : this.plugin.settings.systemPrompt;
|
||||||
|
|
||||||
const cacheFolder = this.getCacheFolderPath();
|
const cacheFolder = this.getCacheFolderPath();
|
||||||
|
const temperature = data.temperature !== undefined
|
||||||
|
? data.temperature
|
||||||
|
: ((this.plugin.settings as any).temperatureChat !== undefined ? (this.plugin.settings as any).temperatureChat : LLM_DEFAULTS.TEMPERATURE);
|
||||||
|
|
||||||
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat/regenerate`, {
|
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat/regenerate`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -477,7 +489,9 @@ export class ChatView extends ItemView {
|
||||||
node_id: data.nodeId,
|
node_id: data.nodeId,
|
||||||
model: data.model,
|
model: data.model,
|
||||||
system_prompt: systemPromptToUse,
|
system_prompt: systemPromptToUse,
|
||||||
cache_folder: cacheFolder
|
cache_folder: cacheFolder,
|
||||||
|
temperature: temperature,
|
||||||
|
max_tokens: data.maxTokens
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -490,9 +504,11 @@ export class ChatView extends ItemView {
|
||||||
const userNodeId = result.user_node_id;
|
const userNodeId = result.user_node_id;
|
||||||
const modelToUse = result.model;
|
const modelToUse = result.model;
|
||||||
const systemPromptFromBackend = result.system_prompt;
|
const systemPromptFromBackend = result.system_prompt;
|
||||||
|
const maxTokensFromBackend = result.max_tokens;
|
||||||
|
const temperatureFromBackend = result.temperature;
|
||||||
|
|
||||||
if (data.graphId) {
|
if (data.graphId) {
|
||||||
await this.streamLLMResponse(data.graphId, userNodeId, modelToUse, existingAssistantNodeId, systemPromptFromBackend);
|
await this.streamLLMResponse(data.graphId, userNodeId, modelToUse, existingAssistantNodeId, systemPromptFromBackend, cacheFolder, maxTokensFromBackend, temperatureFromBackend);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Ошибка регенерации:', error);
|
console.error('Ошибка регенерации:', error);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user