mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-25 16:12:13 +00:00
Compaction/Safeguard: bound preserved tail fallback
This commit is contained in:
committed by
Josh Lehman
parent
3a8cf6a863
commit
1566d428a3
@@ -559,6 +559,68 @@ describe("compaction-safeguard recent-turn preservation", () => {
|
||||
expect(section).not.toContain("[non-text content]");
|
||||
});
|
||||
|
||||
it("caps preserved tail when user turns are below preserve target", () => {
|
||||
const messages: AgentMessage[] = [
|
||||
{ role: "user", content: "single user prompt", timestamp: 1 },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-1" }],
|
||||
timestamp: 2,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-2" }],
|
||||
timestamp: 3,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-3" }],
|
||||
timestamp: 4,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-4" }],
|
||||
timestamp: 5,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-5" }],
|
||||
timestamp: 6,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-6" }],
|
||||
timestamp: 7,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-7" }],
|
||||
timestamp: 8,
|
||||
} as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "assistant-8" }],
|
||||
timestamp: 9,
|
||||
} as unknown as AgentMessage,
|
||||
];
|
||||
|
||||
const split = splitPreservedRecentTurns({
|
||||
messages,
|
||||
recentTurnsPreserve: 3,
|
||||
});
|
||||
|
||||
// preserve target is 3 turns -> fallback should cap at 6 role messages
|
||||
expect(split.preservedMessages).toHaveLength(6);
|
||||
expect(
|
||||
split.preservedMessages.some(
|
||||
(msg) =>
|
||||
msg.role === "user" && (msg as { content?: unknown }).content === "single user prompt",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(formatPreservedTurnsSection(split.preservedMessages)).toContain("assistant-8");
|
||||
expect(formatPreservedTurnsSection(split.preservedMessages)).not.toContain("assistant-2");
|
||||
});
|
||||
|
||||
it("clamps preserve count into a safe range", () => {
|
||||
expect(resolveRecentTurnsPreserve(undefined)).toBe(3);
|
||||
expect(resolveRecentTurnsPreserve(-1)).toBe(0);
|
||||
|
||||
@@ -228,26 +228,6 @@ function formatNonTextPlaceholder(content: unknown): string | null {
|
||||
return `[non-text content: ${parts.join(", ")}]`;
|
||||
}
|
||||
|
||||
function findPreservedStartIndexByTurnBoundary(
|
||||
messages: AgentMessage[],
|
||||
preserveTurns: number,
|
||||
): number {
|
||||
let seenUsers = 0;
|
||||
let earliestSelectedUserIndex = -1;
|
||||
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
||||
const role = (messages[i] as { role?: unknown }).role;
|
||||
if (role !== "user") {
|
||||
continue;
|
||||
}
|
||||
seenUsers += 1;
|
||||
earliestSelectedUserIndex = i;
|
||||
if (seenUsers >= preserveTurns) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return earliestSelectedUserIndex;
|
||||
}
|
||||
|
||||
function splitPreservedRecentTurns(params: {
|
||||
messages: AgentMessage[];
|
||||
recentTurnsPreserve: number;
|
||||
@@ -259,22 +239,42 @@ function splitPreservedRecentTurns(params: {
|
||||
if (preserveTurns <= 0) {
|
||||
return { summarizableMessages: params.messages, preservedMessages: [] };
|
||||
}
|
||||
const boundaryStartIndex = findPreservedStartIndexByTurnBoundary(params.messages, preserveTurns);
|
||||
const conversationIndexes: number[] = [];
|
||||
const userIndexes: number[] = [];
|
||||
for (let i = 0; i < params.messages.length; i += 1) {
|
||||
const role = (params.messages[i] as { role?: unknown }).role;
|
||||
if (role === "user" || role === "assistant") {
|
||||
conversationIndexes.push(i);
|
||||
if (role === "user") {
|
||||
userIndexes.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (conversationIndexes.length === 0) {
|
||||
return { summarizableMessages: params.messages, preservedMessages: [] };
|
||||
}
|
||||
|
||||
const preservedIndexSet = new Set<number>();
|
||||
if (boundaryStartIndex >= 0) {
|
||||
for (let i = boundaryStartIndex; i < params.messages.length; i += 1) {
|
||||
const role = (params.messages[i] as { role?: unknown }).role;
|
||||
if (role === "user" || role === "assistant") {
|
||||
preservedIndexSet.add(i);
|
||||
if (userIndexes.length >= preserveTurns) {
|
||||
const boundaryStartIndex = userIndexes[userIndexes.length - preserveTurns] ?? -1;
|
||||
if (boundaryStartIndex >= 0) {
|
||||
for (const index of conversationIndexes) {
|
||||
if (index >= boundaryStartIndex) {
|
||||
preservedIndexSet.add(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const fallbackMessageCount = preserveTurns * 2;
|
||||
for (let i = params.messages.length - 1; i >= 0; i -= 1) {
|
||||
const role = (params.messages[i] as { role?: unknown }).role;
|
||||
if (role === "user" || role === "assistant") {
|
||||
preservedIndexSet.add(i);
|
||||
for (const userIndex of userIndexes) {
|
||||
preservedIndexSet.add(userIndex);
|
||||
}
|
||||
for (let i = conversationIndexes.length - 1; i >= 0; i -= 1) {
|
||||
const index = conversationIndexes[i];
|
||||
if (index === undefined) {
|
||||
continue;
|
||||
}
|
||||
preservedIndexSet.add(index);
|
||||
if (preservedIndexSet.size >= fallbackMessageCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user