@@ -4,31 +4,31 @@ import { v4 } from "uuid";
4
4
import pg from "pg" ;
5
5
type Pool = pg . Pool ;
6
6
7
- import {
8
- QueryConfig ,
9
- QueryConfigValues ,
10
- QueryResult ,
11
- QueryResultRow ,
12
- } from "pg" ;
13
7
import {
14
8
Account ,
15
9
Actor ,
10
+ DatabaseAdapter ,
11
+ EmbeddingProvider ,
16
12
GoalStatus ,
13
+ Participant ,
14
+ RAGKnowledgeItem ,
15
+ elizaLogger ,
16
+ getEmbeddingConfig ,
17
17
type Goal ,
18
+ type IDatabaseCacheAdapter ,
18
19
type Memory ,
19
20
type Relationship ,
20
21
type UUID ,
21
- type IDatabaseCacheAdapter ,
22
- Participant ,
23
- elizaLogger ,
24
- getEmbeddingConfig ,
25
- DatabaseAdapter ,
26
- EmbeddingProvider ,
27
- RAGKnowledgeItem
28
22
} from "@elizaos/core" ;
29
23
import fs from "fs" ;
30
- import { fileURLToPath } from "url" ;
31
24
import path from "path" ;
25
+ import {
26
+ QueryConfig ,
27
+ QueryConfigValues ,
28
+ QueryResult ,
29
+ QueryResultRow ,
30
+ } from "pg" ;
31
+ import { fileURLToPath } from "url" ;
32
32
33
33
const __filename = fileURLToPath ( import . meta. url ) ; // get the resolved path to the file
34
34
const __dirname = path . dirname ( __filename ) ; // get the name of the directory
@@ -199,7 +199,7 @@ export class PostgresDatabaseAdapter
199
199
return true ;
200
200
} catch ( error ) {
201
201
elizaLogger . error ( "Failed to validate vector extension:" , {
202
- error : error instanceof Error ? error . message : String ( error )
202
+ error : error instanceof Error ? error . message : String ( error ) ,
203
203
} ) ;
204
204
return false ;
205
205
}
@@ -239,8 +239,10 @@ export class PostgresDatabaseAdapter
239
239
);
240
240
` ) ;
241
241
242
- if ( ! rows [ 0 ] . exists || ! await this . validateVectorSetup ( ) ) {
243
- elizaLogger . info ( "Applying database schema - tables or vector extension missing" ) ;
242
+ if ( ! rows [ 0 ] . exists || ! ( await this . validateVectorSetup ( ) ) ) {
243
+ elizaLogger . info (
244
+ "Applying database schema - tables or vector extension missing"
245
+ ) ;
244
246
const schema = fs . readFileSync (
245
247
path . resolve ( __dirname , "../schema.sql" ) ,
246
248
"utf8"
@@ -1523,12 +1525,17 @@ export class PostgresDatabaseAdapter
1523
1525
1524
1526
const { rows } = await this . pool . query ( sql , queryParams ) ;
1525
1527
1526
- return rows . map ( row => ( {
1528
+ return rows . map ( ( row ) => ( {
1527
1529
id : row . id ,
1528
1530
agentId : row . agentId ,
1529
- content : typeof row . content === 'string' ? JSON . parse ( row . content ) : row . content ,
1530
- embedding : row . embedding ? new Float32Array ( row . embedding ) : undefined ,
1531
- createdAt : row . createdAt . getTime ( )
1531
+ content :
1532
+ typeof row . content === "string"
1533
+ ? JSON . parse ( row . content )
1534
+ : row . content ,
1535
+ embedding : row . embedding
1536
+ ? new Float32Array ( row . embedding )
1537
+ : undefined ,
1538
+ createdAt : row . createdAt . getTime ( ) ,
1532
1539
} ) ) ;
1533
1540
} , "getKnowledge" ) ;
1534
1541
}
@@ -1544,7 +1551,7 @@ export class PostgresDatabaseAdapter
1544
1551
const cacheKey = `embedding_${ params . agentId } _${ params . searchText } ` ;
1545
1552
const cachedResult = await this . getCache ( {
1546
1553
key : cacheKey ,
1547
- agentId : params . agentId
1554
+ agentId : params . agentId ,
1548
1555
} ) ;
1549
1556
1550
1557
if ( cachedResult ) {
@@ -1594,24 +1601,29 @@ export class PostgresDatabaseAdapter
1594
1601
const { rows } = await this . pool . query ( sql , [
1595
1602
vectorStr ,
1596
1603
params . agentId ,
1597
- `%${ params . searchText || '' } %` ,
1604
+ `%${ params . searchText || "" } %` ,
1598
1605
params . match_threshold ,
1599
- params . match_count
1606
+ params . match_count ,
1600
1607
] ) ;
1601
1608
1602
- const results = rows . map ( row => ( {
1609
+ const results = rows . map ( ( row ) => ( {
1603
1610
id : row . id ,
1604
1611
agentId : row . agentId ,
1605
- content : typeof row . content === 'string' ? JSON . parse ( row . content ) : row . content ,
1606
- embedding : row . embedding ? new Float32Array ( row . embedding ) : undefined ,
1612
+ content :
1613
+ typeof row . content === "string"
1614
+ ? JSON . parse ( row . content )
1615
+ : row . content ,
1616
+ embedding : row . embedding
1617
+ ? new Float32Array ( row . embedding )
1618
+ : undefined ,
1607
1619
createdAt : row . createdAt . getTime ( ) ,
1608
- similarity : row . combined_score
1620
+ similarity : row . combined_score ,
1609
1621
} ) ) ;
1610
1622
1611
1623
await this . setCache ( {
1612
1624
key : cacheKey ,
1613
1625
agentId : params . agentId ,
1614
- value : JSON . stringify ( results )
1626
+ value : JSON . stringify ( results ) ,
1615
1627
} ) ;
1616
1628
1617
1629
return results ;
@@ -1622,35 +1634,52 @@ export class PostgresDatabaseAdapter
1622
1634
return this . withDatabase ( async ( ) => {
1623
1635
const client = await this . pool . connect ( ) ;
1624
1636
try {
1625
- await client . query ( 'BEGIN' ) ;
1626
-
1627
- const sql = `
1628
- INSERT INTO knowledge (
1629
- id, "agentId", content, embedding, "createdAt",
1630
- "isMain", "originalId", "chunkIndex", "isShared"
1631
- ) VALUES ($1, $2, $3, $4, to_timestamp($5/1000.0), $6, $7, $8, $9)
1632
- ON CONFLICT (id) DO NOTHING
1633
- ` ;
1637
+ await client . query ( "BEGIN" ) ;
1634
1638
1635
1639
const metadata = knowledge . content . metadata || { } ;
1636
- const vectorStr = knowledge . embedding ?
1637
- `[${ Array . from ( knowledge . embedding ) . join ( "," ) } ]` : null ;
1638
-
1639
- await client . query ( sql , [
1640
- knowledge . id ,
1641
- metadata . isShared ? null : knowledge . agentId ,
1642
- knowledge . content ,
1643
- vectorStr ,
1644
- knowledge . createdAt || Date . now ( ) ,
1645
- metadata . isMain || false ,
1646
- metadata . originalId || null ,
1647
- metadata . chunkIndex || null ,
1648
- metadata . isShared || false
1649
- ] ) ;
1640
+ const vectorStr = knowledge . embedding
1641
+ ? `[${ Array . from ( knowledge . embedding ) . join ( "," ) } ]`
1642
+ : null ;
1643
+
1644
+ // If this is a chunk, use createKnowledgeChunk
1645
+ if ( metadata . isChunk && metadata . originalId ) {
1646
+ await this . createKnowledgeChunk ( {
1647
+ id : knowledge . id ,
1648
+ originalId : metadata . originalId ,
1649
+ agentId : metadata . isShared ? null : knowledge . agentId ,
1650
+ content : knowledge . content ,
1651
+ embedding : knowledge . embedding ,
1652
+ chunkIndex : metadata . chunkIndex || 0 ,
1653
+ isShared : metadata . isShared || false ,
1654
+ createdAt : knowledge . createdAt || Date . now ( ) ,
1655
+ } ) ;
1656
+ } else {
1657
+ // This is a main knowledge item
1658
+ await client . query (
1659
+ `
1660
+ INSERT INTO knowledge (
1661
+ id, "agentId", content, embedding, "createdAt",
1662
+ "isMain", "originalId", "chunkIndex", "isShared"
1663
+ ) VALUES ($1, $2, $3, $4, to_timestamp($5/1000.0), $6, $7, $8, $9)
1664
+ ON CONFLICT (id) DO NOTHING
1665
+ ` ,
1666
+ [
1667
+ knowledge . id ,
1668
+ metadata . isShared ? null : knowledge . agentId ,
1669
+ knowledge . content ,
1670
+ vectorStr ,
1671
+ knowledge . createdAt || Date . now ( ) ,
1672
+ true ,
1673
+ null ,
1674
+ null ,
1675
+ metadata . isShared || false ,
1676
+ ]
1677
+ ) ;
1678
+ }
1650
1679
1651
- await client . query ( ' COMMIT' ) ;
1680
+ await client . query ( " COMMIT" ) ;
1652
1681
} catch ( error ) {
1653
- await client . query ( ' ROLLBACK' ) ;
1682
+ await client . query ( " ROLLBACK" ) ;
1654
1683
throw error ;
1655
1684
} finally {
1656
1685
client . release ( ) ;
@@ -1660,19 +1689,100 @@ export class PostgresDatabaseAdapter
1660
1689
1661
1690
async removeKnowledge ( id : UUID ) : Promise < void > {
1662
1691
return this . withDatabase ( async ( ) => {
1663
- await this . pool . query ( 'DELETE FROM knowledge WHERE id = $1' , [ id ] ) ;
1692
+ const client = await this . pool . connect ( ) ;
1693
+ try {
1694
+ await client . query ( "BEGIN" ) ;
1695
+
1696
+ // Check if this is a pattern-based chunk deletion (e.g., "id-chunk-*")
1697
+ if ( typeof id === "string" && id . includes ( "-chunk-*" ) ) {
1698
+ const mainId = id . split ( "-chunk-" ) [ 0 ] ;
1699
+ // Delete chunks for this main ID
1700
+ await client . query (
1701
+ 'DELETE FROM knowledge WHERE "originalId" = $1' ,
1702
+ [ mainId ]
1703
+ ) ;
1704
+ } else {
1705
+ // First delete all chunks associated with this knowledge item
1706
+ await client . query (
1707
+ 'DELETE FROM knowledge WHERE "originalId" = $1' ,
1708
+ [ id ]
1709
+ ) ;
1710
+ // Then delete the main knowledge item
1711
+ await client . query ( "DELETE FROM knowledge WHERE id = $1" , [
1712
+ id ,
1713
+ ] ) ;
1714
+ }
1715
+
1716
+ await client . query ( "COMMIT" ) ;
1717
+ } catch ( error ) {
1718
+ await client . query ( "ROLLBACK" ) ;
1719
+ elizaLogger . error ( "Error removing knowledge" , {
1720
+ error :
1721
+ error instanceof Error ? error . message : String ( error ) ,
1722
+ id,
1723
+ } ) ;
1724
+ throw error ;
1725
+ } finally {
1726
+ client . release ( ) ;
1727
+ }
1664
1728
} , "removeKnowledge" ) ;
1665
1729
}
1666
1730
1667
1731
async clearKnowledge ( agentId : UUID , shared ?: boolean ) : Promise < void > {
1668
1732
return this . withDatabase ( async ( ) => {
1669
- const sql = shared ?
1670
- 'DELETE FROM knowledge WHERE ("agentId" = $1 OR "isShared" = true)' :
1671
- 'DELETE FROM knowledge WHERE "agentId" = $1' ;
1733
+ const sql = shared
1734
+ ? 'DELETE FROM knowledge WHERE ("agentId" = $1 OR "isShared" = true)'
1735
+ : 'DELETE FROM knowledge WHERE "agentId" = $1' ;
1672
1736
1673
1737
await this . pool . query ( sql , [ agentId ] ) ;
1674
1738
} , "clearKnowledge" ) ;
1675
1739
}
1740
+
1741
+ private async createKnowledgeChunk ( params : {
1742
+ id : UUID ;
1743
+ originalId : UUID ;
1744
+ agentId : UUID | null ;
1745
+ content : any ;
1746
+ embedding : Float32Array | undefined | null ;
1747
+ chunkIndex : number ;
1748
+ isShared : boolean ;
1749
+ createdAt : number ;
1750
+ } ) : Promise < void > {
1751
+ const vectorStr = params . embedding
1752
+ ? `[${ Array . from ( params . embedding ) . join ( "," ) } ]`
1753
+ : null ;
1754
+
1755
+ // Store the pattern-based ID in the content metadata for compatibility
1756
+ const patternId = `${ params . originalId } -chunk-${ params . chunkIndex } ` ;
1757
+ const contentWithPatternId = {
1758
+ ...params . content ,
1759
+ metadata : {
1760
+ ...params . content . metadata ,
1761
+ patternId,
1762
+ } ,
1763
+ } ;
1764
+
1765
+ await this . pool . query (
1766
+ `
1767
+ INSERT INTO knowledge (
1768
+ id, "agentId", content, embedding, "createdAt",
1769
+ "isMain", "originalId", "chunkIndex", "isShared"
1770
+ ) VALUES ($1, $2, $3, $4, to_timestamp($5/1000.0), $6, $7, $8, $9)
1771
+ ON CONFLICT (id) DO NOTHING
1772
+ ` ,
1773
+ [
1774
+ v4 ( ) , // Generate a proper UUID for PostgreSQL
1775
+ params . agentId ,
1776
+ contentWithPatternId , // Store the pattern ID in metadata
1777
+ vectorStr ,
1778
+ params . createdAt ,
1779
+ false ,
1780
+ params . originalId ,
1781
+ params . chunkIndex ,
1782
+ params . isShared ,
1783
+ ]
1784
+ ) ;
1785
+ }
1676
1786
}
1677
1787
1678
1788
export default PostgresDatabaseAdapter ;
0 commit comments