fix: resolve Prisma 'vector' deserialization error and missing PDF canvas dependency
This commit is contained in:
@@ -111,7 +111,7 @@ export class MemoryEchoService {
|
||||
userId,
|
||||
isArchived: false,
|
||||
trashedAt: null,
|
||||
noteEmbedding: { isNot: null } // Only notes with embeddings
|
||||
// noteEmbedding: { isNot: null } // Removed because Unsupported type cannot be used in 'where' easily
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
@@ -127,11 +127,20 @@ export class MemoryEchoService {
|
||||
return [] // Need at least 2 notes to find connections
|
||||
}
|
||||
|
||||
if (notes.length < 2) return []
|
||||
|
||||
// Fetch embeddings separately using raw SQL to avoid deserialization error
|
||||
const noteIds = notes.map(n => n.id)
|
||||
const embeddings: Array<{ noteId: string, embedding: any }> = await prisma.$queryRawUnsafe(
|
||||
`SELECT "noteId", "embedding"::text FROM "NoteEmbedding" WHERE "noteId" IN (${noteIds.map(id => `'${id}'`).join(',')})`
|
||||
)
|
||||
const embeddingMap = new Map(embeddings.map(e => [e.noteId, e.embedding]))
|
||||
|
||||
const notesWithEmbeddings = notes
|
||||
.map(note => ({
|
||||
...note,
|
||||
embedding: note.noteEmbedding?.embedding
|
||||
? embeddingService.fromVectorString(note.noteEmbedding.embedding as unknown as string)
|
||||
embedding: embeddingMap.has(note.id)
|
||||
? embeddingService.fromVectorString(embeddingMap.get(note.id))
|
||||
: null
|
||||
}))
|
||||
.filter(note => note.embedding && Array.isArray(note.embedding))
|
||||
@@ -446,7 +455,6 @@ Explain in one brief sentence (max 15 words) why these notes are connected. Focu
|
||||
id: true,
|
||||
title: true,
|
||||
content: true,
|
||||
noteEmbedding: true,
|
||||
createdAt: true,
|
||||
userId: true
|
||||
}
|
||||
@@ -456,7 +464,14 @@ Explain in one brief sentence (max 15 words) why these notes are connected. Focu
|
||||
return [] // Note not found or doesn't belong to user
|
||||
}
|
||||
|
||||
if (!targetNote.noteEmbedding) {
|
||||
// Fetch embedding separately
|
||||
const embeddingResult: Array<{ embedding: any }> = await prisma.$queryRawUnsafe(
|
||||
`SELECT "embedding"::text FROM "NoteEmbedding" WHERE "noteId" = $1`,
|
||||
noteId
|
||||
)
|
||||
const targetEmbeddingStr = embeddingResult[0]?.embedding
|
||||
|
||||
if (!targetEmbeddingStr) {
|
||||
return [] // Note has no embedding
|
||||
}
|
||||
|
||||
@@ -490,13 +505,11 @@ Explain in one brief sentence (max 15 words) why these notes are connected. Focu
|
||||
id: { not: noteId },
|
||||
isArchived: false,
|
||||
trashedAt: null,
|
||||
noteEmbedding: { isNot: null }
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
content: true,
|
||||
noteEmbedding: true,
|
||||
createdAt: true
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
@@ -506,11 +519,18 @@ Explain in one brief sentence (max 15 words) why these notes are connected. Focu
|
||||
return []
|
||||
}
|
||||
|
||||
const targetEmbedding = targetNote.noteEmbedding?.embedding
|
||||
? embeddingService.fromVectorString(targetNote.noteEmbedding.embedding as unknown as string)
|
||||
const targetEmbedding = targetEmbeddingStr
|
||||
? embeddingService.fromVectorString(targetEmbeddingStr)
|
||||
: null
|
||||
if (!targetEmbedding) return []
|
||||
|
||||
// Fetch all other embeddings
|
||||
const otherNoteIds = otherNotes.map(n => n.id)
|
||||
const otherEmbeddings: Array<{ noteId: string, embedding: any }> = await prisma.$queryRawUnsafe(
|
||||
`SELECT "noteId", "embedding"::text FROM "NoteEmbedding" WHERE "noteId" IN (${otherNoteIds.map(id => `'${id}'`).join(',')})`
|
||||
)
|
||||
const otherEmbeddingMap = new Map(otherEmbeddings.map(e => [e.noteId, e.embedding]))
|
||||
|
||||
// Check if user has demo mode enabled
|
||||
const settings = await prisma.userAISettings.findUnique({
|
||||
where: { userId }
|
||||
@@ -540,11 +560,10 @@ Explain in one brief sentence (max 15 words) why these notes are connected. Focu
|
||||
|
||||
// Compare target note with all other notes
|
||||
for (const otherNote of otherNotes) {
|
||||
if (!otherNote.noteEmbedding) continue
|
||||
const otherEmbeddingStr = otherEmbeddingMap.get(otherNote.id)
|
||||
if (!otherEmbeddingStr) continue
|
||||
|
||||
const otherEmbedding = otherNote.noteEmbedding?.embedding
|
||||
? embeddingService.fromVectorString(otherNote.noteEmbedding.embedding as unknown as string)
|
||||
: null
|
||||
const otherEmbedding = embeddingService.fromVectorString(otherEmbeddingStr)
|
||||
if (!otherEmbedding) continue
|
||||
|
||||
// Check if this connection was dismissed
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tinyld": "^1.3.4",
|
||||
"vazirmatn": "^33.0.3",
|
||||
"zod": "^4.3.5"
|
||||
"zod": "^4.3.5",
|
||||
"@napi-rs/canvas": "^0.1.65"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.57.0",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["debian-openssl-3.0.x", "native"]
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["debian-openssl-3.0.x", "native", "postgresqlExtensions"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
extensions = [pgvector]
|
||||
}
|
||||
|
||||
model User {
|
||||
@@ -315,8 +316,8 @@ model UserAISettings {
|
||||
|
||||
model NoteEmbedding {
|
||||
id String @id @default(cuid())
|
||||
noteId String @unique
|
||||
embedding String
|
||||
noteId String @unique
|
||||
embedding Unsupported("vector(1536)")?
|
||||
createdAt DateTime @default(now())
|
||||
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user