fix: remove pgvector dependency from AI knowledge retrieval

This commit is contained in:
2026-07-06 10:36:21 -07:00
parent 0d5093ac6c
commit f74b7f3ca0
9 changed files with 78 additions and 30 deletions

View File

@@ -1,7 +1,7 @@
import "server-only";
import { OpenAIEmbeddings } from "@langchain/openai";
import { and, desc, eq, isNull, or, sql } from "drizzle-orm";
import { and, desc, eq, isNull, or } from "drizzle-orm";
import { getAiRuntimeConfig } from "@/modules/ai/server/config";
import type {
@@ -316,6 +316,29 @@ export async function deleteKnowledgeEntry(context: AuthContext, id: string): Pr
return { success: true, data: { entry: toKnowledgeEntry(row) } };
}
function calculateCosineSimilarity(left: readonly number[], right: readonly number[]): number {
if (left.length === 0 || right.length === 0 || left.length !== right.length) {
return 0;
}
let dotProduct = 0;
let leftMagnitude = 0;
let rightMagnitude = 0;
for (let index = 0; index < left.length; index += 1) {
const leftValue = left[index] ?? 0;
const rightValue = right[index] ?? 0;
dotProduct += leftValue * rightValue;
leftMagnitude += leftValue * leftValue;
rightMagnitude += rightValue * rightValue;
}
if (leftMagnitude === 0 || rightMagnitude === 0) {
return 0;
}
return dotProduct / (Math.sqrt(leftMagnitude) * Math.sqrt(rightMagnitude));
}
export async function retrieveKnowledge(
organizationId: string,
query: string,
@@ -333,7 +356,6 @@ export async function retrieveKnowledge(
}
const database = getDatabase();
const distance = sql<number>`${aiKnowledgeChunks.embedding} <=> ${JSON.stringify(embedding)}::vector`;
const rows = await database
.select({
entryId: aiKnowledgeChunks.entryId,
@@ -341,25 +363,26 @@ export async function retrieveKnowledge(
title: aiKnowledgeChunks.sourceTitle,
category: aiKnowledgeChunks.sourceCategory,
content: aiKnowledgeChunks.content,
distance,
embedding: aiKnowledgeChunks.embedding,
})
.from(aiKnowledgeChunks)
.innerJoin(aiKnowledgeEntries, eq(aiKnowledgeEntries.id, aiKnowledgeChunks.entryId))
.where(getEnabledChunkWhere(organizationId))
.orderBy(distance)
.limit(options?.limit ?? DEFAULT_RETRIEVAL_LIMIT);
.where(getEnabledChunkWhere(organizationId));
return {
success: true,
data: {
results: rows.map((row) => ({
entryId: row.entryId,
chunkId: row.chunkId,
title: row.title,
category: row.category,
content: row.content,
score: 1 - row.distance,
})),
results: rows
.map((row) => ({
entryId: row.entryId,
chunkId: row.chunkId,
title: row.title,
category: row.category,
content: row.content,
score: calculateCosineSimilarity(row.embedding, embedding),
}))
.sort((left, right) => right.score - left.score)
.slice(0, options?.limit ?? DEFAULT_RETRIEVAL_LIMIT),
},
};
}