@@ -14,6 +14,7 @@ import {
14
14
getEmbeddingConfig ,
15
15
DatabaseAdapter ,
16
16
EmbeddingProvider ,
17
+ RAGKnowledgeItem ,
17
18
} from "@elizaos/core" ;
18
19
import fs from "fs" ;
19
20
import { fileURLToPath } from "url" ;
@@ -1282,6 +1283,247 @@ export class PGLiteDatabaseAdapter
1282
1283
} , "deleteCache" ) ) ?? false
1283
1284
) ;
1284
1285
}
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
+ }
1285
1527
}
1286
1528
1287
1529
export default PGLiteDatabaseAdapter ;
0 commit comments