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 { 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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
⚙️
|
||||
</span>
|
||||
</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 class="control-buttons-right">
|
||||
<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.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.setupReferenceSystem();
|
||||
this.renderHistory();
|
||||
|
|
@ -560,6 +591,16 @@ export class ChatPanel {
|
|||
|
||||
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
|
||||
document.addEventListener('click', (e) => {
|
||||
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)
|
||||
const allAttachments = [...attachments, ...this.currentAttachments];
|
||||
|
||||
// Отправляем оригинальный текст и все вложения
|
||||
// Отправляем оригинальный текст и все вложения, включая лимит токенов
|
||||
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();
|
||||
|
|
@ -938,7 +985,13 @@ export class ChatPanel {
|
|||
handleSend(): void {
|
||||
const message = this.messageInput.textContent?.trim() || '';
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1019,8 +1072,19 @@ export class ChatPanel {
|
|||
const regenerateIconContainer = regenerateButton.createSpan({ cls: 'flex items-center justify-center touch:w-10 h-8 w-8' });
|
||||
setIcon(regenerateIconContainer, 'refresh-cw');
|
||||
regenerateButton.addEventListener('click', async () => {
|
||||
// Передаем выбранную модель при регенерации
|
||||
this.props.plugin.eventBus.emit('regenerate-message', { graphId, nodeId, model: this.selectedModel });
|
||||
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.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 { Dialog } from 'src/utils/Dialog';
|
||||
|
||||
import { LLM_DEFAULTS } from 'src/constants';
|
||||
|
||||
// <-- ИСПРАВЛЕНО: Это ключевой момент. Убедитесь, что esbuild настроен на обработку CSS импортов.
|
||||
// Обычно, при использовании esbuild для React-проектов, вы импортируете CSS прямо в JS/TS файл.
|
||||
// esbuild должен быть настроен для копирования или встраивания этого CSS.
|
||||
|
|
@ -98,10 +100,18 @@ const BaseNodeWrapper: React.FC<BaseNodeWrapperProps> = ({ data, isConnectable,
|
|||
// Используем EventBus вместо workspace.trigger
|
||||
const eventBus = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.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', {
|
||||
graphId: data.graphId,
|
||||
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).
|
||||
*/
|
||||
|
|
@ -1449,4 +1449,59 @@
|
|||
.attach-file-button svg {
|
||||
width: 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';
|
||||
|
||||
import { LLM_DEFAULTS } from 'src/constants';
|
||||
|
||||
// ----------------------------------------------- Chat View ---------------------------------------------------------------
|
||||
|
||||
export class ChatView extends ItemView {
|
||||
|
|
@ -60,7 +62,7 @@ export class ChatView extends ItemView {
|
|||
this.chatPanel = new ChatPanel(this.chatContainer, {
|
||||
chatHistory: this.chatHistory,
|
||||
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,
|
||||
currentGraphCustomSystemPrompt: this.currentGraphCustomSystemPrompt
|
||||
});
|
||||
|
|
@ -156,7 +158,7 @@ export class ChatView extends ItemView {
|
|||
|
||||
// ----------------------------------------------- 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 {
|
||||
if (!this.selectedGraphId) {
|
||||
new Notice('Граф не выбран или не инициализирован. Пожалуйста, начните новый чат или выберите существующий.', 3000);
|
||||
|
|
@ -199,7 +201,7 @@ export class ChatView extends ItemView {
|
|||
const systemPromptToUse =
|
||||
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 {
|
||||
throw new Error('Graph ID не определён после отправки сообщения');
|
||||
}
|
||||
|
|
@ -274,21 +276,29 @@ export class ChatView extends ItemView {
|
|||
selectedModel?: string,
|
||||
existingAssistantNodeId: string | null = null,
|
||||
systemPrompt: string | null = null,
|
||||
cacheFolder?: string
|
||||
cacheFolder?: string,
|
||||
customMaxTokens?: number,
|
||||
customTemperature?: number
|
||||
): Promise<void> {
|
||||
// Разворачиваем системный промпт (например, если там передано "#my_system_instructions")
|
||||
// Разворачиваем системный промпт
|
||||
let finalSystemPrompt = systemPrompt;
|
||||
if (systemPrompt) {
|
||||
const templateEngine = new TemplateEngine(this.plugin);
|
||||
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 = {
|
||||
graph_id: graphId,
|
||||
user_node_id: userNodeId,
|
||||
system_prompt: finalSystemPrompt,
|
||||
model: selectedModel || this.plugin.settings.defaultModel,
|
||||
cache_folder: cacheFolder
|
||||
cache_folder: cacheFolder,
|
||||
temperature: temperature,
|
||||
max_tokens: customMaxTokens
|
||||
};
|
||||
|
||||
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 {
|
||||
// Определяем системный промпт для отправки: кастомный для графа, если есть, иначе глобальный
|
||||
const systemPromptToUse =
|
||||
this.currentGraphCustomSystemPrompt !== null ? this.currentGraphCustomSystemPrompt : this.plugin.settings.systemPrompt;
|
||||
|
||||
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`, {
|
||||
method: 'POST',
|
||||
|
|
@ -477,7 +489,9 @@ export class ChatView extends ItemView {
|
|||
node_id: data.nodeId,
|
||||
model: data.model,
|
||||
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 modelToUse = result.model;
|
||||
const systemPromptFromBackend = result.system_prompt;
|
||||
const maxTokensFromBackend = result.max_tokens;
|
||||
const temperatureFromBackend = result.temperature;
|
||||
|
||||
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) {
|
||||
console.error('Ошибка регенерации:', error);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user