Skip to content

Commit 37af89a

Browse files
authoredJan 8, 2025
Merge branch 'develop' into patch-1
2 parents 8a06988 + dce9f99 commit 37af89a

File tree

1 file changed

+242
-0
lines changed

1 file changed

+242
-0
lines changed
 

‎packages/adapter-pglite/src/index.ts

+242
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,247 @@ 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+
interface KnowledgeSearchRow {
1353+
id: UUID;
1354+
agentId: UUID;
1355+
content: string;
1356+
embedding: Buffer | null;
1357+
createdAt: string | number;
1358+
vector_score: number;
1359+
keyword_score: number;
1360+
combined_score: number;
1361+
}
1362+
try {
1363+
const cacheKey = `embedding_${params.agentId}_${params.searchText}`;
1364+
const cachedResult = await this.getCache({
1365+
key: cacheKey,
1366+
agentId: params.agentId,
1367+
});
1368+
1369+
if (cachedResult) {
1370+
return JSON.parse(cachedResult);
1371+
}
1372+
1373+
const vectorStr = `[${Array.from(params.embedding).join(",")}]`;
1374+
1375+
const sql = `
1376+
WITH vector_scores AS (
1377+
SELECT id,
1378+
1 - (embedding <-> $1::vector) as vector_score
1379+
FROM knowledge
1380+
WHERE ("agentId" IS NULL AND "isShared" = true) OR "agentId" = $2
1381+
AND embedding IS NOT NULL
1382+
),
1383+
keyword_matches AS (
1384+
SELECT id,
1385+
CASE
1386+
WHEN content->>'text' ILIKE $3 THEN 3.0
1387+
ELSE 1.0
1388+
END *
1389+
CASE
1390+
WHEN (content->'metadata'->>'isChunk')::boolean = true THEN 1.5
1391+
WHEN (content->'metadata'->>'isMain')::boolean = true THEN 1.2
1392+
ELSE 1.0
1393+
END as keyword_score
1394+
FROM knowledge
1395+
WHERE ("agentId" IS NULL AND "isShared" = true) OR "agentId" = $2
1396+
)
1397+
SELECT k.*,
1398+
v.vector_score,
1399+
kw.keyword_score,
1400+
(v.vector_score * kw.keyword_score) as combined_score
1401+
FROM knowledge k
1402+
JOIN vector_scores v ON k.id = v.id
1403+
LEFT JOIN keyword_matches kw ON k.id = kw.id
1404+
WHERE ("agentId" IS NULL AND "isShared" = true) OR k."agentId" = $2
1405+
AND (
1406+
v.vector_score >= $4
1407+
OR (kw.keyword_score > 1.0 AND v.vector_score >= 0.3)
1408+
)
1409+
ORDER BY combined_score DESC
1410+
LIMIT $5
1411+
`;
1412+
1413+
const { rows } = await this.query<KnowledgeSearchRow>(sql, [
1414+
vectorStr,
1415+
params.agentId,
1416+
`%${params.searchText || ""}%`,
1417+
params.match_threshold,
1418+
params.match_count,
1419+
]);
1420+
1421+
const results = rows.map((row) => ({
1422+
id: row.id,
1423+
agentId: row.agentId,
1424+
content:
1425+
typeof row.content === "string"
1426+
? JSON.parse(row.content)
1427+
: row.content,
1428+
embedding: row.embedding
1429+
? new Float32Array(row.embedding)
1430+
: undefined,
1431+
createdAt: row.createdAt
1432+
? new Date(row.createdAt).getTime()
1433+
: undefined,
1434+
similarity: row.combined_score,
1435+
}));
1436+
1437+
await this.setCache({
1438+
key: cacheKey,
1439+
agentId: params.agentId,
1440+
value: JSON.stringify(results),
1441+
});
1442+
1443+
return results;
1444+
} catch (error) {
1445+
elizaLogger.error("Error searching knowledge", {
1446+
error:
1447+
error instanceof Error ? error.message : String(error),
1448+
searchText: params.searchText,
1449+
agentId: params.agentId,
1450+
});
1451+
throw new Error(
1452+
`Failed to search knowledge: ${error instanceof Error ? error.message : String(error)}`
1453+
);
1454+
}
1455+
}, "searchKnowledge");
1456+
}
1457+
1458+
async createKnowledge(knowledge: RAGKnowledgeItem): Promise<void> {
1459+
return this.withTransaction(async (tx) => {
1460+
try {
1461+
const sql = `
1462+
INSERT INTO knowledge (
1463+
id, "agentId", content, embedding, "createdAt",
1464+
"isMain", "originalId", "chunkIndex", "isShared"
1465+
) VALUES ($1, $2, $3, $4, to_timestamp($5/1000.0), $6, $7, $8, $9)
1466+
ON CONFLICT (id) DO NOTHING
1467+
`;
1468+
1469+
const metadata = knowledge.content.metadata || {};
1470+
const vectorStr = knowledge.embedding
1471+
? `[${Array.from(knowledge.embedding).join(",")}]`
1472+
: null;
1473+
1474+
await tx.query(sql, [
1475+
knowledge.id,
1476+
metadata.isShared ? null : knowledge.agentId,
1477+
knowledge.content,
1478+
vectorStr,
1479+
knowledge.createdAt || Date.now(),
1480+
metadata.isMain || false,
1481+
metadata.originalId || null,
1482+
metadata.chunkIndex || null,
1483+
metadata.isShared || false,
1484+
]);
1485+
} catch (error) {
1486+
elizaLogger.error("Failed to create knowledge:", {
1487+
error:
1488+
error instanceof Error ? error.message : String(error),
1489+
});
1490+
throw error;
1491+
}
1492+
}, "createKnowledge");
1493+
}
1494+
1495+
async removeKnowledge(id: UUID): Promise<void> {
1496+
return await this.withTransaction(async (tx) => {
1497+
try {
1498+
await tx.query("DELETE FROM knowledge WHERE id = $1", [id]);
1499+
} catch (error) {
1500+
tx.rollback();
1501+
elizaLogger.error("Error removing knowledge", {
1502+
error:
1503+
error instanceof Error ? error.message : String(error),
1504+
id,
1505+
});
1506+
}
1507+
}, "removeKnowledge");
1508+
}
1509+
1510+
async clearKnowledge(agentId: UUID, shared?: boolean): Promise<void> {
1511+
return await this.withTransaction(async (tx) => {
1512+
try {
1513+
const sql = shared
1514+
? 'DELETE FROM knowledge WHERE ("agentId" = $1 OR "isShared" = true)'
1515+
: 'DELETE FROM knowledge WHERE "agentId" = $1';
1516+
await tx.query(sql, [agentId]);
1517+
} catch (error) {
1518+
tx.rollback();
1519+
elizaLogger.error("Error clearing knowledge", {
1520+
error:
1521+
error instanceof Error ? error.message : String(error),
1522+
agentId,
1523+
});
1524+
}
1525+
}, "clearKnowledge");
1526+
}
12851527
}
12861528

12871529
export default PGLiteDatabaseAdapter;

0 commit comments

Comments
 (0)