@@ -3,7 +3,6 @@ import { IAgentSaverService, ChatMessage, StoredMessage, ChatMessageOptions } fr
33import { ILoggerService , ILogger } from "../Logger" ;
44import { inject } from "scorpio.di" ;
55import { T_DBUrl , T_DBTable } from "../Core/tokens" ;
6- import { applyTokenLimit } from "./messageSerializer" ;
76
87// ─────────────────────────────────────────────────────────────────────────────
98// AgentPostgresSaver
@@ -17,6 +16,7 @@ export class AgentPostgresSaver implements IAgentSaverService {
1716
1817 private readonly table : string ;
1918 private get thinksTable ( ) { return `${ this . table } _thinks` ; }
19+ private get metadataTable ( ) { return `${ this . table } _metadata` ; }
2020
2121 constructor (
2222 @inject ( T_DBTable ) table : string ,
@@ -33,8 +33,6 @@ export class AgentPostgresSaver implements IAgentSaverService {
3333 return this . setupPromise ;
3434 }
3535
36- private get metadataTable ( ) { return `${ this . table } _metadata` ; }
37-
3836 private async initTables ( ) : Promise < void > {
3937 await this . pool . query ( `
4038 CREATE TABLE IF NOT EXISTS ${ this . table } (
@@ -57,6 +55,12 @@ export class AgentPostgresSaver implements IAgentSaverService {
5755 CREATE INDEX IF NOT EXISTS idx_${ this . table } _thinks_think_id
5856 ON ${ this . thinksTable } (think_id);
5957 ` ) ;
58+ try {
59+ await this . pool . query ( `ALTER TABLE ${ this . table } ADD COLUMN compacted INTEGER NOT NULL DEFAULT 0` ) ;
60+ } catch { /* column already exists */ }
61+ await this . pool . query (
62+ `CREATE INDEX IF NOT EXISTS idx_${ this . table } _fts ON ${ this . table } USING gin(to_tsvector('simple', data))`
63+ ) ;
6064 }
6165
6266 async pushMessage ( message : ChatMessage , options ?: ChatMessageOptions ) : Promise < void > {
@@ -66,9 +70,9 @@ export class AgentPostgresSaver implements IAgentSaverService {
6670 [ JSON . stringify ( message ) , Math . floor ( Date . now ( ) / 1000 ) , options ?. thinkId ?? null ]
6771 ) ;
6872 await this . pool . query ( `
69- DELETE FROM ${ this . table }
70- WHERE id < (
71- SELECT id FROM ${ this . table }
73+ UPDATE ${ this . table } SET compacted = 1
74+ WHERE compacted = 0 AND id < (
75+ SELECT id FROM ${ this . table } WHERE compacted = 0
7276 ORDER BY id DESC
7377 LIMIT 1 OFFSET 999
7478 )
@@ -79,9 +83,10 @@ export class AgentPostgresSaver implements IAgentSaverService {
7983 await this . ensureSetup ( ) ;
8084 try {
8185 const result = await this . pool . query (
82- `SELECT data, created_at, think_id FROM ${ this . table } ORDER BY id`
86+ `SELECT id, data, created_at, think_id FROM ${ this . table } WHERE compacted = 0 ORDER BY id`
8387 ) ;
84- return result . rows . map ( ( r : { data : string ; created_at : number ; think_id : string | null } ) => ( {
88+ return result . rows . map ( ( r : any ) => ( {
89+ id : parseInt ( r . id ) ,
8590 message : JSON . parse ( r . data ) as ChatMessage ,
8691 createdAt : r . created_at ,
8792 thinkId : r . think_id ?? undefined ,
@@ -92,15 +97,15 @@ export class AgentPostgresSaver implements IAgentSaverService {
9297 }
9398 }
9499
95- async getMessages ( maxTokens : number ) : Promise < ChatMessage [ ] > {
96- return applyTokenLimit ( ( await this . getAllMessages ( ) ) . map ( ( r ) => r . message ) , maxTokens ) ;
100+ async getMessages ( ) : Promise < ChatMessage [ ] > {
101+ return ( await this . getAllMessages ( ) ) . map ( ( r ) => r . message ) ;
97102 }
98103
99104 async replaceAllMessages ( messages : StoredMessage [ ] ) : Promise < void > {
100105 await this . ensureSetup ( ) ;
101106 await this . pool . query ( `BEGIN` ) ;
102107 try {
103- await this . pool . query ( `DELETE FROM ${ this . table } ` ) ;
108+ await this . pool . query ( `DELETE FROM ${ this . table } WHERE compacted = 0 ` ) ;
104109 await this . pool . query ( `DELETE FROM ${ this . thinksTable } ` ) ;
105110 for ( const stored of messages ) {
106111 await this . pool . query (
@@ -115,6 +120,38 @@ export class AgentPostgresSaver implements IAgentSaverService {
115120 }
116121 }
117122
123+ async markMessagesAsCompacted ( ids : number [ ] ) : Promise < void > {
124+ if ( ids . length === 0 ) return ;
125+ await this . ensureSetup ( ) ;
126+ const placeholders = ids . map ( ( _ , i ) => `$${ i + 1 } ` ) . join ( ',' ) ;
127+ await this . pool . query (
128+ `UPDATE ${ this . table } SET compacted = 1 WHERE id IN (${ placeholders } )` ,
129+ ids ,
130+ ) ;
131+ }
132+
133+ async searchMessages ( query : string , limit : number = 20 ) : Promise < StoredMessage [ ] > {
134+ await this . ensureSetup ( ) ;
135+ try {
136+ const result = await this . pool . query (
137+ `SELECT id, data, created_at, think_id FROM ${ this . table }
138+ WHERE to_tsvector('simple', data) @@ plainto_tsquery('simple', $1)
139+ ORDER BY ts_rank(to_tsvector('simple', data), plainto_tsquery('simple', $1)) DESC
140+ LIMIT $2` ,
141+ [ query , limit ] ,
142+ ) ;
143+ return result . rows . map ( ( r : any ) => ( {
144+ id : parseInt ( r . id ) ,
145+ message : JSON . parse ( r . data ) as ChatMessage ,
146+ createdAt : r . created_at ,
147+ thinkId : r . think_id ?? undefined ,
148+ } ) ) ;
149+ } catch ( error : any ) {
150+ this . logger ?. warn ( `Postgres FTS 搜索失败: ${ error . message } ` ) ;
151+ return [ ] ;
152+ }
153+ }
154+
118155 async clearMessages ( ) : Promise < void > {
119156 await this . ensureSetup ( ) ;
120157 await this . pool . query ( `DELETE FROM ${ this . table } ` ) ;
0 commit comments