Fix problem with expanding of prompts and articles (it is returned back), remove not used code

This commit is contained in:
dimitrievgs 2026-05-10 17:49:52 +03:00
parent f66dd0920b
commit 3d3af78563
4 changed files with 32 additions and 275 deletions

View File

@ -72,7 +72,7 @@ export default class LLMAgentPlugin extends Plugin {
// Клонируем настройки, чтобы не испортить исходные пути в конфиге плагина
const settingsToSync = JSON.parse(JSON.stringify(this.settings));
// Собираем промпты (раскрываем все цепочки ссылок)
// Собираем промпты (раскрываем все цепочки ссылок) для работы с аудиологами на бекенде
settingsToSync.systemPrompt = await engine.processRawSettingValue(this.settings.systemPrompt);
settingsToSync.promptRegular = await engine.processRawSettingValue(this.settings.promptRegular);
settingsToSync.promptCommands = await engine.processRawSettingValue(this.settings.promptCommands);

View File

@ -7,6 +7,7 @@ import { TFile, normalizePath, FileSystemAdapter } from 'obsidian';
import LLMAgentPlugin from 'main';
import { v4 as uuidv4 } from 'uuid'; // Нужно установить: npm install uuid @types/uuid
import { getMimeType } from 'src/utils/fileUtils';
import { TemplateEngine } from './TemplateEngine';
export class CacheManager {
private plugin: LLMAgentPlugin;
@ -67,11 +68,9 @@ export class CacheManager {
await this.ensureCacheFolderExists();
const uuid = uuidv4();
const extension = file.extension;
const extension = file.extension.toLowerCase();
const cachedFileName = `${file.basename}-${uuid}.${extension}`;
const content = await this.plugin.app.vault.readBinary(file);
const cacheFolderPath = this.getCacheFolderPath();
const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`;
@ -80,7 +79,25 @@ export class CacheManager {
}
const fs = window.require('fs').promises;
// Список расширений, внутри которых мы ищем и разворачиваем макросы
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 {
uuid: uuid,
@ -122,6 +139,7 @@ export class CacheManager {
};
}
// TODO: Кажется, не используется
/**
* Восстанавливает кеш из исходного файла
*/

View File

@ -26,58 +26,6 @@ export class ReferenceExpander {
// ----------------------------------------------- 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).
@ -195,221 +143,4 @@ export class ReferenceExpander {
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, '\\$&');
}*/
}

View File

@ -7,6 +7,7 @@ import { ItemView, Notice, WorkspaceLeaf } from 'obsidian';
import { ChatPanel } from '../components/ChatPanel';
import LLMAgentPlugin from 'main';
import { Attachment } from 'src/components/models/ChatHistoryItem';
import { TemplateEngine } from '../references/TemplateEngine';
export const CHAT_VIEW_TYPE = 'llm-agent-chat-view';
@ -275,10 +276,17 @@ export class ChatView extends ItemView {
systemPrompt: string | null = null,
cacheFolder?: string
): Promise<void> {
// Разворачиваем системный промпт (например, если там передано "#my_system_instructions")
let finalSystemPrompt = systemPrompt;
if (systemPrompt) {
const templateEngine = new TemplateEngine(this.plugin);
finalSystemPrompt = await templateEngine.processRawSettingValue(systemPrompt);
}
const body = {
graph_id: graphId,
user_node_id: userNodeId,
system_prompt: systemPrompt,
system_prompt: finalSystemPrompt,
model: selectedModel || this.plugin.settings.defaultModel,
cache_folder: cacheFolder
};