feat: include compaction messages and handle new summary message types (#756)

This commit is contained in:
jnjpng
2026-01-30 18:19:51 -08:00
committed by GitHub
parent bb6ce1f2c8
commit 37a237ad0f
10 changed files with 497 additions and 27 deletions

View File

@@ -5,11 +5,7 @@ import type {
Message,
TextContent,
} from "@letta-ai/letta-client/resources/agents/messages";
import {
COMPACTION_SUMMARY_HEADER,
SYSTEM_REMINDER_CLOSE,
SYSTEM_REMINDER_OPEN,
} from "../../constants";
import { SYSTEM_REMINDER_CLOSE, SYSTEM_REMINDER_OPEN } from "../../constants";
import type { Buffers } from "./accumulator";
/**
@@ -183,15 +179,18 @@ export function backfillBuffers(buffers: Buffers, history: Message[]): void {
case "user_message": {
const rawText = renderUserContentParts(msg.content);
// Check if this is a compaction summary message (system_alert with summary)
// Check if this is a compaction summary message (old format embedded in user_message)
const compactionSummary = extractCompactionSummary(rawText);
if (compactionSummary) {
// Render as a user message with context header and summary
// Render as a finished compaction event
const exists = buffers.byId.has(lineId);
buffers.byId.set(lineId, {
kind: "user",
kind: "event",
id: lineId,
text: `${COMPACTION_SUMMARY_HEADER}\n\n${compactionSummary}`,
eventType: "compaction",
eventData: {},
phase: "finished",
summary: compactionSummary,
});
if (!exists) buffers.order.push(lineId);
break;
@@ -336,8 +335,74 @@ export function backfillBuffers(buffers: Buffers, history: Message[]): void {
break;
}
default:
break; // ignore other message types
default: {
// Handle new compaction message types (when include_compaction_messages=true)
// These are not yet in the SDK types, so we handle them via string comparison
const msgType = msg.message_type as string | undefined;
if (msgType === "summary_message") {
// SummaryMessage has: summary (str), compaction_stats (optional)
const summaryMsg = msg as Message & {
summary?: string;
compaction_stats?: {
trigger?: string;
context_tokens_before?: number;
context_tokens_after?: number;
context_window?: number;
messages_count_before?: number;
messages_count_after?: number;
};
};
const summaryText = summaryMsg.summary || "";
const stats = summaryMsg.compaction_stats;
// Find the most recent compaction event line and update it with summary and stats
for (let i = buffers.order.length - 1; i >= 0; i--) {
const orderId = buffers.order[i];
if (!orderId) continue;
const line = buffers.byId.get(orderId);
if (line?.kind === "event" && line.eventType === "compaction") {
line.phase = "finished";
line.summary = summaryText;
if (stats) {
line.stats = {
trigger: stats.trigger,
contextTokensBefore: stats.context_tokens_before,
contextTokensAfter: stats.context_tokens_after,
contextWindow: stats.context_window,
messagesCountBefore: stats.messages_count_before,
messagesCountAfter: stats.messages_count_after,
};
}
break;
}
}
break;
}
if (msgType === "event_message") {
// EventMessage has: event_type (str), event_data (dict)
const eventMsg = msg as Message & {
event_type?: string;
event_data?: Record<string, unknown>;
};
const exists = buffers.byId.has(lineId);
buffers.byId.set(lineId, {
kind: "event",
id: lineId,
eventType: eventMsg.event_type || "unknown",
eventData: eventMsg.event_data || {},
phase: "finished", // In backfill, events are always finished (summary already processed)
});
if (!exists) buffers.order.push(lineId);
break;
}
// ignore other message types
break;
}
}
}