feat(db): extraction des embeddings + mode WAL + config DB provider-agnostic
- Ajout de la table de relation 1-1 NoteEmbedding pour alléger Model Note - Refactor complet des actions IA sémantique et Memory Echo pour utiliser la jointure - Migration propre des 85 embeddings locaux existants - Ajout PRAGMA journal_mode=WAL pour la concurrence au sein de lib/prisma - Ajout npm run db:switch pour configuration auto SQLite / PostgreSQL - Fix du compilateur Turbopack et Next-PWA
This commit is contained in:
@@ -349,8 +349,9 @@ async function semanticSearch(query: string, userId: string, notebookId?: string
|
||||
where: {
|
||||
userId: userId,
|
||||
isArchived: false,
|
||||
...(notebookId !== undefined ? { notebookId } : {}) // NEW: Filter by notebook (IA5)
|
||||
}
|
||||
...(notebookId !== undefined ? { notebookId } : {})
|
||||
},
|
||||
include: { noteEmbedding: true }
|
||||
});
|
||||
|
||||
const queryLower = query.toLowerCase().trim();
|
||||
@@ -380,8 +381,8 @@ async function semanticSearch(query: string, userId: string, notebookId?: string
|
||||
// Semantic match (if embedding available)
|
||||
let semanticMatch = false;
|
||||
let similarity = 0;
|
||||
if (queryEmbedding && note.embedding) {
|
||||
similarity = cosineSimilarity(queryEmbedding, JSON.parse(note.embedding));
|
||||
if (queryEmbedding && note.noteEmbedding?.embedding) {
|
||||
similarity = cosineSimilarity(queryEmbedding, JSON.parse(note.noteEmbedding.embedding));
|
||||
semanticMatch = similarity > 0.3; // 30% threshold - works well for related concepts
|
||||
}
|
||||
|
||||
@@ -450,7 +451,6 @@ export async function createNote(data: {
|
||||
reminder: data.reminder || null,
|
||||
isMarkdown: data.isMarkdown || false,
|
||||
size: data.size || 'small',
|
||||
embedding: null, // Generated in background
|
||||
autoGenerated: data.autoGenerated || null,
|
||||
notebookId: data.notebookId || null,
|
||||
}
|
||||
@@ -480,9 +480,10 @@ export async function createNote(data: {
|
||||
const provider = getAIProvider(await getSystemConfig())
|
||||
const embedding = await provider.getEmbeddings(content)
|
||||
if (embedding) {
|
||||
await prisma.note.update({
|
||||
where: { id: noteId },
|
||||
data: { embedding: JSON.stringify(embedding) }
|
||||
await prisma.noteEmbedding.upsert({
|
||||
where: { noteId: noteId },
|
||||
create: { noteId: noteId, embedding: JSON.stringify(embedding) },
|
||||
update: { embedding: JSON.stringify(embedding) }
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -579,9 +580,10 @@ export async function updateNote(id: string, data: {
|
||||
const provider = getAIProvider(await getSystemConfig());
|
||||
const embedding = await provider.getEmbeddings(content);
|
||||
if (embedding) {
|
||||
await prisma.note.update({
|
||||
where: { id: noteId },
|
||||
data: { embedding: JSON.stringify(embedding) }
|
||||
await prisma.noteEmbedding.upsert({
|
||||
where: { noteId: noteId },
|
||||
create: { noteId: noteId, embedding: JSON.stringify(embedding) },
|
||||
update: { embedding: JSON.stringify(embedding) }
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -863,14 +865,23 @@ export async function syncAllEmbeddings() {
|
||||
const userId = session.user.id;
|
||||
let updatedCount = 0;
|
||||
try {
|
||||
const notesToSync = await prisma.note.findMany({ where: { userId, embedding: null } })
|
||||
const notesToSync = await prisma.note.findMany({
|
||||
where: {
|
||||
userId,
|
||||
noteEmbedding: { is: null }
|
||||
}
|
||||
})
|
||||
const provider = getAIProvider(await getSystemConfig());
|
||||
for (const note of notesToSync) {
|
||||
if (!note.content) continue;
|
||||
try {
|
||||
const embedding = await provider.getEmbeddings(note.content);
|
||||
if (embedding) {
|
||||
await prisma.note.update({ where: { id: note.id }, data: { embedding: JSON.stringify(embedding) } })
|
||||
await prisma.noteEmbedding.upsert({
|
||||
where: { noteId: note.id },
|
||||
create: { noteId: note.id, embedding: JSON.stringify(embedding) },
|
||||
update: { embedding: JSON.stringify(embedding) }
|
||||
})
|
||||
updatedCount++;
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
@@ -29,7 +29,7 @@ export async function GET() {
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
embedding: true
|
||||
noteEmbedding: true
|
||||
}
|
||||
})
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function GET() {
|
||||
|
||||
for (const note of allNotes) {
|
||||
// Check if embedding is missing
|
||||
if (!note.embedding) {
|
||||
if (!note.noteEmbedding?.embedding) {
|
||||
missingCount++
|
||||
invalidNotes.push({
|
||||
id: note.id,
|
||||
@@ -57,8 +57,8 @@ export async function GET() {
|
||||
|
||||
// Validate embedding
|
||||
try {
|
||||
if (!note.embedding) continue
|
||||
const embedding = JSON.parse(note.embedding) as number[]
|
||||
if (!note.noteEmbedding?.embedding) continue
|
||||
const embedding = JSON.parse(note.noteEmbedding.embedding) as number[]
|
||||
const validation = validateEmbedding(embedding)
|
||||
|
||||
if (!validation.valid) {
|
||||
|
||||
Reference in New Issue
Block a user