MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / compactMessages

Function compactMessages

src/utils/compaction.ts:134–193  ·  view source on GitHub ↗
(
  messages: Message[],
  options: {
    keepLastTurns?: number;
    summarize: (msgs: Message[]) => Promise<string>;
  },
)

Source from the content-addressed store, hash-verified

132}
133
134/**
135 * Compact a message list by summarizing all but the most recent `keepLast` turns.
136 * A "turn" is a user message + everything until the next user message.
137 *
138 * The caller provides a `summarize` callback that runs an LLM call on the
139 * messages-to-summarize and returns the summary text.
140 */
141export async function compactMessages(
142 messages: Message[],
143 options: {
144 keepLastTurns?: number;
145 summarize: (msgs: Message[]) => Promise<string>;
146 },
147): Promise<CompactionResult> {
148 const keepLastTurns = options.keepLastTurns ?? 6;
149 const before = estimateTokens(messages);
150
151 const system = messages.filter(m => m.role === 'system');
152 const nonSystem = messages.filter(m => m.role !== 'system');
153
154 // Group into turn-groups: user msg starts each group
155 const groups: Message[][] = [];
156 let cur: Message[] = [];
157 for (const m of nonSystem) {
158 if (m.role === 'user' && cur.length > 0) {
159 groups.push(cur);
160 cur = [m];
161 } else {
162 cur.push(m);
163 }
164 }
165 if (cur.length > 0) groups.push(cur);
166
167 if (groups.length <= keepLastTurns) {
168 // Nothing to do
169 return { messages, before, after: before, turnsCompacted: 0 };
170 }
171
172 const toSummarize = groups.slice(0, groups.length - keepLastTurns).flat();
173 const toKeep = groups.slice(groups.length - keepLastTurns).flat();
174 const turnsCompacted = groups.length - keepLastTurns;
175
176 // Filter out existing summary messages from the to-summarize set
177 // and keep ALL system messages (including any prior summary) so the new
178 // summary CAN reference them.
179 const summarizationInput = buildCompactionRequest(toSummarize);
180 let summaryText: string;
181 try {
182 summaryText = await options.summarize(summarizationInput);
183 } catch (e: any) {
184 logger.warn('Compaction summarization failed; returning original messages', { err: e?.message });
185 return { messages, before, after: before, turnsCompacted: 0 };
186 }
187 // Refuse to trade real history for an empty summary.
188 //
189 // Compaction DELETES the summarized turns. A throw is handled above, but a summarizer can
190 // also succeed and return nothing useful: an aborted stream (the caller's loop does
191 // `if (signal?.aborted) break` and returns ''), a provider sending an empty body, a local

Callers 1

runCompactionMethod · 0.85

Calls 6

buildCompactionRequestFunction · 0.85
summarizeMethod · 0.80
warnMethod · 0.80
infoMethod · 0.80
estimateTokensFunction · 0.70
pushMethod · 0.45

Tested by

no test coverage detected