147 lines
5.7 KiB
TypeScript
147 lines
5.7 KiB
TypeScript
/**
|
||
* Расширяет ссылки на файлы и промпты, заменяя их содержимым перед отправкой на сервер.
|
||
* Обрабатывает как одинарные (@/#), так и постоянные (@@/##) ссылки.
|
||
*/
|
||
|
||
import { FileSystemAdapter, TFile } from 'obsidian';
|
||
import LLMAgentPlugin from 'main';
|
||
import { FileReference, ReferenceParser } from './ReferenceParser';
|
||
import { TemplateEngine } from './TemplateEngine';
|
||
import { Attachment, ChatHistoryItem } from 'src/components/models/ChatHistoryItem';
|
||
import { CacheManager } from './CacheManager';
|
||
import { getMimeType } from 'src/utils/fileUtils';
|
||
|
||
// ----------------------------------------------- Reference Expander ---------------------------------------------------------------
|
||
|
||
export class ReferenceExpander {
|
||
private plugin: LLMAgentPlugin;
|
||
private templateEngine: TemplateEngine;
|
||
private cacheManager: CacheManager;
|
||
|
||
constructor(plugin: LLMAgentPlugin, templateEngine: TemplateEngine) {
|
||
this.plugin = plugin;
|
||
this.templateEngine = templateEngine;
|
||
this.cacheManager = new CacheManager(plugin);
|
||
}
|
||
|
||
// ----------------------------------------------- Public Methods ---------------------------------------------------------------
|
||
|
||
/**
|
||
* Извлекает вложения из текста и кеширует файлы.
|
||
* Обрабатывает как физические файлы, так и виртуальные (current-log).
|
||
*/
|
||
async extractAttachments(
|
||
text: string,
|
||
references: FileReference[]
|
||
): Promise<{
|
||
originalText: string;
|
||
attachments: Attachment[];
|
||
}> {
|
||
const attachments: Attachment[] = [];
|
||
|
||
for (const reference of references) {
|
||
try {
|
||
let cachedPath: string;
|
||
let uuid: string;
|
||
let mimeType: string;
|
||
let sourcePath: string;
|
||
|
||
// --- Обработка виртуальных ссылок на логи ---
|
||
if (reference.path.startsWith("virtual:current-log")) {
|
||
const logsFolder = this.plugin.settings.logsFolder || "audio-logs";
|
||
// Ищем md файлы в папке логов, сортируем по дате изменения (самый свежий — первый)
|
||
const logFiles = this.plugin.app.vault.getMarkdownFiles()
|
||
.filter(f => f.path.startsWith(logsFolder))
|
||
.sort((a, b) => b.stat.mtime - a.stat.mtime);
|
||
|
||
if (logFiles.length > 0) {
|
||
const file = logFiles[0];
|
||
let content = await this.plugin.app.vault.read(file);
|
||
|
||
// Если выбрана часть, отрезаем последние N символов из настроек
|
||
if (reference.path.endsWith(":part")) {
|
||
const limit = this.plugin.settings.voiceContextLimit; // || 30000;
|
||
content = content.slice(-limit);
|
||
}
|
||
|
||
uuid = "log-" + Date.now();
|
||
cachedPath = `log-snapshot-${Date.now()}.md`;
|
||
mimeType = "text/markdown";
|
||
sourcePath = file.path;
|
||
|
||
// Сохраняем контент лога во временный файл в папку кеша, чтобы бэкенд мог его прочитать
|
||
if (this.plugin.app.vault.adapter instanceof FileSystemAdapter) {
|
||
const cacheFolderPath = this.cacheManager.getCacheFolderPath();
|
||
const fs = window.require('fs').promises;
|
||
|
||
// Убедимся, что папка кеша существует
|
||
try { await fs.mkdir(cacheFolderPath, { recursive: true }); } catch(e) {}
|
||
|
||
await fs.writeFile(`${cacheFolderPath}/${cachedPath}`, content, 'utf8');
|
||
} else {
|
||
throw new Error("Кеширование логов доступно только в десктопной версии");
|
||
}
|
||
} else {
|
||
console.warn(`Файлы логов не найдены в папке: ${logsFolder}`);
|
||
continue;
|
||
}
|
||
}
|
||
// --- Обработка внешних файлов ($ / !$) ---
|
||
else if (reference.type === 'external') {
|
||
const cacheResult = await this.cacheManager.cacheExternalFile(reference.path);
|
||
cachedPath = cacheResult.cachedPath;
|
||
uuid = cacheResult.uuid;
|
||
mimeType = cacheResult.mimeType;
|
||
sourcePath = reference.path; // Абсолютный
|
||
}
|
||
// --- Обработка файлов из Vault (@ / @@) ---
|
||
else if (reference.type === 'file') {
|
||
const file = this.plugin.app.vault.getAbstractFileByPath(reference.path);
|
||
if (file instanceof TFile) {
|
||
const cacheResult = await this.cacheManager.cacheVaultFile(file);
|
||
cachedPath = cacheResult.cachedPath;
|
||
uuid = cacheResult.uuid;
|
||
mimeType = getMimeType(file.name);
|
||
sourcePath = file.path; // Относительный
|
||
} else {
|
||
console.warn(`Файл не найден в vault: ${reference.path}`);
|
||
continue;
|
||
}
|
||
}
|
||
// --- Обработка промптов (# / ##) ---
|
||
else {
|
||
const file = this.plugin.app.vault.getAbstractFileByPath(reference.path);
|
||
|
||
if (file instanceof TFile) {
|
||
const cacheResult = await this.cacheManager.cacheVaultFile(file);
|
||
cachedPath = cacheResult.cachedPath;
|
||
uuid = cacheResult.uuid;
|
||
mimeType = 'text/markdown';
|
||
sourcePath = file.path; // Относительный от promptsFolder
|
||
} else {
|
||
console.warn(`Промпт не найден: ${reference.path}`);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
attachments.push({
|
||
type: reference.type,
|
||
name: reference.name,
|
||
sourcePath: sourcePath,
|
||
cachedPath: cachedPath,
|
||
uuid: uuid,
|
||
permanent: reference.permanent,
|
||
mimeType: mimeType
|
||
});
|
||
} catch (error) {
|
||
console.error(`Не удалось обработать вложение ${reference.name}:`, error);
|
||
}
|
||
}
|
||
|
||
return {
|
||
originalText: text,
|
||
attachments: attachments
|
||
};
|
||
}
|
||
}
|