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

@@ -54,6 +54,7 @@ export interface ProjectMetadata {
picoTheme?: import('./picoThemes').PicoThemeName;
categoryMetadata?: Record<string, CategoryMetadata>;
categorySettings?: Record<string, CategoryRenderSettings>;
semanticSimilarityEnabled?: boolean;
}
export interface CategoryRenderSettings {
@@ -505,6 +506,23 @@ export interface ChatSendMetadata {
import type { A2UIServerMessage, A2UIClientAction } from '../a2ui/types';
export type { A2UIServerMessage, A2UIClientAction };
export interface SimilarPost {
postId: string;
similarity: number;
}
export interface TagSuggestion {
name: string;
score: number;
}
export interface DuplicatePair {
postA: { id: string; title: string; slug: string; publishedAt?: Date };
postB: { id: string; title: string; slug: string; publishedAt?: Date };
similarity: number;
exactMatch?: boolean;
}
export interface SiteValidationReport {
sitemapPath: string;
sitemapChanged: boolean;
@@ -577,6 +595,7 @@ export interface ElectronAPI {
update: (id: string, data: Partial<PostData>) => Promise<PostData | null>;
delete: (id: string) => Promise<boolean>;
get: (id: string) => Promise<PostData | null>;
getBySlug: (slug: string) => Promise<PostData | null>;
getPreviewUrl: (id: string, options?: { draft?: boolean }) => Promise<string | null>;
getAll: (options?: { limit?: number; offset?: number }) => Promise<PaginatedPostsResult>;
getByStatus: (status: string) => Promise<PostData[]>;
@@ -728,7 +747,7 @@ export interface ElectronAPI {
syncOnStartup: () => Promise<{ tags: string[]; categories: string[]; projectMetadata: ProjectMetadata | null }>;
getProjectMetadata: () => Promise<ProjectMetadata | null>;
setProjectMetadata: (metadata: { name: string; description?: string }) => Promise<ProjectMetadata | null>;
updateProjectMetadata: (updates: { name?: string; description?: string; dataPath?: string; publicUrl?: string; mainLanguage?: string; defaultAuthor?: string; maxPostsPerPage?: number; blogmarkCategory?: string; pythonRuntimeMode?: 'webworker' | 'main-thread'; picoTheme?: import('./picoThemes').PicoThemeName; categoryMetadata?: Record<string, CategoryMetadata>; categorySettings?: Record<string, CategoryRenderSettings> }) => Promise<ProjectMetadata | null>;
updateProjectMetadata: (updates: { name?: string; description?: string; dataPath?: string; publicUrl?: string; mainLanguage?: string; defaultAuthor?: string; maxPostsPerPage?: number; blogmarkCategory?: string; pythonRuntimeMode?: 'webworker' | 'main-thread'; picoTheme?: import('./picoThemes').PicoThemeName; categoryMetadata?: Record<string, CategoryMetadata>; categorySettings?: Record<string, CategoryRenderSettings>; semanticSimilarityEnabled?: boolean }) => Promise<ProjectMetadata | null>;
getPublishingPreferences: () => Promise<PublishingPreferences | null>;
setPublishingPreferences: (prefs: PublishingPreferences) => Promise<void>;
clearPublishingPreferences: () => Promise<void>;
@@ -986,6 +1005,17 @@ export interface ElectronAPI {
onA2UIMessage: (callback: (data: { conversationId: string; message: A2UIServerMessage }) => void) => () => void;
dispatchA2UIAction: (action: A2UIClientAction) => Promise<{ success: boolean; error?: string }>;
};
embeddings: {
findSimilar: (postId: string, k?: number) => Promise<SimilarPost[]>;
computeSimilarities: (sourcePostId: string, targetPostIds: string[]) => Promise<Record<string, number>>;
getProgress: () => Promise<{ indexed: number; total: number }>;
suggestTags: (postId: string, excludeTags: string[]) => Promise<TagSuggestion[]>;
findDuplicates: (threshold?: number) => Promise<DuplicatePair[]>;
runDuplicateSearch: (threshold?: number) => Promise<void>;
dismissPair: (postIdA: string, postIdB: string) => Promise<void>;
dismissPairs: (pairIds: Array<[string, string]>) => Promise<void>;
indexUnindexedPosts: () => Promise<void>;
};
on: (channel: string, callback: (...args: unknown[]) => void) => () => void;
once: (channel: string, callback: (...args: unknown[]) => void) => void;
/** Subscribe to entity-changed events fired by the CLI NotificationWatcher. */