Import source: app/backend/src/lib/embeddings
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import { prisma } from '../db.js';
|
||||||
|
|
||||||
|
// Simulated embedding generation for Phase 14 demo
|
||||||
|
// In production, this would call OpenAI text-embedding-3-small or similar
|
||||||
|
export async function generateEmbedding(text: string): Promise<number[]> {
|
||||||
|
// Deterministic-ish random for demo purposes based on text length
|
||||||
|
const vector = new Array(768).fill(0).map((_, i) => Math.sin(text.length * i));
|
||||||
|
// Normalize vector
|
||||||
|
const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
||||||
|
return vector.map(val => val / magnitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function storeArticleEmbedding(articleId: string, text: string) {
|
||||||
|
const vector = await generateEmbedding(text);
|
||||||
|
|
||||||
|
// Format for pgvector: '[0.1, 0.2, ...]'
|
||||||
|
const vectorString = `[${vector.join(',')}]`;
|
||||||
|
|
||||||
|
await prisma.$executeRaw`
|
||||||
|
INSERT INTO "ArticleEmbedding" ("id", "articleId", "embedding", "updatedAt")
|
||||||
|
VALUES (gen_random_uuid(), ${articleId}, ${vectorString}::vector, NOW())
|
||||||
|
ON CONFLICT ("articleId") DO UPDATE SET
|
||||||
|
"embedding" = EXCLUDED."embedding",
|
||||||
|
"updatedAt" = NOW();
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function semanticSearch(query: string, limit = 5) {
|
||||||
|
const queryVector = await generateEmbedding(query);
|
||||||
|
const vectorString = `[${queryVector.join(',')}]`;
|
||||||
|
|
||||||
|
// Cosine distance operator <=> in pgvector
|
||||||
|
const results = await prisma.$queryRaw<Array<{ id: string; articleId: string; distance: number; title: string; slug: string }>>`
|
||||||
|
SELECT
|
||||||
|
e."id",
|
||||||
|
e."articleId",
|
||||||
|
(e."embedding" <=> ${vectorString}::vector) as distance,
|
||||||
|
a."title",
|
||||||
|
a."slug"
|
||||||
|
FROM "ArticleEmbedding" e
|
||||||
|
JOIN "Article" a ON e."articleId" = a."id"
|
||||||
|
ORDER BY distance ASC
|
||||||
|
LIMIT ${limit};
|
||||||
|
`;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user