mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 21:11:30 +08:00 
			
		
		
		
	feat(llm): remove everything to do with embeddings, part 2
This commit is contained in:
		
							parent
							
								
									44a45780b7
								
							
						
					
					
						commit
						44a2e7df21
					
				| @ -11,7 +11,7 @@ import { TPL, addMessageToChat, showSources, hideSources, showLoadingIndicator, | |||||||
| import { formatMarkdown } from "./utils.js"; | import { formatMarkdown } from "./utils.js"; | ||||||
| import { createChatSession, checkSessionExists, setupStreamingResponse, getDirectResponse } from "./communication.js"; | import { createChatSession, checkSessionExists, setupStreamingResponse, getDirectResponse } from "./communication.js"; | ||||||
| import { extractInChatToolSteps } from "./message_processor.js"; | import { extractInChatToolSteps } from "./message_processor.js"; | ||||||
| import { validateEmbeddingProviders } from "./validation.js"; | import { validateProviders } from "./validation.js"; | ||||||
| import type { MessageData, ToolExecutionStep, ChatData } from "./types.js"; | import type { MessageData, ToolExecutionStep, ChatData } from "./types.js"; | ||||||
| import { formatCodeBlocks } from "../../services/syntax_highlight.js"; | import { formatCodeBlocks } from "../../services/syntax_highlight.js"; | ||||||
| import { ClassicEditor, type CKTextEditor, type MentionFeed } from "@triliumnext/ckeditor5"; | import { ClassicEditor, type CKTextEditor, type MentionFeed } from "@triliumnext/ckeditor5"; | ||||||
| @ -616,7 +616,7 @@ export default class LlmChatPanel extends BasicWidget { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Check for any provider validation issues when refreshing
 |         // Check for any provider validation issues when refreshing
 | ||||||
|         await validateEmbeddingProviders(this.validationWarning); |         await validateProviders(this.validationWarning); | ||||||
| 
 | 
 | ||||||
|         // Get current note context if needed
 |         // Get current note context if needed
 | ||||||
|         const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null; |         const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null; | ||||||
| @ -767,7 +767,7 @@ export default class LlmChatPanel extends BasicWidget { | |||||||
|      */ |      */ | ||||||
|     private async processUserMessage(content: string) { |     private async processUserMessage(content: string) { | ||||||
|         // Check for validation issues first
 |         // Check for validation issues first
 | ||||||
|         await validateEmbeddingProviders(this.validationWarning); |         await validateProviders(this.validationWarning); | ||||||
| 
 | 
 | ||||||
|         // Make sure we have a valid session
 |         // Make sure we have a valid session
 | ||||||
|         if (!this.noteId) { |         if (!this.noteId) { | ||||||
|  | |||||||
| @ -26,8 +26,6 @@ interface MetricsData { | |||||||
|         totalBlobs: number; |         totalBlobs: number; | ||||||
|         totalEtapiTokens: number; |         totalEtapiTokens: number; | ||||||
|         totalRecentNotes: number; |         totalRecentNotes: number; | ||||||
|         totalEmbeddings: number; |  | ||||||
|         totalEmbeddingProviders: number; |  | ||||||
|     }; |     }; | ||||||
|     noteTypes: Record<string, number>; |     noteTypes: Record<string, number>; | ||||||
|     attachmentTypes: Record<string, number>; |     attachmentTypes: Record<string, number>; | ||||||
| @ -88,8 +86,6 @@ function formatPrometheusMetrics(data: MetricsData): string { | |||||||
|     addMetric('trilium_blobs_total', data.database.totalBlobs, 'Total number of blob records'); |     addMetric('trilium_blobs_total', data.database.totalBlobs, 'Total number of blob records'); | ||||||
|     addMetric('trilium_etapi_tokens_total', data.database.totalEtapiTokens, 'Number of active ETAPI tokens'); |     addMetric('trilium_etapi_tokens_total', data.database.totalEtapiTokens, 'Number of active ETAPI tokens'); | ||||||
|     addMetric('trilium_recent_notes_total', data.database.totalRecentNotes, 'Number of recent notes tracked'); |     addMetric('trilium_recent_notes_total', data.database.totalRecentNotes, 'Number of recent notes tracked'); | ||||||
|     addMetric('trilium_embeddings_total', data.database.totalEmbeddings, 'Number of note embeddings'); |  | ||||||
|     addMetric('trilium_embedding_providers_total', data.database.totalEmbeddingProviders, 'Number of embedding providers'); |  | ||||||
| 
 | 
 | ||||||
|     // Note types
 |     // Note types
 | ||||||
|     for (const [type, count] of Object.entries(data.noteTypes)) { |     for (const [type, count] of Object.entries(data.noteTypes)) { | ||||||
| @ -155,15 +151,6 @@ function collectMetrics(): MetricsData { | |||||||
|     const totalEtapiTokens = sql.getValue<number>("SELECT COUNT(*) FROM etapi_tokens WHERE isDeleted = 0"); |     const totalEtapiTokens = sql.getValue<number>("SELECT COUNT(*) FROM etapi_tokens WHERE isDeleted = 0"); | ||||||
|     const totalRecentNotes = sql.getValue<number>("SELECT COUNT(*) FROM recent_notes"); |     const totalRecentNotes = sql.getValue<number>("SELECT COUNT(*) FROM recent_notes"); | ||||||
| 
 | 
 | ||||||
|     // Embedding-related metrics (these tables might not exist in older versions)
 |  | ||||||
|     let totalEmbeddings = 0; |  | ||||||
|     let totalEmbeddingProviders = 0; |  | ||||||
|     try { |  | ||||||
|         totalEmbeddings = sql.getValue<number>("SELECT COUNT(*) FROM note_embeddings"); |  | ||||||
|         totalEmbeddingProviders = sql.getValue<number>("SELECT COUNT(*) FROM embedding_providers"); |  | ||||||
|     } catch (e) { |  | ||||||
|         // Tables don't exist, keep defaults
 |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     const database = { |     const database = { | ||||||
|         totalNotes, |         totalNotes, | ||||||
| @ -179,8 +166,6 @@ function collectMetrics(): MetricsData { | |||||||
|         totalBlobs, |         totalBlobs, | ||||||
|         totalEtapiTokens, |         totalEtapiTokens, | ||||||
|         totalRecentNotes, |         totalRecentNotes, | ||||||
|         totalEmbeddings, |  | ||||||
|         totalEmbeddingProviders |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // Note types breakdown
 |     // Note types breakdown
 | ||||||
|  | |||||||
| @ -99,12 +99,6 @@ type MetricsData = ReturnType<typeof etapiMetrics.collectMetrics>; | |||||||
|  *                     totalRecentNotes: |  *                     totalRecentNotes: | ||||||
|  *                       type: integer |  *                       type: integer | ||||||
|  *                       example: 50 |  *                       example: 50 | ||||||
|  *                     totalEmbeddings: |  | ||||||
|  *                       type: integer |  | ||||||
|  *                       example: 123 |  | ||||||
|  *                     totalEmbeddingProviders: |  | ||||||
|  *                       type: integer |  | ||||||
|  *                       example: 2 |  | ||||||
|  *                 noteTypes: |  *                 noteTypes: | ||||||
|  *                   type: object |  *                   type: object | ||||||
|  *                   additionalProperties: |  *                   additionalProperties: | ||||||
|  | |||||||
| @ -28,10 +28,6 @@ function eraseNotes(noteIdsToErase: string[]) { | |||||||
| 
 | 
 | ||||||
|     eraseRevisions(revisionIdsToErase); |     eraseRevisions(revisionIdsToErase); | ||||||
| 
 | 
 | ||||||
|     // Erase embeddings related to the deleted notes
 |  | ||||||
|     const embeddingIdsToErase = sql.getManyRows<{ embedId: string }>(`SELECT embedId FROM note_embeddings WHERE noteId IN (???)`, noteIdsToErase).map((row) => row.embedId); |  | ||||||
| 
 |  | ||||||
|     eraseEmbeddings(embeddingIdsToErase); |  | ||||||
| 
 | 
 | ||||||
|     log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`); |     log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`); | ||||||
| } | } | ||||||
| @ -156,12 +152,6 @@ function eraseNotesWithDeleteId(deleteId: string) { | |||||||
|     const attachmentIdsToErase = sql.getColumn<string>("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]); |     const attachmentIdsToErase = sql.getColumn<string>("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]); | ||||||
|     eraseAttachments(attachmentIdsToErase); |     eraseAttachments(attachmentIdsToErase); | ||||||
| 
 | 
 | ||||||
|     // Find and erase embeddings for deleted notes
 |  | ||||||
|     const deletedNoteIds = sql.getColumn<string>("SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]); |  | ||||||
|     if (deletedNoteIds.length > 0) { |  | ||||||
|         const embeddingIdsToErase = sql.getColumn<string>("SELECT embedId FROM note_embeddings WHERE noteId IN (???)", deletedNoteIds); |  | ||||||
|         eraseEmbeddings(embeddingIdsToErase); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     eraseUnusedBlobs(); |     eraseUnusedBlobs(); | ||||||
| } | } | ||||||
| @ -185,16 +175,6 @@ function eraseScheduledAttachments(eraseUnusedAttachmentsAfterSeconds: number | | |||||||
|     eraseAttachments(attachmentIdsToErase); |     eraseAttachments(attachmentIdsToErase); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function eraseEmbeddings(embedIdsToErase: string[]) { |  | ||||||
|     if (embedIdsToErase.length === 0) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     sql.executeMany(`DELETE FROM note_embeddings WHERE embedId IN (???)`, embedIdsToErase); |  | ||||||
|     setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'note_embeddings' AND entityId IN (???)`, embedIdsToErase)); |  | ||||||
| 
 |  | ||||||
|     log.info(`Erased embeddings: ${JSON.stringify(embedIdsToErase)}`); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export function startScheduledCleanup() { | export function startScheduledCleanup() { | ||||||
|     sqlInit.dbReady.then(() => { |     sqlInit.dbReady.then(() => { | ||||||
|  | |||||||
| @ -364,14 +364,6 @@ function getEntityChangeRow(entityChange: EntityChange) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Special handling for note_embeddings embedding field
 |  | ||||||
|         if (entityName === "note_embeddings") { |  | ||||||
|             // Cast to any to access the embedding property
 |  | ||||||
|             const row = entityRow as any; |  | ||||||
|             if (row.embedding && Buffer.isBuffer(row.embedding)) { |  | ||||||
|                 row.embedding = row.embedding.toString("base64"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         return entityRow; |         return entityRow; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -54,9 +54,7 @@ function updateEntity(remoteEC: EntityChange, remoteEntityRow: EntityRow | undef | |||||||
| 
 | 
 | ||||||
|     const updated = remoteEC.entityName === "note_reordering" |     const updated = remoteEC.entityName === "note_reordering" | ||||||
|         ? updateNoteReordering(remoteEC, remoteEntityRow, instanceId) |         ? updateNoteReordering(remoteEC, remoteEntityRow, instanceId) | ||||||
|         : (remoteEC.entityName === "note_embeddings" |         : updateNormalEntity(remoteEC, remoteEntityRow, instanceId, updateContext); | ||||||
|             ? updateNoteEmbedding(remoteEC, remoteEntityRow, instanceId, updateContext) |  | ||||||
|             : updateNormalEntity(remoteEC, remoteEntityRow, instanceId, updateContext)); |  | ||||||
| 
 | 
 | ||||||
|     if (updated) { |     if (updated) { | ||||||
|         if (remoteEntityRow?.isDeleted) { |         if (remoteEntityRow?.isDeleted) { | ||||||
| @ -145,78 +143,11 @@ function updateNoteReordering(remoteEC: EntityChange, remoteEntityRow: EntityRow | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateNoteEmbedding(remoteEC: EntityChange, remoteEntityRow: EntityRow | undefined, instanceId: string, updateContext: UpdateContext) { |  | ||||||
|     if (remoteEC.isErased) { |  | ||||||
|         eraseEntity(remoteEC); |  | ||||||
|         updateContext.erased++; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (!remoteEntityRow) { |  | ||||||
|         log.error(`Entity ${remoteEC.entityName} ${remoteEC.entityId} not found in sync update.`); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     interface NoteEmbeddingRow { |  | ||||||
|         embedId: string; |  | ||||||
|         noteId: string; |  | ||||||
|         providerId: string; |  | ||||||
|         modelId: string; |  | ||||||
|         dimension: number; |  | ||||||
|         embedding: Buffer; |  | ||||||
|         version: number; |  | ||||||
|         dateCreated: string; |  | ||||||
|         utcDateCreated: string; |  | ||||||
|         dateModified: string; |  | ||||||
|         utcDateModified: string; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Cast remoteEntityRow to include required embedding properties
 |  | ||||||
|     const typedRemoteEntityRow = remoteEntityRow as unknown as NoteEmbeddingRow; |  | ||||||
| 
 |  | ||||||
|     // Convert embedding from base64 string to Buffer if needed
 |  | ||||||
|     if (typedRemoteEntityRow.embedding && typeof typedRemoteEntityRow.embedding === "string") { |  | ||||||
|         typedRemoteEntityRow.embedding = Buffer.from(typedRemoteEntityRow.embedding, "base64"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const localEntityRow = sql.getRow<NoteEmbeddingRow>(`SELECT * FROM note_embeddings WHERE embedId = ?`, [remoteEC.entityId]); |  | ||||||
| 
 |  | ||||||
|     if (localEntityRow) { |  | ||||||
|         // We already have this embedding, check if we need to update it
 |  | ||||||
|         if (localEntityRow.utcDateModified >= typedRemoteEntityRow.utcDateModified) { |  | ||||||
|             // Local is newer or same, no need to update
 |  | ||||||
|             entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId); |  | ||||||
|             return true; |  | ||||||
|         } else { |  | ||||||
|             // Remote is newer, update local
 |  | ||||||
|             sql.replace("note_embeddings", remoteEntityRow); |  | ||||||
| 
 |  | ||||||
|             if (!updateContext.updated[remoteEC.entityName]) { |  | ||||||
|                 updateContext.updated[remoteEC.entityName] = []; |  | ||||||
|             } |  | ||||||
|             updateContext.updated[remoteEC.entityName].push(remoteEC.entityId); |  | ||||||
| 
 |  | ||||||
|             entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         // We don't have this embedding, insert it
 |  | ||||||
|         sql.replace("note_embeddings", remoteEntityRow); |  | ||||||
| 
 |  | ||||||
|         if (!updateContext.updated[remoteEC.entityName]) { |  | ||||||
|             updateContext.updated[remoteEC.entityName] = []; |  | ||||||
|         } |  | ||||||
|         updateContext.updated[remoteEC.entityName].push(remoteEC.entityId); |  | ||||||
| 
 |  | ||||||
|         entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| function eraseEntity(entityChange: EntityChange) { | function eraseEntity(entityChange: EntityChange) { | ||||||
|     const { entityName, entityId } = entityChange; |     const { entityName, entityId } = entityChange; | ||||||
| 
 | 
 | ||||||
|     const entityNames = ["notes", "branches", "attributes", "revisions", "attachments", "blobs", "note_embeddings"]; |     const entityNames = ["notes", "branches", "attributes", "revisions", "attachments", "blobs"]; | ||||||
| 
 | 
 | ||||||
|     if (!entityNames.includes(entityName)) { |     if (!entityNames.includes(entityName)) { | ||||||
|         log.error(`Cannot erase ${entityName} '${entityId}'.`); |         log.error(`Cannot erase ${entityName} '${entityId}'.`); | ||||||
|  | |||||||
| @ -203,13 +203,6 @@ function fillInAdditionalProperties(entityChange: EntityChange) { | |||||||
|                                                 WHERE attachmentId = ?`,
 |                                                 WHERE attachmentId = ?`,
 | ||||||
|             [entityChange.entityId] |             [entityChange.entityId] | ||||||
|         ); |         ); | ||||||
|     } else if (entityChange.entityName === "note_embeddings") { |  | ||||||
|         // Note embeddings are backend-only entities for AI/vector search
 |  | ||||||
|         // Frontend doesn't need the full embedding data (which is large binary data)
 |  | ||||||
|         // Just ensure entity is marked as handled - actual sync happens at database level
 |  | ||||||
|         if (!entityChange.isErased) { |  | ||||||
|             entityChange.entity = { embedId: entityChange.entityId }; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (entityChange.entity instanceof AbstractBeccaEntity) { |     if (entityChange.entity instanceof AbstractBeccaEntity) { | ||||||
| @ -228,7 +221,6 @@ const ORDERING: Record<string, number> = { | |||||||
|     attachments: 3, |     attachments: 3, | ||||||
|     notes: 1, |     notes: 1, | ||||||
|     options: 0, |     options: 0, | ||||||
|     note_embeddings: 3 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| function sendPing(client: WebSocket, entityChangeIds = []) { | function sendPing(client: WebSocket, entityChangeIds = []) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 perf3ct
						perf3ct