Fix problem with expanding of prompts and articles (it is returned back), remove not used code
This commit is contained in:
parent
f66dd0920b
commit
3d3af78563
2
main.ts
2
main.ts
|
|
@ -72,7 +72,7 @@ export default class LLMAgentPlugin extends Plugin {
|
||||||
// Клонируем настройки, чтобы не испортить исходные пути в конфиге плагина
|
// Клонируем настройки, чтобы не испортить исходные пути в конфиге плагина
|
||||||
const settingsToSync = JSON.parse(JSON.stringify(this.settings));
|
const settingsToSync = JSON.parse(JSON.stringify(this.settings));
|
||||||
|
|
||||||
// Собираем промпты (раскрываем все цепочки ссылок)
|
// Собираем промпты (раскрываем все цепочки ссылок) для работы с аудиологами на бекенде
|
||||||
settingsToSync.systemPrompt = await engine.processRawSettingValue(this.settings.systemPrompt);
|
settingsToSync.systemPrompt = await engine.processRawSettingValue(this.settings.systemPrompt);
|
||||||
settingsToSync.promptRegular = await engine.processRawSettingValue(this.settings.promptRegular);
|
settingsToSync.promptRegular = await engine.processRawSettingValue(this.settings.promptRegular);
|
||||||
settingsToSync.promptCommands = await engine.processRawSettingValue(this.settings.promptCommands);
|
settingsToSync.promptCommands = await engine.processRawSettingValue(this.settings.promptCommands);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { TFile, normalizePath, FileSystemAdapter } from 'obsidian';
|
||||||
import LLMAgentPlugin from 'main';
|
import LLMAgentPlugin from 'main';
|
||||||
import { v4 as uuidv4 } from 'uuid'; // Нужно установить: npm install uuid @types/uuid
|
import { v4 as uuidv4 } from 'uuid'; // Нужно установить: npm install uuid @types/uuid
|
||||||
import { getMimeType } from 'src/utils/fileUtils';
|
import { getMimeType } from 'src/utils/fileUtils';
|
||||||
|
import { TemplateEngine } from './TemplateEngine';
|
||||||
|
|
||||||
export class CacheManager {
|
export class CacheManager {
|
||||||
private plugin: LLMAgentPlugin;
|
private plugin: LLMAgentPlugin;
|
||||||
|
|
@ -67,11 +68,9 @@ export class CacheManager {
|
||||||
await this.ensureCacheFolderExists();
|
await this.ensureCacheFolderExists();
|
||||||
|
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
const extension = file.extension;
|
const extension = file.extension.toLowerCase();
|
||||||
const cachedFileName = `${file.basename}-${uuid}.${extension}`;
|
const cachedFileName = `${file.basename}-${uuid}.${extension}`;
|
||||||
|
|
||||||
const content = await this.plugin.app.vault.readBinary(file);
|
|
||||||
|
|
||||||
const cacheFolderPath = this.getCacheFolderPath();
|
const cacheFolderPath = this.getCacheFolderPath();
|
||||||
const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`;
|
const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`;
|
||||||
|
|
||||||
|
|
@ -80,7 +79,25 @@ export class CacheManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fs = window.require('fs').promises;
|
const fs = window.require('fs').promises;
|
||||||
await fs.writeFile(cachedFilePath, Buffer.from(content));
|
|
||||||
|
// Список расширений, внутри которых мы ищем и разворачиваем макросы
|
||||||
|
const textExtensions = ['md', 'txt']; // , 'canvas'
|
||||||
|
|
||||||
|
if (textExtensions.includes(extension)) {
|
||||||
|
// Читаем как текст
|
||||||
|
let content = await this.plugin.app.vault.read(file);
|
||||||
|
|
||||||
|
// Инициализируем движок и полностью разворачиваем все вложенные @{} и #{}
|
||||||
|
const templateEngine = new TemplateEngine(this.plugin);
|
||||||
|
const expandedContent = await templateEngine.expandPromptTemplate(content);
|
||||||
|
|
||||||
|
// Сохраняем в кеш РАЗВЕРНУТЫЙ текст (снапшот)
|
||||||
|
await fs.writeFile(cachedFilePath, expandedContent, 'utf8');
|
||||||
|
} else {
|
||||||
|
// Если это картинка, PDF или другой бинарник, просто копируем байты
|
||||||
|
const content = await this.plugin.app.vault.readBinary(file);
|
||||||
|
await fs.writeFile(cachedFilePath, Buffer.from(content));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uuid: uuid,
|
uuid: uuid,
|
||||||
|
|
@ -122,6 +139,7 @@ export class CacheManager {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Кажется, не используется
|
||||||
/**
|
/**
|
||||||
* Восстанавливает кеш из исходного файла
|
* Восстанавливает кеш из исходного файла
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -26,58 +26,6 @@ export class ReferenceExpander {
|
||||||
|
|
||||||
// ----------------------------------------------- Public Methods ---------------------------------------------------------------
|
// ----------------------------------------------- Public Methods ---------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Заменяет все ссылки на содержимое файлов (перед отправкой на сервер)
|
|
||||||
* @param text - текст с ссылками
|
|
||||||
* @param references - массив ссылок для обработки
|
|
||||||
* @returns текст с развернутыми ссылками
|
|
||||||
*/
|
|
||||||
/*async expandReferences(text: string, references: FileReference[]): Promise<string> {
|
|
||||||
if (references.length === 0) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
let expandedText = text;
|
|
||||||
|
|
||||||
// Обрабатываем ссылки от конца к началу, чтобы не сбивать индексы
|
|
||||||
const sortedReferences = [...references].sort((a, b) => {
|
|
||||||
const aMatches = ReferenceParser.parseReferences(text).filter((m) => m.name === a.name);
|
|
||||||
const bMatches = ReferenceParser.parseReferences(text).filter((m) => m.name === b.name);
|
|
||||||
const aIndex = aMatches.length > 0 ? aMatches[aMatches.length - 1].startIndex : 0;
|
|
||||||
const bIndex = bMatches.length > 0 ? bMatches[bMatches.length - 1].startIndex : 0;
|
|
||||||
return bIndex - aIndex;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const reference of sortedReferences) {
|
|
||||||
expandedText = await this.expandSingleReference(expandedText, reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandedText;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Обрабатывает историю сообщений, раскрывая постоянные ссылки (@@/##)
|
|
||||||
* @param messages - массив сообщений
|
|
||||||
* @returns обработанные сообщения
|
|
||||||
*/
|
|
||||||
/*async expandMessageHistory(messages: any[]): Promise<any[]> {
|
|
||||||
const expandedMessages = [];
|
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (message.role === 'user') {
|
|
||||||
const expandedContent = await this.expandPermanentReferences(message.content);
|
|
||||||
expandedMessages.push({
|
|
||||||
...message,
|
|
||||||
content: expandedContent
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
expandedMessages.push(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandedMessages;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Извлекает вложения из текста и кеширует файлы.
|
* Извлекает вложения из текста и кеширует файлы.
|
||||||
* Обрабатывает как физические файлы, так и виртуальные (current-log).
|
* Обрабатывает как физические файлы, так и виртуальные (current-log).
|
||||||
|
|
@ -195,221 +143,4 @@ export class ReferenceExpander {
|
||||||
attachments: attachments
|
attachments: attachments
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Подготавливает сообщения для LLM с учетом вложений.
|
|
||||||
* Раскрывает только постоянные (@@/##) ссылки и ссылки в последнем сообщении.
|
|
||||||
*/
|
|
||||||
/*async prepareMessagesForLLM(messages: ChatHistoryItem[], isLastMessage: boolean = false): Promise<any[]> {
|
|
||||||
const preparedMessages = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < messages.length; i++) {
|
|
||||||
const msg = messages[i];
|
|
||||||
const isLast = i === messages.length - 1;
|
|
||||||
|
|
||||||
if (msg.role === 'user' && msg.attachments && msg.attachments.length > 0) {
|
|
||||||
// Определяем, какие вложения нужно раскрыть
|
|
||||||
const attachmentsToExpand = msg.attachments.filter((att) => att.permanent || (isLast && isLastMessage));
|
|
||||||
|
|
||||||
if (attachmentsToExpand.length > 0) {
|
|
||||||
// Формируем сообщение с раскрытыми вложениями
|
|
||||||
let expandedContent = msg.content;
|
|
||||||
|
|
||||||
for (const attachment of attachmentsToExpand) {
|
|
||||||
const formattedContent = this.formatContentForInsertion(attachment.content || '', attachment);
|
|
||||||
expandedContent += '\n' + formattedContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
preparedMessages.push({
|
|
||||||
role: msg.role,
|
|
||||||
content: expandedContent
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Вложения есть, но раскрывать не нужно
|
|
||||||
preparedMessages.push({
|
|
||||||
role: msg.role,
|
|
||||||
content: msg.content
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Сообщение без вложений
|
|
||||||
preparedMessages.push({
|
|
||||||
role: msg.role,
|
|
||||||
content: msg.content
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return preparedMessages;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// TODO: При реализации снапшотов - сохранять содержимое файлов
|
|
||||||
// async expandReferencesWithSnapshots(text: string, references: FileReference[], messageId: string): Promise<{expandedText: string, snapshots: Record<string, string>}> {
|
|
||||||
// const snapshots: Record<string, string> = {};
|
|
||||||
// let expandedText = text;
|
|
||||||
//
|
|
||||||
// for (const reference of references) {
|
|
||||||
// const content = await this.loadFileContent(reference);
|
|
||||||
// if (content !== null) {
|
|
||||||
// snapshots[reference.path] = content;
|
|
||||||
// expandedText = await this.expandSingleReference(expandedText, reference, content);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return { expandedText, snapshots };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ----------------------------------------------- Private Methods ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Раскрывает одну ссылку в тексте
|
|
||||||
* @param text - текст для обработки
|
|
||||||
* @param reference - ссылка для раскрытия
|
|
||||||
* @param preloadedContent - предзагруженное содержимое (для снапшотов)
|
|
||||||
* @returns текст с раскрытой ссылкой
|
|
||||||
*/
|
|
||||||
/*private async expandSingleReference(text: string, reference: FileReference, preloadedContent?: string): Promise<string> {
|
|
||||||
const content = preloadedContent || (await this.loadFileContent(reference));
|
|
||||||
|
|
||||||
if (content === null) {
|
|
||||||
console.warn(`Не удалось загрузить содержимое для ссылки: ${reference.name}`);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Определяем паттерн для замены
|
|
||||||
const prefix = reference.permanent ? (reference.type === 'file' ? '@@' : '##') : reference.type === 'file' ? '@' : '#';
|
|
||||||
|
|
||||||
// Создаем регулярное выражение для поиска ссылки
|
|
||||||
const escapedName = this.escapeRegExp(reference.name);
|
|
||||||
const pattern = new RegExp(`${this.escapeRegExp(prefix)}(?:"${escapedName}"|${escapedName})(?=\\s|$|[^\\w-])`, 'g');
|
|
||||||
|
|
||||||
// Форматируем содержимое для вставки
|
|
||||||
const formattedContent = this.formatContentForInsertion(content, reference);
|
|
||||||
|
|
||||||
return text.replace(pattern, formattedContent);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Раскрывает только постоянные ссылки (@@/##) в тексте
|
|
||||||
* @param text - текст для обработки
|
|
||||||
* @returns текст с раскрытыми постоянными ссылками
|
|
||||||
*/
|
|
||||||
/*private async expandPermanentReferences(text: string): Promise<string> {
|
|
||||||
const matches = ReferenceParser.parseReferences(text);
|
|
||||||
const permanentMatches = matches.filter((match) => match.prefix.length === 2); // @@ или ##
|
|
||||||
|
|
||||||
if (permanentMatches.length === 0) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
let expandedText = text;
|
|
||||||
|
|
||||||
// Обрабатываем ссылки от конца к началу
|
|
||||||
const sortedMatches = [...permanentMatches].sort((a, b) => b.startIndex - a.startIndex);
|
|
||||||
|
|
||||||
for (const match of sortedMatches) {
|
|
||||||
const reference: FileReference = {
|
|
||||||
type: match.type,
|
|
||||||
name: match.name,
|
|
||||||
path: await this.resolveFilePath(match.name, match.type),
|
|
||||||
permanent: true
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = await this.loadFileContent(reference);
|
|
||||||
if (content !== null) {
|
|
||||||
const formattedContent = this.formatContentForInsertion(content, reference);
|
|
||||||
expandedText = expandedText.substring(0, match.startIndex) + formattedContent + expandedText.substring(match.endIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandedText;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Загружает содержимое файла
|
|
||||||
* @param reference - ссылка на файл
|
|
||||||
* @returns содержимое файла или null если не найден
|
|
||||||
*/
|
|
||||||
/*private async loadFileContent(reference: FileReference): Promise<string | null> {
|
|
||||||
try {
|
|
||||||
const file = this.findFile(reference.path, reference.type);
|
|
||||||
if (!file) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = await this.plugin.app.vault.read(file);
|
|
||||||
|
|
||||||
// Для промптов обрабатываем шаблоны
|
|
||||||
if (reference.type === 'prompt') {
|
|
||||||
return await this.templateEngine.expandPromptTemplate(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Ошибка загрузки файла ${reference.path}:`, error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Находит файл по пути
|
|
||||||
* @param path - путь к файлу
|
|
||||||
* @param type - тип файла
|
|
||||||
* @returns файл или null
|
|
||||||
*/
|
|
||||||
/*private findFile(path: string, type: 'file' | 'prompt'): TFile | null {
|
|
||||||
let file = this.plugin.app.vault.getAbstractFileByPath(path);
|
|
||||||
|
|
||||||
if (!file && type === 'prompt') {
|
|
||||||
// Попробуем найти в папке промптов
|
|
||||||
const promptsFolder = this.plugin.settings.promptsFolder;
|
|
||||||
if (promptsFolder) {
|
|
||||||
const fullPath = `${promptsFolder}/${path}`;
|
|
||||||
file = this.plugin.app.vault.getAbstractFileByPath(fullPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return file instanceof TFile ? file : null;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Разрешает полный путь к файлу по имени
|
|
||||||
* @param name - имя файла
|
|
||||||
* @param type - тип файла
|
|
||||||
* @returns полный путь к файлу
|
|
||||||
*/
|
|
||||||
/*private async resolveFilePath(name: string, type: 'file' | 'prompt'): Promise<string> {
|
|
||||||
if (type === 'prompt') {
|
|
||||||
const promptsFolder = this.plugin.settings.promptsFolder;
|
|
||||||
return promptsFolder ? `${promptsFolder}/${name}.md` : `${name}.md`;
|
|
||||||
} else {
|
|
||||||
// Ищем файл в хранилище
|
|
||||||
const files = this.plugin.app.vault.getMarkdownFiles();
|
|
||||||
const matchingFile = files.find((f) => f.basename === name);
|
|
||||||
return matchingFile ? matchingFile.path : `${name}.md`;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Форматирует содержимое для вставки в сообщение
|
|
||||||
* @param content - содержимое файла
|
|
||||||
* @param reference - информация о ссылке
|
|
||||||
* @returns отформатированное содержимое
|
|
||||||
*/
|
|
||||||
/*private formatContentForInsertion(content: string, reference: FileReference): string {
|
|
||||||
// Добавляем заголовок с информацией о файле
|
|
||||||
const header = `\n\n--- Содержимое ${reference.type === 'prompt' ? 'промпта' : 'файла'}: ${reference.name} ---\n`;
|
|
||||||
const footer = `\n--- Конец ${reference.type === 'prompt' ? 'промпта' : 'файла'}: ${reference.name} ---\n\n`;
|
|
||||||
|
|
||||||
return header + content.trim() + footer;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Экранирует специальные символы для регулярного выражения
|
|
||||||
* @param string - строка для экранирования
|
|
||||||
* @returns экранированная строка
|
|
||||||
*/
|
|
||||||
/*private escapeRegExp(string: string): string {
|
|
||||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { ItemView, Notice, WorkspaceLeaf } from 'obsidian';
|
||||||
import { ChatPanel } from '../components/ChatPanel';
|
import { ChatPanel } from '../components/ChatPanel';
|
||||||
import LLMAgentPlugin from 'main';
|
import LLMAgentPlugin from 'main';
|
||||||
import { Attachment } from 'src/components/models/ChatHistoryItem';
|
import { Attachment } from 'src/components/models/ChatHistoryItem';
|
||||||
|
import { TemplateEngine } from '../references/TemplateEngine';
|
||||||
|
|
||||||
export const CHAT_VIEW_TYPE = 'llm-agent-chat-view';
|
export const CHAT_VIEW_TYPE = 'llm-agent-chat-view';
|
||||||
|
|
||||||
|
|
@ -275,10 +276,17 @@ export class ChatView extends ItemView {
|
||||||
systemPrompt: string | null = null,
|
systemPrompt: string | null = null,
|
||||||
cacheFolder?: string
|
cacheFolder?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// Разворачиваем системный промпт (например, если там передано "#my_system_instructions")
|
||||||
|
let finalSystemPrompt = systemPrompt;
|
||||||
|
if (systemPrompt) {
|
||||||
|
const templateEngine = new TemplateEngine(this.plugin);
|
||||||
|
finalSystemPrompt = await templateEngine.processRawSettingValue(systemPrompt);
|
||||||
|
}
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
graph_id: graphId,
|
graph_id: graphId,
|
||||||
user_node_id: userNodeId,
|
user_node_id: userNodeId,
|
||||||
system_prompt: systemPrompt,
|
system_prompt: finalSystemPrompt,
|
||||||
model: selectedModel || this.plugin.settings.defaultModel,
|
model: selectedModel || this.plugin.settings.defaultModel,
|
||||||
cache_folder: cacheFolder
|
cache_folder: cacheFolder
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user