Support linking audio-logs with @ in chat
This commit is contained in:
parent
9ae3256c35
commit
f66dd0920b
33
main.ts
33
main.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, ItemView, WorkspaceLeaf, TFile } from 'obsidian';
|
||||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, ItemView, WorkspaceLeaf, TFile, FileSystemAdapter } from 'obsidian';
|
||||
import { EventBus } from './src/utils/EventBus';
|
||||
import { GraphView, GRAPH_VIEW_TYPE } from './src/views/GraphView';
|
||||
import { ChatView, CHAT_VIEW_TYPE } from './src/views/ChatView';
|
||||
|
|
@ -24,6 +24,7 @@ interface LLMAgentSettings {
|
|||
|
||||
summarizationModel: string;
|
||||
voiceCommandModel: string;
|
||||
logsFolder: string;
|
||||
voiceInterval: number;
|
||||
voiceDisplayLimit: number; // 3000
|
||||
voiceContextLimit: number; // N2 символов для LLM
|
||||
|
|
@ -43,10 +44,12 @@ const DEFAULT_SETTINGS: LLMAgentSettings = {
|
|||
excludedFolders: ['.obsidian', 'llm-agent-backend', 'templates', 'ref-cache'], // Папка "templates" по умолчанию исключена
|
||||
refCacheFolder: 'ref-cache', // По умолчанию относительно vault
|
||||
|
||||
voiceModel: '',
|
||||
summarizationModel: '',
|
||||
voiceCommandModel: '',
|
||||
logsFolder: 'audio-logs',
|
||||
voiceInterval: 1,
|
||||
voiceDisplayLimit: 3000,
|
||||
voiceContextLimit: 3000,
|
||||
voiceDisplayLimit: 30000,
|
||||
voiceContextLimit: 30000,
|
||||
promptRegular: '',
|
||||
promptCommands: '',
|
||||
markerStart: 'старт',
|
||||
|
|
@ -74,6 +77,13 @@ export default class LLMAgentPlugin extends Plugin {
|
|||
settingsToSync.promptRegular = await engine.processRawSettingValue(this.settings.promptRegular);
|
||||
settingsToSync.promptCommands = await engine.processRawSettingValue(this.settings.promptCommands);
|
||||
|
||||
const adapter = this.app.vault.adapter;
|
||||
if (adapter instanceof FileSystemAdapter) {
|
||||
// Соединяем базовый путь хранилища и относительный путь к логам
|
||||
settingsToSync.absoluteLogsPath = adapter.getBasePath() +
|
||||
(this.settings.logsFolder ? '/' + this.settings.logsFolder : '');
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.settings.apiBaseUrl}/settings/sync`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -310,6 +320,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('Как часто LLM анализирует последние фразы (Prompt 1).')
|
||||
|
|
|
|||
|
|
@ -45,22 +45,36 @@ export class AutocompleteManager {
|
|||
* @returns массив файлов для автокомплита
|
||||
*/
|
||||
async getFileOptions(query: string): Promise<AutocompleteItem[]> {
|
||||
if (!this.plugin.settings.enableFileReferences) {
|
||||
return [];
|
||||
if (!this.plugin.settings.enableFileReferences) return [];
|
||||
|
||||
const lowercaseQuery = query.toLowerCase();
|
||||
const virtualItems: AutocompleteItem[] = [];
|
||||
|
||||
// Пункт 1: Если в запросе "cur", добавляем логи в начало
|
||||
if ("current-log".includes(lowercaseQuery)) {
|
||||
virtualItems.push({
|
||||
name: "current-log",
|
||||
path: "virtual:current-log",
|
||||
type: "file", // Для применения иконки @ по умолчанию
|
||||
icon: "file-text"
|
||||
});
|
||||
virtualItems.push({
|
||||
name: "current-log:part",
|
||||
path: "virtual:current-log:part",
|
||||
type: "file",
|
||||
icon: "file-text"
|
||||
});
|
||||
}
|
||||
|
||||
await this.updateCacheIfNeeded();
|
||||
|
||||
const allFiles = this.fileCache.get('all') || [];
|
||||
|
||||
if (!query.trim()) {
|
||||
return allFiles.slice(0, AutocompleteManager.MAX_TOTAL_ITEMS); // Показываем первые 10 файлов
|
||||
}
|
||||
// Объединяем: виртуальные вначале, потом поиск по файлам
|
||||
const filteredFiles = !query.trim()
|
||||
? allFiles
|
||||
: allFiles.filter((item) => item.name.toLowerCase().includes(lowercaseQuery) || item.path.toLowerCase().includes(lowercaseQuery));
|
||||
|
||||
const lowercaseQuery = query.toLowerCase();
|
||||
return allFiles
|
||||
.filter((item) => item.name.toLowerCase().includes(lowercaseQuery) || item.path.toLowerCase().includes(lowercaseQuery))
|
||||
.slice(0, AutocompleteManager.MAX_TOTAL_ITEMS);
|
||||
return [...virtualItems, ...filteredFiles].slice(0, AutocompleteManager.MAX_TOTAL_ITEMS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Обрабатывает как одинарные (@/#), так и постоянные (@@/##) ссылки.
|
||||
*/
|
||||
|
||||
import { TFile } from 'obsidian';
|
||||
import { FileSystemAdapter, TFile } from 'obsidian';
|
||||
import LLMAgentPlugin from 'main';
|
||||
import { FileReference, ReferenceParser } from './ReferenceParser';
|
||||
import { TemplateEngine } from './TemplateEngine';
|
||||
|
|
@ -79,7 +79,8 @@ export class ReferenceExpander {
|
|||
}*/
|
||||
|
||||
/**
|
||||
* Извлекает вложения из текста и кеширует файлы
|
||||
* Извлекает вложения из текста и кеширует файлы.
|
||||
* Обрабатывает как физические файлы, так и виртуальные (current-log).
|
||||
*/
|
||||
async extractAttachments(
|
||||
text: string,
|
||||
|
|
@ -97,15 +98,56 @@ export class ReferenceExpander {
|
|||
let mimeType: string;
|
||||
let sourcePath: string;
|
||||
|
||||
if (reference.type === 'external') {
|
||||
// Кешируем внешний файл
|
||||
// --- Обработка виртуальных ссылок на логи ---
|
||||
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; // Абсолютный
|
||||
} else if (reference.type === 'file') {
|
||||
// ИЗМЕНЕНО: Кешируем файл из vault
|
||||
}
|
||||
// --- Обработка файлов из 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);
|
||||
|
|
@ -117,9 +159,9 @@ export class ReferenceExpander {
|
|||
console.warn(`Файл не найден в vault: ${reference.path}`);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// prompt
|
||||
// Кешируем промпт
|
||||
}
|
||||
// --- Обработка промптов (# / ##) ---
|
||||
else {
|
||||
const file = this.plugin.app.vault.getAbstractFileByPath(reference.path);
|
||||
|
||||
if (file instanceof TFile) {
|
||||
|
|
@ -144,7 +186,7 @@ export class ReferenceExpander {
|
|||
mimeType: mimeType
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Не удалось закешировать ${reference.name}:`, error);
|
||||
console.error(`Не удалось обработать вложение ${reference.name}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user