Feature/semantic similarity (#36)

* fix: mixed up migrations

* feat: semantic similarity first take

* feat: semantic similarity first round of fixes

* feat: more work on making semantic similarity work properly

* feat: getPostBySlug for the AI

* feat: show similarity in post-link-insert-modal

* chore: remove done doc

---------

Co-authored-by: hugo <hugoms@me.com>
This commit is contained in:
Georg Bauer
2026-03-05 22:05:32 +01:00
committed by GitHub
parent 8ac8305e01
commit 7e1e8981a3
64 changed files with 6429 additions and 499 deletions

View File

@@ -2,7 +2,7 @@ import { app, BrowserWindow, Menu, MenuItemConstructorOptions, ipcMain, protocol
import * as path from 'path';
import * as fs from 'fs';
import { getDatabase, initDatabase } from './database';
import { registerIpcHandlers, registerEventForwarding, registerChatHandlers, initializeChatHandlers, cleanupChatHandlers } from './ipc';
import { registerIpcHandlers, registerEventForwarding, registerChatHandlers, initializeChatHandlers, cleanupChatHandlers, startEmbeddingIndexTask, startRebuildEmbeddingIndexTask } from './ipc';
import { media } from './database/schema';
import { eq } from 'drizzle-orm';
import { MediaEngine } from './engine/MediaEngine';
@@ -26,6 +26,7 @@ import { BlogmarkPythonWorkerRuntime } from './engine/BlogmarkPythonWorkerRuntim
import { PythonMacroWorkerRuntime } from './engine/PythonMacroWorkerRuntime';
import { AppApiAdapter } from './engine/AppApiAdapter';
import { PublishApiAdapter } from './engine/PublishApiAdapter';
import { EmbeddingEngine } from './engine/EmbeddingEngine';
import { NoopNotifier } from './engine/CliNotifier';
import { NotificationWatcher } from './engine/NotificationWatcher';
import { setEngineBundle } from './engine/mainProcessPythonApiInvoker';
@@ -536,6 +537,9 @@ async function initializeActiveProjectContext(): Promise<void> {
mediaEngine.setProjectContext?.(project.id, dataDir, dataDir);
metaEngine.setProjectContext?.(project.id, dataDir);
const embeddingEngineInstance = bundle!.embeddingEngine;
await embeddingEngineInstance.setProjectContext(project.id);
const templateEngine = bundle!.templateEngine as {
setProjectContext?: (projectId: string, dataDir?: string) => void;
};
@@ -645,6 +649,11 @@ function createApplicationMenu(): Menu {
return;
}
if (action === 'rebuildEmbeddingIndex') {
startRebuildEmbeddingIndexTask(bundle!);
return;
}
const channel = APP_MENU_ACTION_EVENT_MAP[action];
if (channel) {
mainWindow?.webContents.send(channel);
@@ -928,6 +937,10 @@ app.whenReady().then(async () => {
const blogmarkPythonWorkerRuntime = new BlogmarkPythonWorkerRuntime();
const pythonMacroWorkerRuntime = new PythonMacroWorkerRuntime();
const blogmarkTransformService = new BlogmarkTransformService({ scriptEngine, metaEngine, blogmarkWorkerRuntime: blogmarkPythonWorkerRuntime });
const embeddingEngine = new EmbeddingEngine({
getIndexPath: (projectId: string) =>
path.join(userData, 'projects', projectId, 'embeddings.usearch'),
});
const appApiAdapter = new AppApiAdapter(projectEngine);
const publishApiAdapter = new PublishApiAdapter(projectEngine, publishEngine, taskManager);
const mcpServer = new MCPServer({
@@ -961,6 +974,7 @@ app.whenReady().then(async () => {
pythonMacroWorkerRuntime,
publishApiAdapter,
appApiAdapter,
embeddingEngine,
};
setEngineBundle(bundle);
@@ -1000,6 +1014,16 @@ app.whenReady().then(async () => {
await activeProjectContextReady;
appInitialized = true;
// If semantic similarity was already enabled when the app started, kick off indexing.
if (bundle) {
const startupBundle = bundle;
startupBundle.metaEngine.getProjectMetadata().then((metadata) => {
if (metadata?.semanticSimilarityEnabled === true) {
startEmbeddingIndexTask(startupBundle);
}
}).catch(() => {});
}
const startupDeepLinks = extractBlogmarkDeepLinks(process.argv);
for (const deepLink of startupDeepLinks) {
enqueueBlogmarkDeepLink(deepLink);
@@ -1038,6 +1062,12 @@ app.on('before-quit', async () => {
console.error('Failed to cleanup MCP server:', error);
}
try {
await bundle?.embeddingEngine.shutdown();
} catch (error) {
console.error('Failed to shutdown embedding engine:', error);
}
const db = getDatabase();
await db.close();
});