feat: integrate vectordb for note embeddings
- Added `vectordb` as a dependency in `package.json`. - Implemented `embedText` function in `src/main/ai/embeddings.ts` to handle text embeddings using GitHub Copilot OAuth token or OpenAI token. - Created `vectordb.ts` for managing LanceDB connection and embedding notes with upsert strategy. - Updated `index.ts` to initialize vector database and migrate existing notes on app ready. - Modified `router/index.ts` to fire-and-forget embedding calls on note creation and updates. - Enhanced `progress.txt` with detailed implementation notes and learnings regarding the integration.
This commit is contained in:
@@ -7,6 +7,7 @@ import { clients, projects, tasks, checkpoints, notes, taskComments } from '../d
|
||||
import { getStore } from '../store';
|
||||
import { saveTokenAndInit, hasActiveToken } from '../ai/provider';
|
||||
import { orchestrate } from '../ai/orchestrator';
|
||||
import { upsertNoteEmbedding } from '../db/vectordb';
|
||||
import type { TRPCContext } from '../ipc';
|
||||
|
||||
const t = initTRPC.context<TRPCContext>().create();
|
||||
@@ -406,7 +407,7 @@ const notesRouter = router({
|
||||
|
||||
create: publicProcedure
|
||||
.input(z.object({ title: z.string(), content: z.string(), projectId: z.string().optional() }))
|
||||
.mutation(({ input }) => {
|
||||
.mutation(async ({ input }) => {
|
||||
const id = crypto.randomUUID();
|
||||
const now = Date.now();
|
||||
getDb().insert(notes).values({
|
||||
@@ -417,18 +418,37 @@ const notesRouter = router({
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}).run();
|
||||
// Fire-and-forget: embed the note. Errors are logged, never thrown.
|
||||
upsertNoteEmbedding(id, input.projectId ?? null, `${input.title}\n\n${input.content}`)
|
||||
.catch((err) => console.error('[VectorDB] Failed to embed note on create:', err));
|
||||
return { id };
|
||||
}),
|
||||
|
||||
update: publicProcedure
|
||||
.input(z.object({ id: z.string(), title: z.string().optional(), content: z.string().optional() }))
|
||||
.mutation(({ input }) => {
|
||||
.mutation(async ({ input }) => {
|
||||
const set: Partial<{ title: string; content: string; updatedAt: number }> = {};
|
||||
if (input.title !== undefined) set.title = input.title;
|
||||
if (input.content !== undefined) set.content = input.content;
|
||||
// Always update updatedAt
|
||||
set.updatedAt = Date.now();
|
||||
getDb().update(notes).set(set).where(eq(notes.id, input.id)).run();
|
||||
|
||||
// Re-embed if searchable text fields changed.
|
||||
// Re-fetch from SQLite so the embedding reflects the full current note
|
||||
// (the update may have changed only one of title or content).
|
||||
if (input.title !== undefined || input.content !== undefined) {
|
||||
const updated = getDb()
|
||||
.select({ id: notes.id, projectId: notes.projectId, title: notes.title, content: notes.content })
|
||||
.from(notes)
|
||||
.where(eq(notes.id, input.id))
|
||||
.all()[0];
|
||||
if (updated) {
|
||||
upsertNoteEmbedding(updated.id, updated.projectId ?? null, `${updated.title}\n\n${updated.content}`)
|
||||
.catch((err) => console.error('[VectorDB] Failed to embed note on update:', err));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user