From c9728e70bbe6f4ddcf913a7e4d0ad2b106819a36 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Thu, 20 Mar 2025 19:50:48 +0000 Subject: [PATCH] also extract Note relationships and send as context --- .../llm/context/modules/context_service.ts | 91 +++++++++++++++++++ .../llm/prompts/llm_prompt_constants.ts | 1 + 2 files changed, 92 insertions(+) diff --git a/src/services/llm/context/modules/context_service.ts b/src/services/llm/context/modules/context_service.ts index dde3cd49c..baefc2691 100644 --- a/src/services/llm/context/modules/context_service.ts +++ b/src/services/llm/context/modules/context_service.ts @@ -7,6 +7,7 @@ import contextFormatter from './context_formatter.js'; import aiServiceManager from '../../ai_service_manager.js'; import { ContextExtractor } from '../index.js'; import { CONTEXT_PROMPTS } from '../../prompts/llm_prompt_constants.js'; +import becca from '../../../../becca/becca.js'; /** * Main context service that integrates all context-related functionality @@ -455,6 +456,51 @@ export class ContextService { for (const note of finalNotes) { agentContext += `### ${note.title}\n`; + // Add relationship information for the note + try { + const noteObj = becca.getNote(note.noteId); + if (noteObj) { + // Get parent notes + const parentNotes = noteObj.getParentNotes(); + if (parentNotes && parentNotes.length > 0) { + agentContext += `**Parent notes:** ${parentNotes.map((p: any) => p.title).join(', ')}\n`; + } + + // Get child notes + const childNotes = noteObj.getChildNotes(); + if (childNotes && childNotes.length > 0) { + agentContext += `**Child notes:** ${childNotes.map((c: any) => c.title).join(', ')}\n`; + } + + // Get attributes + const attributes = noteObj.getAttributes(); + if (attributes && attributes.length > 0) { + const filteredAttrs = attributes.filter((a: any) => !a.name.startsWith('_')); // Filter out system attributes + if (filteredAttrs.length > 0) { + agentContext += `**Attributes:** ${filteredAttrs.map((a: any) => `${a.name}=${a.value}`).join(', ')}\n`; + } + } + + // Get backlinks/related notes through relation attributes + const relationAttrs = attributes?.filter((a: any) => + a.name.startsWith('relation:') || + a.name.startsWith('label:') + ); + + if (relationAttrs && relationAttrs.length > 0) { + agentContext += `**Relationships:** ${relationAttrs.map((a: any) => { + const targetNote = becca.getNote(a.value); + const targetTitle = targetNote ? targetNote.title : a.value; + return `${a.name.substring(a.name.indexOf(':') + 1)} → ${targetTitle}`; + }).join(', ')}\n`; + } + } + } catch (error) { + log.error(`Error getting relationship info for note ${note.noteId}: ${error}`); + } + + agentContext += '\n'; + if (note.content) { // Extract relevant content instead of just taking first 2000 chars const relevantContent = await this.extractRelevantContent(note.content, query, 2000); @@ -474,6 +520,51 @@ export class ContextService { for (const note of finalNotes) { agentContext += `### ${note.title}\n`; + // Add relationship information for the note + try { + const noteObj = becca.getNote(note.noteId); + if (noteObj) { + // Get parent notes + const parentNotes = noteObj.getParentNotes(); + if (parentNotes && parentNotes.length > 0) { + agentContext += `**Parent notes:** ${parentNotes.map((p: any) => p.title).join(', ')}\n`; + } + + // Get child notes + const childNotes = noteObj.getChildNotes(); + if (childNotes && childNotes.length > 0) { + agentContext += `**Child notes:** ${childNotes.map((c: any) => c.title).join(', ')}\n`; + } + + // Get attributes + const attributes = noteObj.getAttributes(); + if (attributes && attributes.length > 0) { + const filteredAttrs = attributes.filter((a: any) => !a.name.startsWith('_')); // Filter out system attributes + if (filteredAttrs.length > 0) { + agentContext += `**Attributes:** ${filteredAttrs.map((a: any) => `${a.name}=${a.value}`).join(', ')}\n`; + } + } + + // Get backlinks/related notes through relation attributes + const relationAttrs = attributes?.filter((a: any) => + a.name.startsWith('relation:') || + a.name.startsWith('label:') + ); + + if (relationAttrs && relationAttrs.length > 0) { + agentContext += `**Relationships:** ${relationAttrs.map((a: any) => { + const targetNote = becca.getNote(a.value); + const targetTitle = targetNote ? targetNote.title : a.value; + return `${a.name.substring(a.name.indexOf(':') + 1)} → ${targetTitle}`; + }).join(', ')}\n`; + } + } + } catch (error) { + log.error(`Error getting relationship info for note ${note.noteId}: ${error}`); + } + + agentContext += '\n'; + if (note.content) { // Extract relevant content instead of just taking first 2000 chars const relevantContent = await this.extractRelevantContent(note.content, query, 2000); diff --git a/src/services/llm/prompts/llm_prompt_constants.ts b/src/services/llm/prompts/llm_prompt_constants.ts index 1d44e26f2..690d12472 100644 --- a/src/services/llm/prompts/llm_prompt_constants.ts +++ b/src/services/llm/prompts/llm_prompt_constants.ts @@ -41,6 +41,7 @@ export const CONTEXT_PROMPTS = { `You are an AI assistant that decides what information needs to be retrieved from a user's knowledge base called TriliumNext Notes to answer the user's question. Given the user's question, generate 3-5 specific search queries that would help find relevant information. Each query should be focused on a different aspect of the question. +Avoid generating queries that are too broad, vague, or about a user's entire Note database, and make sure they are relevant to the user's question. Format your answer as a JSON array of strings, with each string being a search query. Example: ["exact topic mentioned", "related concept 1", "related concept 2"]`,