@ppagent/memory 0.4.1 → 0.4.2

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.js CHANGED
@@ -1874,7 +1874,7 @@ var LlmService = class {
1874
1874
 
1875
1875
  \u8F93\u51FA\u5FC5\u987B\u662F\u5408\u6CD5\u7684 JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u5305\u542B\u4EFB\u4F55 markdown \u6807\u8BB0\uFF0C\u683C\u5F0F\u5982\u4E0B\uFF1A
1876
1876
  {"title": "...", "summary": "..."}`;
1877
- const formatted = messages.map((m) => formatStoredMessage(m)).join("\n");
1877
+ const formatted = messages.map((m) => formatStoredMessageForCompression(m)).join("\n");
1878
1878
  const response = await this.chatCompletionWithUsage(
1879
1879
  systemPrompt,
1880
1880
  `\u8BF7\u538B\u7F29\u4EE5\u4E0B\u5BF9\u8BDD\uFF1A
@@ -2039,11 +2039,81 @@ ${text}`
2039
2039
  ${context}`, false);
2040
2040
  }
2041
2041
  };
2042
- function formatStoredMessage(message) {
2043
- const extras = [message.parts, message.metadata, message.payload].filter((value) => value && value !== "[]" && value !== "{}").join(" ");
2042
+ function formatStoredMessageForCompression(message) {
2043
+ const metadata = stripToolCallContentFromMetadata(message.metadata);
2044
+ const extras = [message.parts, metadata, message.payload].filter((value) => value && value !== "[]" && value !== "{}").join(" ");
2044
2045
  return `[${new Date(message.createdAt).toISOString()}] ${message.talkerId || "user"}: ${message.content}${extras ? `
2045
2046
  meta=${extras}` : ""}`;
2046
2047
  }
2048
+ var OMIT_COMPRESSION_VALUE = Symbol("omit-compression-value");
2049
+ var TOOL_CALL_CONTAINER_KEYS = /* @__PURE__ */ new Set([
2050
+ "toolcall",
2051
+ "toolcalls",
2052
+ "toolresult",
2053
+ "toolresults",
2054
+ "tooloutput",
2055
+ "tooloutputs",
2056
+ "toolresponse",
2057
+ "toolresponses",
2058
+ "tooluse",
2059
+ "tooluses",
2060
+ "functioncall",
2061
+ "functioncalls",
2062
+ "functionresponse",
2063
+ "functionresponses"
2064
+ ]);
2065
+ var TOOL_CALL_ID_KEYS = /* @__PURE__ */ new Set(["toolcallid", "tooluseid"]);
2066
+ var TOOL_BLOCK_TYPES = /* @__PURE__ */ new Set([
2067
+ "toolcall",
2068
+ "tooloutput",
2069
+ "toolresponse",
2070
+ "toolresult",
2071
+ "tooluse",
2072
+ "functioncall",
2073
+ "functionresponse"
2074
+ ]);
2075
+ function stripToolCallContentFromMetadata(metadata) {
2076
+ if (!metadata || metadata === "{}") return metadata;
2077
+ try {
2078
+ const parsed = JSON.parse(metadata);
2079
+ const sanitized = stripToolCallContent(parsed);
2080
+ return JSON.stringify(sanitized === OMIT_COMPRESSION_VALUE ? {} : sanitized);
2081
+ } catch {
2082
+ return metadata;
2083
+ }
2084
+ }
2085
+ function stripToolCallContent(value) {
2086
+ if (Array.isArray(value)) {
2087
+ return value.map((item) => stripToolCallContent(item)).filter((item) => item !== OMIT_COMPRESSION_VALUE);
2088
+ }
2089
+ if (!isRecord(value)) return value;
2090
+ if (isToolCallBlock(value)) return OMIT_COMPRESSION_VALUE;
2091
+ const sanitized = {};
2092
+ for (const [key, child] of Object.entries(value)) {
2093
+ const normalizedKey = normalizeToolKey(key);
2094
+ if (TOOL_CALL_CONTAINER_KEYS.has(normalizedKey) || TOOL_CALL_ID_KEYS.has(normalizedKey)) {
2095
+ continue;
2096
+ }
2097
+ const next = stripToolCallContent(child);
2098
+ if (next !== OMIT_COMPRESSION_VALUE) sanitized[key] = next;
2099
+ }
2100
+ return sanitized;
2101
+ }
2102
+ function isToolCallBlock(value) {
2103
+ const role = typeof value.role === "string" ? normalizeToolKey(value.role) : "";
2104
+ const type = typeof value.type === "string" ? normalizeToolKey(value.type) : "";
2105
+ if (role === "tool" || role === "function") return true;
2106
+ if (TOOL_BLOCK_TYPES.has(type)) return true;
2107
+ const normalizedKeys = new Set(Object.keys(value).map(normalizeToolKey));
2108
+ const hasToolCallId = [...TOOL_CALL_ID_KEYS].some((key) => normalizedKeys.has(key));
2109
+ return hasToolCallId && ["content", "output", "result", "response"].some((key) => normalizedKeys.has(key));
2110
+ }
2111
+ function normalizeToolKey(value) {
2112
+ return value.replace(/[\s_-]/g, "").toLowerCase();
2113
+ }
2114
+ function isRecord(value) {
2115
+ return value !== null && typeof value === "object";
2116
+ }
2047
2117
 
2048
2118
  // src/manager/compress.manager.ts
2049
2119
  import { v4 as uuidv4 } from "uuid";
package/llms.txt CHANGED
@@ -375,6 +375,7 @@ The package intentionally separates durable base writes from expensive graph con
375
375
 
376
376
  - Registers its write synchronously, so a caller may intentionally fire-and-forget it and a following `getHistoryWindow` will still wait for that write.
377
377
  - Calculates tokens over `content`, `parts`, `payload`, and `metadata`, embeds the searchable text, and upserts raw messages by stable `messageId` before resolving.
378
+ - Raw-message token usage always counts the complete stored `metadata`. When a raw batch is summarized, common tool-call/tool-result fields are removed only from the temporary LLM compression input; persisted metadata and uncompressed `recentMessages` remain unchanged.
378
379
  - Creates or updates the session record.
379
380
  - Updates the in-memory session cache.
380
381
  - Once `getHistoryWindow` has supplied the current model size, reaching the precompression threshold starts background compression.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppagent/memory",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "独立记忆系统模块,向量存储支持 LanceDB / SQLite(sqlite-vec) 双后端自动切换 + Grafeo 知识图谱",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",