mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-09-03 05:57:47 +08:00
feat(llm): add utils for excluding notes from LLM
This commit is contained in:
parent
ba98bd9097
commit
3fae664877
@ -211,5 +211,10 @@ export const LLM_CONSTANTS = {
|
|||||||
CONTENT: {
|
CONTENT: {
|
||||||
MAX_NOTE_CONTENT_LENGTH: 1500,
|
MAX_NOTE_CONTENT_LENGTH: 1500,
|
||||||
MAX_TOTAL_CONTENT_LENGTH: 10000
|
MAX_TOTAL_CONTENT_LENGTH: 10000
|
||||||
|
},
|
||||||
|
|
||||||
|
// AI Feature Exclusion
|
||||||
|
AI_EXCLUSION: {
|
||||||
|
LABEL_NAME: 'aiExclude' // Label used to exclude notes from all AI/LLM features
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -18,6 +18,7 @@ import cacheManager from '../modules/cache_manager.js';
|
|||||||
import type { NoteSearchResult } from '../../interfaces/context_interfaces.js';
|
import type { NoteSearchResult } from '../../interfaces/context_interfaces.js';
|
||||||
import type { LLMServiceInterface } from '../../interfaces/agent_tool_interfaces.js';
|
import type { LLMServiceInterface } from '../../interfaces/agent_tool_interfaces.js';
|
||||||
import { SEARCH_CONSTANTS } from '../../constants/search_constants.js';
|
import { SEARCH_CONSTANTS } from '../../constants/search_constants.js';
|
||||||
|
import { isNoteExcludedFromAI } from '../../utils/ai_exclusion_utils.js';
|
||||||
|
|
||||||
export interface VectorSearchOptions {
|
export interface VectorSearchOptions {
|
||||||
maxResults?: number;
|
maxResults?: number;
|
||||||
@ -118,6 +119,11 @@ export class VectorSearchService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this note is excluded from AI features
|
||||||
|
if (isNoteExcludedFromAI(note)) {
|
||||||
|
return null; // Skip this note if it has the AI exclusion label
|
||||||
|
}
|
||||||
|
|
||||||
// Get note content - full or summarized based on option
|
// Get note content - full or summarized based on option
|
||||||
let content: string | null = null;
|
let content: string | null = null;
|
||||||
|
|
||||||
@ -289,6 +295,12 @@ export class VectorSearchService {
|
|||||||
|
|
||||||
for (const noteId of noteIds) {
|
for (const noteId of noteIds) {
|
||||||
try {
|
try {
|
||||||
|
// Check if this note is excluded from AI features
|
||||||
|
const note = becca.getNote(noteId);
|
||||||
|
if (!note || isNoteExcludedFromAI(note)) {
|
||||||
|
continue; // Skip this note if it doesn't exist or has the AI exclusion label
|
||||||
|
}
|
||||||
|
|
||||||
// Get note embedding
|
// Get note embedding
|
||||||
const embeddingResult = await vectorStore.getEmbeddingForNote(
|
const embeddingResult = await vectorStore.getEmbeddingForNote(
|
||||||
noteId,
|
noteId,
|
||||||
|
@ -9,6 +9,7 @@ import { deleteNoteEmbeddings } from "./storage.js";
|
|||||||
import type { QueueItem } from "./types.js";
|
import type { QueueItem } from "./types.js";
|
||||||
import { getChunkingOperations } from "./chunking/chunking_interface.js";
|
import { getChunkingOperations } from "./chunking/chunking_interface.js";
|
||||||
import indexService from '../index_service.js';
|
import indexService from '../index_service.js';
|
||||||
|
import { isNoteExcludedFromAIById } from "../utils/ai_exclusion_utils.js";
|
||||||
|
|
||||||
// Track which notes are currently being processed
|
// Track which notes are currently being processed
|
||||||
const notesInProcess = new Set<string>();
|
const notesInProcess = new Set<string>();
|
||||||
@ -261,6 +262,17 @@ export async function processEmbeddingQueue() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this note is excluded from AI features
|
||||||
|
if (isNoteExcludedFromAIById(noteId)) {
|
||||||
|
log.info(`Note ${noteId} excluded from AI features, removing from embedding queue`);
|
||||||
|
await sql.execute(
|
||||||
|
"DELETE FROM embedding_queue WHERE noteId = ?",
|
||||||
|
[noteId]
|
||||||
|
);
|
||||||
|
await deleteNoteEmbeddings(noteId); // Also remove any existing embeddings
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (noteData.operation === 'DELETE') {
|
if (noteData.operation === 'DELETE') {
|
||||||
await deleteNoteEmbeddings(noteId);
|
await deleteNoteEmbeddings(noteId);
|
||||||
await sql.execute(
|
await sql.execute(
|
||||||
|
@ -8,6 +8,9 @@ import entityChangesService from "../../../services/entity_changes.js";
|
|||||||
import type { EntityChange } from "../../../services/entity_changes_interface.js";
|
import type { EntityChange } from "../../../services/entity_changes_interface.js";
|
||||||
import { EMBEDDING_CONSTANTS } from "../constants/embedding_constants.js";
|
import { EMBEDDING_CONSTANTS } from "../constants/embedding_constants.js";
|
||||||
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
||||||
|
import type { NoteEmbeddingContext } from "./embeddings_interface.js";
|
||||||
|
import becca from "../../../becca/becca.js";
|
||||||
|
import { isNoteExcludedFromAIById } from "../utils/ai_exclusion_utils.js";
|
||||||
|
|
||||||
interface Similarity {
|
interface Similarity {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
@ -452,6 +455,11 @@ async function processEmbeddings(queryEmbedding: Float32Array, embeddings: any[]
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
for (const e of embeddings) {
|
for (const e of embeddings) {
|
||||||
|
// Check if this note is excluded from AI features
|
||||||
|
if (isNoteExcludedFromAIById(e.noteId)) {
|
||||||
|
continue; // Skip this note if it has the AI exclusion label
|
||||||
|
}
|
||||||
|
|
||||||
const embVector = bufferToEmbedding(e.embedding, e.dimension);
|
const embVector = bufferToEmbedding(e.embedding, e.dimension);
|
||||||
|
|
||||||
// Detect content type from mime type if available
|
// Detect content type from mime type if available
|
||||||
|
@ -20,6 +20,7 @@ import sql from "../sql.js";
|
|||||||
import sqlInit from "../sql_init.js";
|
import sqlInit from "../sql_init.js";
|
||||||
import { CONTEXT_PROMPTS } from './constants/llm_prompt_constants.js';
|
import { CONTEXT_PROMPTS } from './constants/llm_prompt_constants.js';
|
||||||
import { SEARCH_CONSTANTS } from './constants/search_constants.js';
|
import { SEARCH_CONSTANTS } from './constants/search_constants.js';
|
||||||
|
import { isNoteExcludedFromAI } from "./utils/ai_exclusion_utils.js";
|
||||||
|
|
||||||
export class IndexService {
|
export class IndexService {
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
@ -803,6 +804,12 @@ export class IndexService {
|
|||||||
throw new Error(`Note ${noteId} not found`);
|
throw new Error(`Note ${noteId} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this note is excluded from AI features
|
||||||
|
if (isNoteExcludedFromAI(note)) {
|
||||||
|
log.info(`Note ${noteId} (${note.title}) excluded from AI indexing due to exclusion label`);
|
||||||
|
return true; // Return true to indicate successful handling (exclusion is intentional)
|
||||||
|
}
|
||||||
|
|
||||||
// Check where embedding generation should happen
|
// Check where embedding generation should happen
|
||||||
const embeddingLocation = await options.getOption('embeddingGenerationLocation') || 'client';
|
const embeddingLocation = await options.getOption('embeddingGenerationLocation') || 'client';
|
||||||
|
|
||||||
|
94
apps/server/src/services/llm/utils/ai_exclusion_utils.ts
Normal file
94
apps/server/src/services/llm/utils/ai_exclusion_utils.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import becca from '../../../becca/becca.js';
|
||||||
|
import type BNote from '../../../becca/entities/bnote.js';
|
||||||
|
import { LLM_CONSTANTS } from '../constants/provider_constants.js';
|
||||||
|
import log from '../../log.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a note should be excluded from all AI/LLM features
|
||||||
|
*
|
||||||
|
* @param note - The note to check (BNote object)
|
||||||
|
* @returns true if the note should be excluded from AI features
|
||||||
|
*/
|
||||||
|
export function isNoteExcludedFromAI(note: BNote): boolean {
|
||||||
|
if (!note) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if the note has the AI exclusion label
|
||||||
|
const hasExclusionLabel = note.hasLabel(LLM_CONSTANTS.AI_EXCLUSION.LABEL_NAME);
|
||||||
|
|
||||||
|
if (hasExclusionLabel) {
|
||||||
|
log.info(`Note ${note.noteId} (${note.title}) excluded from AI features due to ${LLM_CONSTANTS.AI_EXCLUSION.LABEL_NAME} label`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error checking AI exclusion for note ${note.noteId}: ${error}`);
|
||||||
|
return false; // Default to not excluding on error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a note should be excluded from AI features by noteId
|
||||||
|
*
|
||||||
|
* @param noteId - The ID of the note to check
|
||||||
|
* @returns true if the note should be excluded from AI features
|
||||||
|
*/
|
||||||
|
export function isNoteExcludedFromAIById(noteId: string): boolean {
|
||||||
|
if (!noteId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const note = becca.getNote(noteId);
|
||||||
|
if (!note) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isNoteExcludedFromAI(note);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error checking AI exclusion for note ID ${noteId}: ${error}`);
|
||||||
|
return false; // Default to not excluding on error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter out notes that are excluded from AI features
|
||||||
|
*
|
||||||
|
* @param notes - Array of notes to filter
|
||||||
|
* @returns Array of notes with AI-excluded notes removed
|
||||||
|
*/
|
||||||
|
export function filterAIExcludedNotes(notes: BNote[]): BNote[] {
|
||||||
|
return notes.filter(note => !isNoteExcludedFromAI(note));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter out note IDs that are excluded from AI features
|
||||||
|
*
|
||||||
|
* @param noteIds - Array of note IDs to filter
|
||||||
|
* @returns Array of note IDs with AI-excluded notes removed
|
||||||
|
*/
|
||||||
|
export function filterAIExcludedNoteIds(noteIds: string[]): string[] {
|
||||||
|
return noteIds.filter(noteId => !isNoteExcludedFromAIById(noteId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any notes in an array are excluded from AI features
|
||||||
|
*
|
||||||
|
* @param notes - Array of notes to check
|
||||||
|
* @returns true if any note should be excluded from AI features
|
||||||
|
*/
|
||||||
|
export function hasAIExcludedNotes(notes: BNote[]): boolean {
|
||||||
|
return notes.some(note => isNoteExcludedFromAI(note));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the AI exclusion label name from constants
|
||||||
|
* This can be used in UI components or other places that need to reference the label
|
||||||
|
*
|
||||||
|
* @returns The label name used for AI exclusion
|
||||||
|
*/
|
||||||
|
export function getAIExclusionLabelName(): string {
|
||||||
|
return LLM_CONSTANTS.AI_EXCLUSION.LABEL_NAME;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user