Skip to content

Commit d2c49d9

Browse files
committed
feat: implement getKnowledge, searchKnowledge, createKnowledge, removeKnowledge and clearKnowledge methods
1 parent b319ff1 commit d2c49d9

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed

packages/adapter-pglite/src/index.ts

+232
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getEmbeddingConfig,
1515
DatabaseAdapter,
1616
EmbeddingProvider,
17+
RAGKnowledgeItem,
1718
} from "@elizaos/core";
1819
import fs from "fs";
1920
import { fileURLToPath } from "url";
@@ -1282,6 +1283,237 @@ export class PGLiteDatabaseAdapter
12821283
}, "deleteCache")) ?? false
12831284
);
12841285
}
1286+
1287+
async getKnowledge(params: {
1288+
id?: UUID;
1289+
agentId: UUID;
1290+
limit?: number;
1291+
query?: string;
1292+
}): Promise<RAGKnowledgeItem[]> {
1293+
return this.withDatabase(async () => {
1294+
try {
1295+
let sql = `SELECT * FROM knowledge WHERE ("agentId" = $1 OR "isShared" = true)`;
1296+
const queryParams: any[] = [params.agentId];
1297+
let paramCount = 1;
1298+
1299+
if (params.id) {
1300+
paramCount++;
1301+
sql += ` AND id = $${paramCount}`;
1302+
queryParams.push(params.id);
1303+
}
1304+
1305+
if (params.limit) {
1306+
paramCount++;
1307+
sql += ` LIMIT $${paramCount}`;
1308+
queryParams.push(params.limit);
1309+
}
1310+
1311+
const { rows } = await this.query<RAGKnowledgeItem>(
1312+
sql,
1313+
queryParams
1314+
);
1315+
1316+
return rows.map((row) => ({
1317+
id: row.id,
1318+
agentId: row.agentId,
1319+
content:
1320+
typeof row.content === "string"
1321+
? JSON.parse(row.content)
1322+
: row.content,
1323+
embedding: row.embedding
1324+
? new Float32Array(row.embedding)
1325+
: undefined,
1326+
createdAt: row.createdAt
1327+
? new Date(row.createdAt).getTime()
1328+
: undefined,
1329+
}));
1330+
} catch (error) {
1331+
elizaLogger.error("Error getting knowledge", {
1332+
error:
1333+
error instanceof Error ? error.message : String(error),
1334+
id: params.id,
1335+
agentId: params.agentId,
1336+
});
1337+
throw new Error(
1338+
`Failed to getting knowledge: ${error instanceof Error ? error.message : String(error)}`
1339+
);
1340+
}
1341+
}, "getKnowledge");
1342+
}
1343+
1344+
async searchKnowledge(params: {
1345+
agentId: UUID;
1346+
embedding: Float32Array;
1347+
match_threshold: number;
1348+
match_count: number;
1349+
searchText?: string;
1350+
}): Promise<RAGKnowledgeItem[]> {
1351+
return this.withDatabase(async () => {
1352+
try {
1353+
const cacheKey = `embedding_${params.agentId}_${params.searchText}`;
1354+
const cachedResult = await this.getCache({
1355+
key: cacheKey,
1356+
agentId: params.agentId,
1357+
});
1358+
1359+
if (cachedResult) {
1360+
return JSON.parse(cachedResult);
1361+
}
1362+
1363+
const vectorStr = `[${Array.from(params.embedding).join(",")}]`;
1364+
1365+
const sql = `
1366+
WITH vector_scores AS (
1367+
SELECT id,
1368+
1 - (embedding <-> $1::vector) as vector_score
1369+
FROM knowledge
1370+
WHERE ("agentId" IS NULL AND "isShared" = true) OR "agentId" = $2
1371+
AND embedding IS NOT NULL
1372+
),
1373+
keyword_matches AS (
1374+
SELECT id,
1375+
CASE
1376+
WHEN content->>'text' ILIKE $3 THEN 3.0
1377+
ELSE 1.0
1378+
END *
1379+
CASE
1380+
WHEN (content->'metadata'->>'isChunk')::boolean = true THEN 1.5
1381+
WHEN (content->'metadata'->>'isMain')::boolean = true THEN 1.2
1382+
ELSE 1.0
1383+
END as keyword_score
1384+
FROM knowledge
1385+
WHERE ("agentId" IS NULL AND "isShared" = true) OR "agentId" = $2
1386+
)
1387+
SELECT k.*,
1388+
v.vector_score,
1389+
kw.keyword_score,
1390+
(v.vector_score * kw.keyword_score) as combined_score
1391+
FROM knowledge k
1392+
JOIN vector_scores v ON k.id = v.id
1393+
LEFT JOIN keyword_matches kw ON k.id = kw.id
1394+
WHERE ("agentId" IS NULL AND "isShared" = true) OR k."agentId" = $2
1395+
AND (
1396+
v.vector_score >= $4
1397+
OR (kw.keyword_score > 1.0 AND v.vector_score >= 0.3)
1398+
)
1399+
ORDER BY combined_score DESC
1400+
LIMIT $5
1401+
`;
1402+
1403+
const { rows } = await this.query<RAGKnowledgeItem>(sql, [
1404+
vectorStr,
1405+
params.agentId,
1406+
`%${params.searchText || ""}%`,
1407+
params.match_threshold,
1408+
params.match_count,
1409+
]);
1410+
1411+
const results = rows.map((row) => ({
1412+
id: row.id,
1413+
agentId: row.agentId,
1414+
content:
1415+
typeof row.content === "string"
1416+
? JSON.parse(row.content)
1417+
: row.content,
1418+
embedding: row.embedding
1419+
? new Float32Array(row.embedding)
1420+
: undefined,
1421+
createdAt: row.createdAt
1422+
? new Date(row.createdAt).getTime()
1423+
: undefined,
1424+
similarity: row.combined_score,
1425+
}));
1426+
1427+
await this.setCache({
1428+
key: cacheKey,
1429+
agentId: params.agentId,
1430+
value: JSON.stringify(results),
1431+
});
1432+
1433+
return results;
1434+
} catch (error) {
1435+
elizaLogger.error("Error searching knowledge", {
1436+
error:
1437+
error instanceof Error ? error.message : String(error),
1438+
searchText: params.searchText,
1439+
agentId: params.agentId,
1440+
});
1441+
throw new Error(
1442+
`Failed to search knowledge: ${error instanceof Error ? error.message : String(error)}`
1443+
);
1444+
}
1445+
}, "searchKnowledge");
1446+
}
1447+
1448+
async createKnowledge(knowledge: RAGKnowledgeItem): Promise<void> {
1449+
return this.withTransaction(async (tx) => {
1450+
try {
1451+
const sql = `
1452+
INSERT INTO knowledge (
1453+
id, "agentId", content, embedding, "createdAt",
1454+
"isMain", "originalId", "chunkIndex", "isShared"
1455+
) VALUES ($1, $2, $3, $4, to_timestamp($5/1000.0), $6, $7, $8, $9)
1456+
ON CONFLICT (id) DO NOTHING
1457+
`;
1458+
1459+
const metadata = knowledge.content.metadata || {};
1460+
const vectorStr = knowledge.embedding
1461+
? `[${Array.from(knowledge.embedding).join(",")}]`
1462+
: null;
1463+
1464+
await tx.query(sql, [
1465+
knowledge.id,
1466+
metadata.isShared ? null : knowledge.agentId,
1467+
knowledge.content,
1468+
vectorStr,
1469+
knowledge.createdAt || Date.now(),
1470+
metadata.isMain || false,
1471+
metadata.originalId || null,
1472+
metadata.chunkIndex || null,
1473+
metadata.isShared || false,
1474+
]);
1475+
} catch (error) {
1476+
elizaLogger.error("Failed to create knowledge:", {
1477+
error:
1478+
error instanceof Error ? error.message : String(error),
1479+
});
1480+
throw error;
1481+
}
1482+
}, "createKnowledge");
1483+
}
1484+
1485+
async removeKnowledge(id: UUID): Promise<void> {
1486+
return await this.withTransaction(async (tx) => {
1487+
try {
1488+
await tx.query("DELETE FROM knowledge WHERE id = $1", [id]);
1489+
} catch (error) {
1490+
tx.rollback();
1491+
elizaLogger.error("Error removing knowledge", {
1492+
error:
1493+
error instanceof Error ? error.message : String(error),
1494+
id,
1495+
});
1496+
}
1497+
}, "removeKnowledge");
1498+
}
1499+
1500+
async clearKnowledge(agentId: UUID, shared?: boolean): Promise<void> {
1501+
return await this.withTransaction(async (tx) => {
1502+
try {
1503+
const sql = shared
1504+
? 'DELETE FROM knowledge WHERE ("agentId" = $1 OR "isShared" = true)'
1505+
: 'DELETE FROM knowledge WHERE "agentId" = $1';
1506+
await tx.query(sql, [agentId]);
1507+
} catch (error) {
1508+
tx.rollback();
1509+
elizaLogger.error("Error clearing knowledge", {
1510+
error:
1511+
error instanceof Error ? error.message : String(error),
1512+
agentId,
1513+
});
1514+
}
1515+
}, "clearKnowledge");
1516+
}
12851517
}
12861518

12871519
export default PGLiteDatabaseAdapter;

0 commit comments

Comments
 (0)