@ppagent/memory 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -27,7 +27,7 @@ interface Paginated<T> {
27
27
  * 多模态消息的单个内容块,与 OpenAI content part 对齐
28
28
  */
29
29
  interface ContentPart {
30
- type: "text" | "image_url" | "file_url";
30
+ type: string;
31
31
  /** type 为 "text" 时的文本内容 */
32
32
  text?: string;
33
33
  /** type 为 "image_url" 时的图片信息 */
@@ -39,6 +39,8 @@ interface ContentPart {
39
39
  url: string;
40
40
  name?: string;
41
41
  };
42
+ /** 保留宿主框架自定义 content part,写入和回读时不丢字段。 */
43
+ [key: string]: unknown;
42
44
  }
43
45
  interface RawMessage {
44
46
  messageId?: string;
@@ -57,6 +59,8 @@ interface RawMessage {
57
59
  * 序列化后存入 LanceDB 的 parts 字段。
58
60
  */
59
61
  parts?: ContentPart[];
62
+ /** 宿主框架的原始消息载荷;记忆框架仅透明保存和回读。 */
63
+ payload?: unknown;
60
64
  usage?: number;
61
65
  metadata?: Record<string, unknown>;
62
66
  createdAt?: number;
@@ -72,6 +76,8 @@ interface StoredMessage {
72
76
  content: string;
73
77
  /** JSON.stringify(ContentPart[]),纯文本消息为 "[]" */
74
78
  parts: string;
79
+ /** JSON.stringify(payload),未提供时为 undefined。 */
80
+ payload?: string;
75
81
  usage: number;
76
82
  metadata: string;
77
83
  vector: number[];
@@ -84,9 +90,17 @@ interface Topic {
84
90
  chatId: string;
85
91
  /** 压缩时自动生成的主题标题(一句话),存量数据可能为空串 */
86
92
  title: string;
87
- detail: string;
93
+ /** 唯一的压缩正文。长度由内容重要性动态决定。 */
88
94
  summary: string;
89
- concise: string;
95
+ /** 压缩结果的 token 数;旧数据缺失时由本地 tokenizer 回填。 */
96
+ tokens: number;
97
+ /** 本 Topic 覆盖的首尾原始消息 id,便于同毫秒边界精确恢复。 */
98
+ startMessageId?: string;
99
+ endMessageId?: string;
100
+ /** @deprecated 仅用于读取尚未迁移的 0.3.x 数据。 */
101
+ detail?: string;
102
+ /** @deprecated 仅用于读取尚未迁移的 0.3.x 数据。 */
103
+ concise?: string;
90
104
  startTime: number;
91
105
  endTime: number;
92
106
  createdAt: number;
@@ -100,8 +114,12 @@ interface Fact {
100
114
  chatId: string;
101
115
  sessionId: string;
102
116
  userId: string;
117
+ /** 同一 level/owner 内用于 upsert 的稳定键;旧数据可能为空。 */
118
+ key?: string;
103
119
  content: string;
104
120
  createdAt: number;
121
+ /** 最近一次同 key 更新时刻;旧数据回落 createdAt。 */
122
+ updatedAt?: number;
105
123
  }
106
124
  interface EntityMeta {
107
125
  messageId?: string;
@@ -179,9 +197,9 @@ interface UpdateEntityOptions {
179
197
  interface CompressOutput {
180
198
  /** 自动生成的主题标题(一句话) */
181
199
  title: string;
182
- detail: string;
183
200
  summary: string;
184
- concise: string;
201
+ /** 模型报告的摘要输出 token;缺失时由本地 tokenizer 计算。 */
202
+ tokens?: number;
185
203
  entities: Array<{
186
204
  name: string;
187
205
  type: string;
@@ -198,10 +216,44 @@ interface CompressOutput {
198
216
  interface SessionEntry {
199
217
  messages: StoredMessage[];
200
218
  totalTokens: number;
219
+ topics: Topic[];
220
+ topicTokens: number;
201
221
  ids: {
202
222
  chatId: string;
203
223
  userId: string;
204
224
  };
225
+ lastModelContextTokens?: number;
226
+ lastAccessAt: number;
227
+ }
228
+ /** getHistoryWindow 返回的原始消息;字段形态与 updateChat 输入保持一致。 */
229
+ interface MemoryRawMessage {
230
+ messageId: string;
231
+ talkerId: string;
232
+ chatId: string;
233
+ userId: string;
234
+ sessionId: string;
235
+ type: MessageType;
236
+ content: string;
237
+ parts?: ContentPart[];
238
+ payload?: unknown;
239
+ usage: number;
240
+ metadata?: Record<string, unknown>;
241
+ createdAt: number;
242
+ }
243
+ interface MemoryContextWindowUsage {
244
+ modelContextTokens: number;
245
+ usableContextTokens: number;
246
+ compressedTokenLimit: number;
247
+ compressedTokens: number;
248
+ rawTokenLimit: number;
249
+ rawTokens: number;
250
+ }
251
+ /** 固定预算的压缩记忆 + 近期未压缩原始消息。 */
252
+ interface MemoryContextWindow {
253
+ sessionId: string;
254
+ compressedContext: string;
255
+ recentMessages: MemoryRawMessage[];
256
+ usage: MemoryContextWindowUsage;
205
257
  }
206
258
  /** 是否为文档构建知识图谱:true/false 显式控制,"auto" 由内置 LLM 判定 */
207
259
  type BuildGraphMode = boolean | "auto";
@@ -264,6 +316,8 @@ interface AddDocumentOptions {
264
316
  userId?: string;
265
317
  chatId?: string;
266
318
  sessionId?: string;
319
+ /** 内容哈希去重范围;默认 session(兼容旧行为)。 */
320
+ scope?: SearchScope;
267
321
  /** 不传则从首个 H1 / sourceName 推断 */
268
322
  title?: string;
269
323
  sourceName?: string;
@@ -333,16 +387,42 @@ interface MemoryConfig {
333
387
  embeddingBatchSize?: number;
334
388
  /** embedding 批次并发数(默认 2)。*/
335
389
  embeddingConcurrency?: number;
390
+ /** 压缩 Topic 在历史窗口中的固定预算,默认 16K。 */
391
+ compressedContextTokenLimit?: number;
392
+ /** 模型上下文中允许历史记忆使用的比例,默认 0.75。 */
393
+ contextUsageRatio?: number;
394
+ /** 原始消息达到其可用预算的此比例时后台预压缩,默认 0.75。 */
395
+ precompressionRatio?: number;
396
+ /** 单次压缩目标占原始消息预算的比例,默认 0.5。 */
397
+ compressionBatchRatio?: number;
398
+ /** 单次压缩硬上限;0 表示只受 compressionBatchRatio 控制。 */
399
+ compressionBatchTokenLimit?: number;
400
+ /** 单条 Topic 摘要硬上限,实际长度由重要性决定,默认 2048。 */
401
+ topicSummaryMaxTokens?: number;
402
+ /** 未传 getHistoryWindow 模型窗口时使用,默认 256K。 */
403
+ defaultModelContextTokens?: number;
404
+ /** 冷启动最多恢复多久以前的压缩 Topic;0 表示永久。 */
405
+ maxHistoryAgeMs?: number;
406
+ /** 会话缓存空闲释放时间,默认 30 分钟;<=0 关闭。 */
407
+ sessionIdleTtlMs?: number;
408
+ /** 空闲会话扫描周期,默认 1 分钟。 */
409
+ sessionSweepIntervalMs?: number;
410
+ /** @deprecated 0.3.x 原始消息固定阈值;仅为配置兼容保留。 */
336
411
  sessionTokenLimit?: number;
412
+ /** @deprecated 使用 compressedContextTokenLimit。 */
337
413
  historyWindowTokenLimit?: number;
414
+ /** @deprecated 三级 Topic 窗口已移除。 */
338
415
  topicRatio?: [number, number, number];
416
+ /** @deprecated 使用 topicSummaryMaxTokens。 */
339
417
  detailMaxTokens?: number;
418
+ /** 文档摘要仍使用此限制;对话 Topic 使用 topicSummaryMaxTokens。 */
340
419
  summaryMaxTokens?: number;
420
+ /** @deprecated 三级 Topic 窗口已移除。 */
341
421
  conciseMaxTokens?: number;
342
422
  maxConcurrentCompressions?: number;
343
423
  entitySimilarityThreshold?: number;
344
424
  defaultSearchLimit?: number;
345
- /** 每次召回相当于多少毫秒的时间权重加成(默认 3_600_000 即 1 小时)*/
425
+ /** @deprecated 连续历史窗口不再按召回热度重排。 */
346
426
  recallBoostMs?: number;
347
427
  /** 切块策略,目前仅 "markdown-heading"(默认)*/
348
428
  chunkStrategy?: "markdown-heading";
@@ -573,7 +653,13 @@ declare class MemoryManager {
573
653
  private readonly knowledgeManager;
574
654
  private readonly sessionMap;
575
655
  private optimizeTimer?;
656
+ private sessionSweepTimer?;
576
657
  private optimizeRunning;
658
+ private optimizeTask?;
659
+ private destroyTask?;
660
+ private readonly hydration;
661
+ private readonly pendingWrites;
662
+ private warnedDefaultModelContext;
577
663
  constructor(config: MemoryConfig);
578
664
  init(): Promise<void>;
579
665
  /** 后台压实的统一入口:防重入(上一轮未结束则跳过),失败仅告警不影响服务。*/
@@ -583,13 +669,17 @@ declare class MemoryManager {
583
669
  * @param retentionMs 保留多久内的历史版本,默认取配置 optimizeVersionRetentionMs
584
670
  */
585
671
  optimizeStorage(retentionMs?: number): Promise<StorageOptimizeResult[]>;
586
- private restoreFromStorage;
672
+ private ensureSessionHydrated;
673
+ private isSessionBusy;
674
+ private waitForPendingWrites;
675
+ private queueSessionWrite;
587
676
  updateChat(messages: RawMessage[], opts?: UpdateChatOptions): Promise<void>;
677
+ private doUpdateChat;
588
678
  flushChat(sessionId?: string, opts?: {
589
679
  wait?: boolean;
590
680
  waitGraph?: boolean;
591
681
  }): Promise<void>;
592
- updateFacts(content: string, level: FactLevel, userId: string, chatId: string, sessionId?: string): Promise<void>;
682
+ updateFacts(content: string, level: FactLevel, userId: string, chatId: string, sessionId?: string, key?: string): Promise<void>;
593
683
  updateEntity(entities: Entity[], relations: Relation[], context?: UpdateEntityOptions): Promise<void>;
594
684
  search(opts: SearchOptions): Promise<SearchResult[]>;
595
685
  ask(opts: SearchOptions & {
@@ -605,7 +695,8 @@ declare class MemoryManager {
605
695
  * 返回 user 级(userId) ∪ chat 级(chatId),按时间正序拼接。
606
696
  */
607
697
  getFactsForContext(userId: string, chatId: string): Promise<string>;
608
- getHistoryWindow(sessionId: string): string;
698
+ getHistoryWindow(sessionId: string, modelContextTokens?: number): Promise<MemoryContextWindow>;
699
+ private calculateWindowUsage;
609
700
  private buildScopeFilter;
610
701
  private deserializeSession;
611
702
  private serializeSession;
@@ -691,7 +782,7 @@ declare class MemoryManager {
691
782
  /** 由 chatId 反查所属 userId:优先用会话表映射,兜底用 chat 级 fact 自身。 */
692
783
  private resolveChatOwner;
693
784
  /** 手动新增一条事实 */
694
- addFact(content: string, level: FactLevel, userId: string, chatId: string, sessionId?: string): Promise<void>;
785
+ addFact(content: string, level: FactLevel, userId: string, chatId: string, sessionId?: string, key?: string): Promise<void>;
695
786
  /** 删除单条事实,返回是否命中 */
696
787
  deleteFact(factId: string): Promise<boolean>;
697
788
  /**
@@ -742,9 +833,18 @@ declare class MemoryManager {
742
833
  chatId?: string;
743
834
  sessionId?: string;
744
835
  } | undefined, page: PageParams): Promise<Paginated<Document>>;
745
- destroy(): void;
836
+ destroy(): Promise<void>;
746
837
  }
747
838
 
839
+ interface MemoryMigrationReport {
840
+ provider: string;
841
+ topicsScanned: number;
842
+ topicsUpdated: number;
843
+ messagesScanned: number;
844
+ messagesUpdated: number;
845
+ factsScanned: number;
846
+ factsUpdated: number;
847
+ }
748
848
  /**
749
849
  * 记忆系统的存储域层:保持原 LanceService 的公开方法面,内部组合
750
850
  * VectorStoreProvider 的通用原语实现。业务逻辑(混合检索融合、话题回退、
@@ -766,7 +866,12 @@ declare class MemoryStore {
766
866
  */
767
867
  optimizeStorage(retentionMs?: number): Promise<StorageOptimizeResult[]>;
768
868
  addMessages(messages: StoredMessage[]): Promise<void>;
869
+ /** 0.3.x → 0.4.x 数据回填;幂等,不调用 LLM、不删除原始消息。 */
870
+ migrateLegacyData(): Promise<MemoryMigrationReport>;
871
+ /** message_id 稳定时更新原行,否则新增;用于流式 assistant 消息最终态覆盖。 */
872
+ upsertMessages(messages: StoredMessage[]): Promise<void>;
769
873
  getMessagesSince(sessionId: string, since: number, limit?: number): Promise<StoredMessage[]>;
874
+ getMessagesAfterBoundary(sessionId: string, endTime: number, endMessageId?: string): Promise<StoredMessage[]>;
770
875
  getLatestMessages(sessionId: string, limit: number): Promise<StoredMessage[]>;
771
876
  /** 取某会话的全部消息(按 createdAt 升序),供管理面板分页切片使用 */
772
877
  getAllMessagesBySession(sessionId: string): Promise<StoredMessage[]>;
@@ -782,7 +887,10 @@ declare class MemoryStore {
782
887
  private hybridSearch;
783
888
  private keyColumnOf;
784
889
  addTopic(topic: Topic): Promise<void>;
890
+ updateTopic(topic: Topic): Promise<void>;
785
891
  updateTopicRecallCount(summaryId: string, count: number): Promise<void>;
892
+ incrementTopicRecallCounts(topics: Topic[]): Promise<void>;
893
+ getTopicsBySession(sessionId: string, since?: number): Promise<Topic[]>;
786
894
  getRecentTopics(chatId: string, userId: string, n1: number, n2: number, n3: number): Promise<{
787
895
  detail: Topic[];
788
896
  summary: Topic[];
@@ -791,7 +899,9 @@ declare class MemoryStore {
791
899
  /** 全量读取 topics(管理面板列表用,不做 recall 加权排序,按 endTime 倒序)*/
792
900
  getAllTopics(): Promise<Topic[]>;
793
901
  deleteTopicsBySession(sessionId: string): Promise<void>;
902
+ deleteTopicsByIds(summaryIds: string[]): Promise<void>;
794
903
  saveFact(fact: Fact): Promise<void>;
904
+ updateFact(fact: Fact): Promise<void>;
795
905
  getAllFacts(): Promise<Fact[]>;
796
906
  /** 删除单条 fact */
797
907
  deleteFact(factId: string): Promise<void>;
@@ -864,4 +974,4 @@ type RelationType = (typeof DEFAULT_RELATION_TYPES)[number];
864
974
  declare const KIND_CONVERSATION = "conversation";
865
975
  declare const KIND_KNOWLEDGE = "knowledge";
866
976
 
867
- export { type AddDocumentOptions, type BuildGraphMode, type Chunk, type ChunkHit, type ChunkPiece, type CompressOutput, type ContentPart, DEFAULT_NODE_TYPES, DEFAULT_RELATION_TYPES, type Document, type DomainIds, type Entity, type EntityMeta, type Fact, type FactLevel, type Filter, type FilterCondition, KIND_CONVERSATION, KIND_KNOWLEDGE, type KnowledgeSearchOptions, type KnowledgeSearchResult, type MemoryConfig, MemoryManager, MemoryStore, type MessageType, type NodeType, type PageParams, type Paginated, type ProviderCapabilities, type ProviderKind, type RawMessage, type Relation, type RelationType, type SearchMode, type SearchOptions, type SearchResult, type SearchScope, type Session, type SessionEntry, type SessionSearchOptions, type SessionView, type StorageOptimizeResult, type StoredMessage, type Topic, type UpdateChatOptions, type UpdateEntityOptions, type UpdateSessionOptions, type VectorStoreProvider };
977
+ export { type AddDocumentOptions, type BuildGraphMode, type Chunk, type ChunkHit, type ChunkPiece, type CompressOutput, type ContentPart, DEFAULT_NODE_TYPES, DEFAULT_RELATION_TYPES, type Document, type DomainIds, type Entity, type EntityMeta, type Fact, type FactLevel, type Filter, type FilterCondition, KIND_CONVERSATION, KIND_KNOWLEDGE, type KnowledgeSearchOptions, type KnowledgeSearchResult, type MemoryConfig, type MemoryContextWindow, type MemoryContextWindowUsage, MemoryManager, type MemoryMigrationReport, type MemoryRawMessage, MemoryStore, type MessageType, type NodeType, type PageParams, type Paginated, type ProviderCapabilities, type ProviderKind, type RawMessage, type Relation, type RelationType, type SearchMode, type SearchOptions, type SearchResult, type SearchScope, type Session, type SessionEntry, type SessionSearchOptions, type SessionView, type StorageOptimizeResult, type StoredMessage, type Topic, type UpdateChatOptions, type UpdateEntityOptions, type UpdateSessionOptions, type VectorStoreProvider };