@xdarkicex/openclaw-memory-libravdb 1.4.10 → 1.4.12
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/context-engine.d.ts +16 -1
- package/dist/context-engine.js +191 -54
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/context-engine.d.ts
CHANGED
|
@@ -17,6 +17,18 @@ type OpenClawCompatibleAssembleResult = {
|
|
|
17
17
|
systemPromptAddition: string;
|
|
18
18
|
debug?: AssembleContextInternalResponse["debug"];
|
|
19
19
|
};
|
|
20
|
+
type OpenClawCompatibleCompactResult = {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
compacted: boolean;
|
|
23
|
+
reason?: string;
|
|
24
|
+
result?: {
|
|
25
|
+
summary?: string;
|
|
26
|
+
firstKeptEntryId?: string;
|
|
27
|
+
tokensBefore: number;
|
|
28
|
+
tokensAfter?: number;
|
|
29
|
+
details?: unknown;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
20
32
|
export declare function normalizeKernelMessage(message: {
|
|
21
33
|
role: string;
|
|
22
34
|
content: unknown;
|
|
@@ -76,7 +88,8 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
|
|
|
76
88
|
sessionId: string;
|
|
77
89
|
force?: boolean;
|
|
78
90
|
targetSize?: number;
|
|
79
|
-
|
|
91
|
+
tokenBudget?: number;
|
|
92
|
+
}): Promise<OpenClawCompatibleCompactResult>;
|
|
80
93
|
afterTurn(args: {
|
|
81
94
|
sessionId: string;
|
|
82
95
|
sessionKey?: string;
|
|
@@ -88,6 +101,8 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
|
|
|
88
101
|
}>;
|
|
89
102
|
prePromptMessageCount?: number;
|
|
90
103
|
isHeartbeat?: boolean;
|
|
104
|
+
tokenBudget?: number;
|
|
105
|
+
runtimeContext?: Record<string, unknown>;
|
|
91
106
|
}): Promise<any>;
|
|
92
107
|
};
|
|
93
108
|
export {};
|
package/dist/context-engine.js
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
const APPROX_CHARS_PER_TOKEN = 4;
|
|
2
|
+
const ASSEMBLE_BUDGET_HEADROOM_TOKENS = 256;
|
|
3
|
+
function requireSessionId(sessionId, operation) {
|
|
4
|
+
const normalized = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
5
|
+
if (normalized.length > 0) {
|
|
6
|
+
return normalized;
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`LibraVDB ${operation} requires a non-empty sessionId; refusing ambiguous request.`);
|
|
9
|
+
}
|
|
10
|
+
function normalizeCompactResult(response) {
|
|
11
|
+
const didCompact = response?.didCompact === true;
|
|
12
|
+
const details = {
|
|
13
|
+
clustersFormed: typeof response?.clustersFormed === "number" ? response.clustersFormed : undefined,
|
|
14
|
+
clustersDeclined: typeof response?.clustersDeclined === "number" ? response.clustersDeclined : undefined,
|
|
15
|
+
turnsRemoved: typeof response?.turnsRemoved === "number" ? response.turnsRemoved : undefined,
|
|
16
|
+
summaryMethod: typeof response?.summaryMethod === "string" && response.summaryMethod.length > 0
|
|
17
|
+
? response.summaryMethod
|
|
18
|
+
: undefined,
|
|
19
|
+
meanConfidence: typeof response?.meanConfidence === "number" ? response.meanConfidence : undefined,
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
ok: true,
|
|
23
|
+
compacted: didCompact,
|
|
24
|
+
...(didCompact ? {} : { reason: "not_compacted" }),
|
|
25
|
+
result: {
|
|
26
|
+
tokensBefore: 0,
|
|
27
|
+
...(details.summaryMethod ? { summary: details.summaryMethod } : {}),
|
|
28
|
+
details,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
1
32
|
function describeUnexpectedContent(value) {
|
|
2
33
|
try {
|
|
3
34
|
const serialized = JSON.stringify(value);
|
|
@@ -57,6 +88,72 @@ function normalizeKernelContent(content) {
|
|
|
57
88
|
}
|
|
58
89
|
return content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n");
|
|
59
90
|
}
|
|
91
|
+
function approximateTokenCount(text) {
|
|
92
|
+
if (!text)
|
|
93
|
+
return 0;
|
|
94
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
95
|
+
}
|
|
96
|
+
function approximateMessageTokens(message) {
|
|
97
|
+
// Approximate per-message wrapper overhead so trimming is conservative.
|
|
98
|
+
return approximateTokenCount(message.content) + 8;
|
|
99
|
+
}
|
|
100
|
+
function approximateMessagesTokens(messages) {
|
|
101
|
+
return messages.reduce((sum, message) => sum + approximateMessageTokens(message), 0);
|
|
102
|
+
}
|
|
103
|
+
function truncateContentToTokenBudget(content, tokenBudget) {
|
|
104
|
+
if (tokenBudget <= 0)
|
|
105
|
+
return "";
|
|
106
|
+
const maxChars = Math.max(1, tokenBudget * APPROX_CHARS_PER_TOKEN);
|
|
107
|
+
if (content.length <= maxChars)
|
|
108
|
+
return content;
|
|
109
|
+
// Keep the tail so recent tool output / latest answer content is preserved.
|
|
110
|
+
return content.slice(content.length - maxChars);
|
|
111
|
+
}
|
|
112
|
+
function trimMessagesToBudget(messages, tokenBudget) {
|
|
113
|
+
if (tokenBudget <= 0 || messages.length === 0) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const kept = [];
|
|
117
|
+
let used = 0;
|
|
118
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
119
|
+
const candidate = messages[i];
|
|
120
|
+
const cost = approximateMessageTokens(candidate);
|
|
121
|
+
if (used + cost > tokenBudget) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
kept.push(candidate);
|
|
125
|
+
used += cost;
|
|
126
|
+
}
|
|
127
|
+
if (kept.length > 0) {
|
|
128
|
+
return kept.reverse();
|
|
129
|
+
}
|
|
130
|
+
const last = messages[messages.length - 1];
|
|
131
|
+
const contentBudget = Math.max(1, tokenBudget - 8);
|
|
132
|
+
const truncated = truncateContentToTokenBudget(last.content, contentBudget);
|
|
133
|
+
if (!truncated) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
return [{ ...last, content: truncated }];
|
|
137
|
+
}
|
|
138
|
+
function enforceTokenBudgetInvariant(result, tokenBudget) {
|
|
139
|
+
if (typeof tokenBudget !== "number" || !Number.isFinite(tokenBudget) || tokenBudget <= 0) {
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
const hardBudget = Math.max(1, Math.floor(tokenBudget));
|
|
143
|
+
const effectiveBudget = Math.max(1, hardBudget - ASSEMBLE_BUDGET_HEADROOM_TOKENS);
|
|
144
|
+
const estimated = typeof result.estimatedTokens === "number" ? result.estimatedTokens : 0;
|
|
145
|
+
const approxFromMessages = approximateMessagesTokens(result.messages);
|
|
146
|
+
if (estimated <= effectiveBudget && approxFromMessages <= effectiveBudget) {
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
const trimmedMessages = trimMessagesToBudget(result.messages, effectiveBudget);
|
|
150
|
+
const trimmedEstimate = approximateMessagesTokens(trimmedMessages);
|
|
151
|
+
return {
|
|
152
|
+
...result,
|
|
153
|
+
messages: trimmedMessages,
|
|
154
|
+
estimatedTokens: Math.min(effectiveBudget, trimmedEstimate),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
60
157
|
export function normalizeKernelMessage(message) {
|
|
61
158
|
return {
|
|
62
159
|
role: message.role,
|
|
@@ -87,6 +184,27 @@ export function normalizeAssembleResult(result) {
|
|
|
87
184
|
};
|
|
88
185
|
}
|
|
89
186
|
export function buildContextEngineFactory(runtime, cfg, recallCache, logger = console) {
|
|
187
|
+
function buildCompactSessionRequest(args) {
|
|
188
|
+
// OpenClaw core now requests budget-style compaction using tokenBudget,
|
|
189
|
+
// but the current LibraVDB compact_session wire contract still expects
|
|
190
|
+
// targetSize. Use tokenBudget as the compatibility target so overflow and
|
|
191
|
+
// timeout retries still compact toward the host's requested prompt budget.
|
|
192
|
+
const targetSize = args.targetSize ?? args.tokenBudget;
|
|
193
|
+
return {
|
|
194
|
+
sessionId: requireSessionId(args.sessionId, "compact"),
|
|
195
|
+
force: args.force,
|
|
196
|
+
...(typeof targetSize === "number" ? { targetSize } : {}),
|
|
197
|
+
...(typeof cfg.continuityMinTurns === "number"
|
|
198
|
+
? { continuityMinTurns: cfg.continuityMinTurns }
|
|
199
|
+
: {}),
|
|
200
|
+
...(typeof cfg.continuityTailBudgetTokens === "number"
|
|
201
|
+
? { continuityTailBudgetTokens: cfg.continuityTailBudgetTokens }
|
|
202
|
+
: {}),
|
|
203
|
+
...(typeof cfg.continuityPriorContextTokens === "number"
|
|
204
|
+
? { continuityPriorContextTokens: cfg.continuityPriorContextTokens }
|
|
205
|
+
: {}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
90
208
|
return {
|
|
91
209
|
info: { id: "libravdb-memory", name: "LibraVDB Memory", ownsCompaction: true },
|
|
92
210
|
ownsCompaction: true,
|
|
@@ -133,71 +251,90 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
|
|
|
133
251
|
const messages = normalizeKernelMessages(args.messages);
|
|
134
252
|
const kernel = runtime.getKernel();
|
|
135
253
|
if (kernel) {
|
|
136
|
-
|
|
254
|
+
try {
|
|
255
|
+
return enforceTokenBudgetInvariant(normalizeAssembleResult(await kernel.assembleContext({
|
|
256
|
+
sessionId: args.sessionId,
|
|
257
|
+
sessionKey: args.sessionKey,
|
|
258
|
+
userId: args.userId,
|
|
259
|
+
queryText: args.prompt ?? "",
|
|
260
|
+
visibleMessages: messages,
|
|
261
|
+
tokenBudget: args.tokenBudget,
|
|
262
|
+
config: {},
|
|
263
|
+
emitDebug: true
|
|
264
|
+
})), args.tokenBudget);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
logger.warn?.(`LibraVDB assemble kernel failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`);
|
|
268
|
+
const fallbackMessages = trimMessagesToBudget(messages.map((message) => ({ ...message })), Math.max(1, Math.floor(args.tokenBudget) - ASSEMBLE_BUDGET_HEADROOM_TOKENS));
|
|
269
|
+
return {
|
|
270
|
+
messages: fallbackMessages,
|
|
271
|
+
estimatedTokens: approximateMessagesTokens(fallbackMessages),
|
|
272
|
+
systemPromptAddition: "",
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const rpc = await runtime.getRpc();
|
|
277
|
+
try {
|
|
278
|
+
const resp = await rpc.call("assemble_context_internal", {
|
|
137
279
|
sessionId: args.sessionId,
|
|
138
280
|
sessionKey: args.sessionKey,
|
|
139
281
|
userId: args.userId,
|
|
140
|
-
|
|
141
|
-
visibleMessages: messages,
|
|
282
|
+
messages,
|
|
142
283
|
tokenBudget: args.tokenBudget,
|
|
143
|
-
|
|
144
|
-
emitDebug: true
|
|
145
|
-
|
|
284
|
+
prompt: args.prompt,
|
|
285
|
+
emitDebug: true,
|
|
286
|
+
config: {
|
|
287
|
+
useSessionRecallProjection: cfg.useSessionRecallProjection,
|
|
288
|
+
useSessionSummarySearchExperiment: cfg.useSessionSummarySearchExperiment,
|
|
289
|
+
tokenBudgetFraction: cfg.tokenBudgetFraction,
|
|
290
|
+
authoredHardBudgetFraction: cfg.authoredHardBudgetFraction,
|
|
291
|
+
authoredSoftBudgetFraction: cfg.authoredSoftBudgetFraction,
|
|
292
|
+
elevatedGuidanceBudgetFraction: cfg.elevatedGuidanceBudgetFraction,
|
|
293
|
+
topK: cfg.topK,
|
|
294
|
+
continuityMinTurns: cfg.continuityMinTurns,
|
|
295
|
+
continuityTailBudgetTokens: cfg.continuityTailBudgetTokens,
|
|
296
|
+
continuityPriorContextTokens: cfg.continuityPriorContextTokens,
|
|
297
|
+
compactThreshold: cfg.compactThreshold,
|
|
298
|
+
compactSessionTokenBudget: cfg.compactSessionTokenBudget,
|
|
299
|
+
section7Theta1: cfg.section7Theta1,
|
|
300
|
+
section7Kappa: cfg.section7Kappa,
|
|
301
|
+
section7HopEta: cfg.section7HopEta,
|
|
302
|
+
section7HopThreshold: cfg.section7HopThreshold,
|
|
303
|
+
section7CoarseTopK: cfg.section7CoarseTopK,
|
|
304
|
+
section7SecondPassTopK: cfg.section7SecondPassTopK,
|
|
305
|
+
section7AuthorityRecencyLambda: cfg.section7AuthorityRecencyLambda,
|
|
306
|
+
section7AuthorityRecencyWeight: cfg.section7AuthorityRecencyWeight,
|
|
307
|
+
section7AuthorityFrequencyWeight: cfg.section7AuthorityFrequencyWeight,
|
|
308
|
+
section7AuthorityAuthoredWeight: cfg.section7AuthorityAuthoredWeight,
|
|
309
|
+
recoveryFloorScore: cfg.recoveryFloorScore,
|
|
310
|
+
recoveryMinTopK: cfg.recoveryMinTopK,
|
|
311
|
+
recoveryMinConfidenceMean: cfg.recoveryMinConfidenceMean,
|
|
312
|
+
recencyLambdaSession: cfg.recencyLambdaSession,
|
|
313
|
+
recencyLambdaUser: cfg.recencyLambdaUser,
|
|
314
|
+
recencyLambdaGlobal: cfg.recencyLambdaGlobal,
|
|
315
|
+
ingestionGateThreshold: cfg.ingestionGateThreshold,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
return enforceTokenBudgetInvariant(normalizeAssembleResult(resp), args.tokenBudget);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
logger.warn?.(`LibraVDB assemble sidecar failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`);
|
|
322
|
+
const fallbackMessages = trimMessagesToBudget(messages.map((message) => ({ ...message })), Math.max(1, Math.floor(args.tokenBudget) - ASSEMBLE_BUDGET_HEADROOM_TOKENS));
|
|
323
|
+
return {
|
|
324
|
+
messages: fallbackMessages,
|
|
325
|
+
estimatedTokens: approximateMessagesTokens(fallbackMessages),
|
|
326
|
+
systemPromptAddition: "",
|
|
327
|
+
};
|
|
146
328
|
}
|
|
147
|
-
const rpc = await runtime.getRpc();
|
|
148
|
-
const resp = await rpc.call("assemble_context_internal", {
|
|
149
|
-
sessionId: args.sessionId,
|
|
150
|
-
sessionKey: args.sessionKey,
|
|
151
|
-
userId: args.userId,
|
|
152
|
-
messages,
|
|
153
|
-
tokenBudget: args.tokenBudget,
|
|
154
|
-
prompt: args.prompt,
|
|
155
|
-
emitDebug: true,
|
|
156
|
-
config: {
|
|
157
|
-
useSessionRecallProjection: cfg.useSessionRecallProjection,
|
|
158
|
-
useSessionSummarySearchExperiment: cfg.useSessionSummarySearchExperiment,
|
|
159
|
-
tokenBudgetFraction: cfg.tokenBudgetFraction,
|
|
160
|
-
authoredHardBudgetFraction: cfg.authoredHardBudgetFraction,
|
|
161
|
-
authoredSoftBudgetFraction: cfg.authoredSoftBudgetFraction,
|
|
162
|
-
elevatedGuidanceBudgetFraction: cfg.elevatedGuidanceBudgetFraction,
|
|
163
|
-
topK: cfg.topK,
|
|
164
|
-
continuityMinTurns: cfg.continuityMinTurns,
|
|
165
|
-
continuityTailBudgetTokens: cfg.continuityTailBudgetTokens,
|
|
166
|
-
continuityPriorContextTokens: cfg.continuityPriorContextTokens,
|
|
167
|
-
compactThreshold: cfg.compactThreshold,
|
|
168
|
-
compactSessionTokenBudget: cfg.compactSessionTokenBudget,
|
|
169
|
-
section7Theta1: cfg.section7Theta1,
|
|
170
|
-
section7Kappa: cfg.section7Kappa,
|
|
171
|
-
section7HopEta: cfg.section7HopEta,
|
|
172
|
-
section7HopThreshold: cfg.section7HopThreshold,
|
|
173
|
-
section7CoarseTopK: cfg.section7CoarseTopK,
|
|
174
|
-
section7SecondPassTopK: cfg.section7SecondPassTopK,
|
|
175
|
-
section7AuthorityRecencyLambda: cfg.section7AuthorityRecencyLambda,
|
|
176
|
-
section7AuthorityRecencyWeight: cfg.section7AuthorityRecencyWeight,
|
|
177
|
-
section7AuthorityFrequencyWeight: cfg.section7AuthorityFrequencyWeight,
|
|
178
|
-
section7AuthorityAuthoredWeight: cfg.section7AuthorityAuthoredWeight,
|
|
179
|
-
recoveryFloorScore: cfg.recoveryFloorScore,
|
|
180
|
-
recoveryMinTopK: cfg.recoveryMinTopK,
|
|
181
|
-
recoveryMinConfidenceMean: cfg.recoveryMinConfidenceMean,
|
|
182
|
-
recencyLambdaSession: cfg.recencyLambdaSession,
|
|
183
|
-
recencyLambdaUser: cfg.recencyLambdaUser,
|
|
184
|
-
recencyLambdaGlobal: cfg.recencyLambdaGlobal,
|
|
185
|
-
ingestionGateThreshold: cfg.ingestionGateThreshold,
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
return normalizeAssembleResult(resp);
|
|
189
329
|
},
|
|
190
330
|
async compact(args) {
|
|
331
|
+
const request = buildCompactSessionRequest(args);
|
|
191
332
|
const kernel = runtime.getKernel();
|
|
192
333
|
if (kernel) {
|
|
193
|
-
return await kernel.compactSession(
|
|
194
|
-
sessionId: args.sessionId,
|
|
195
|
-
force: args.force,
|
|
196
|
-
targetSize: args.targetSize,
|
|
197
|
-
});
|
|
334
|
+
return normalizeCompactResult(await kernel.compactSession(request));
|
|
198
335
|
}
|
|
199
336
|
const rpc = await runtime.getRpc();
|
|
200
|
-
return await rpc.call("compact_session",
|
|
337
|
+
return normalizeCompactResult(await rpc.call("compact_session", request));
|
|
201
338
|
},
|
|
202
339
|
async afterTurn(args) {
|
|
203
340
|
const messages = normalizeKernelMessages(args.messages);
|
package/openclaw.plugin.json
CHANGED