Skip to content

Commit 1e15200

Browse files
committed
delete buildTranscript
1 parent b52756c commit 1e15200

5 files changed

Lines changed: 39 additions & 90 deletions

File tree

packages/codingcode/src/context/compressor.ts

Lines changed: 13 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import { randomUUID } from 'crypto';
2-
import { resolveSessionDir, appendLine } from '../session/io.js';
2+
import { resolveSessionJsonlPath, appendLine } from '../session/io.js';
33
import {
44
estimateTokens,
55
estimateMessageTokens,
6-
estimateTokensForContent,
76
} from './util.js';
8-
import { applyVisibilityEvents } from '../session/messages.js';
7+
import { buildMessagesFromEvents } from '../session/messages.js';
98
import { resolveLLM } from '../llm/llm-resolver.js';
109
import { COMPACTION_SYSTEM_PROMPT } from './compaction-prompt.js';
1110
import type { ContextConfig } from './config.js';
1211
import type { Message } from '../core/types.js';
1312
import type { SessionEvent, SummaryEvent } from '../session/types.js';
1413
import type { LLMClient } from '../llm/client.js';
1514
import { assemblePayload } from './organizer.js';
16-
import { join } from 'path';
1715

1816
export interface CompressResult {
1917
didCompress: boolean;
@@ -85,8 +83,8 @@ export async function compactWithLLM(
8583
usage?: number,
8684
modelMaxTokens?: number
8785
): Promise<CompressResult> {
86+
const payload = assemblePayload(sessionId, encodedProjectPath, config, modelMaxTokens);
8887
if (!compactedEvents || currentTurnId === undefined) {
89-
const payload = assemblePayload(sessionId, encodedProjectPath, config, modelMaxTokens);
9088
compactedEvents = payload.compactedEvents;
9189
currentTurnId = payload.currentTurnId;
9290
}
@@ -95,38 +93,33 @@ export async function compactWithLLM(
9593

9694
const threshold = modelMaxTokens ? modelMaxTokens * config.compactionThreshold : Infinity;
9795
if (usage === undefined || usage - released > threshold) {
98-
released += await tryCompaction(sessionId, config, llm, compactedEvents, currentTurnId);
96+
released += await tryCompaction(sessionId, config, llm, compactedEvents, currentTurnId, payload.compactedTurnIds);
9997
}
10098

101-
const payload = assemblePayload(sessionId, encodedProjectPath, config, modelMaxTokens);
99+
const postPayload = assemblePayload(sessionId, encodedProjectPath, config, modelMaxTokens);
102100
return {
103101
didCompress: released > 0,
104102
released,
105-
promptEstimate: estimateTokens(payload.messages),
103+
promptEstimate: estimateTokens(postPayload.messages),
106104
};
107105
}
108106

109107

110108
// ---------- LLM Compaction ----------
111109

112-
const ESTIMATED_SUMMARY_TOKENS = 5000;
113-
const MAX_TOOL_RESULT_TOKENS = 30000;
114-
115110
async function tryCompaction(
116111
sessionId: string,
117112
config: ContextConfig,
118113
llm: LLMClient | null,
119114
compactedEvents: SessionEvent[],
120-
currentTurnId: number
115+
currentTurnId: number,
116+
compactedTurnIds: Set<number>,
121117
): Promise<number> {
122118
const endTurn = currentTurnId - config.keepRecentTurns - 1;
123119
if (endTurn < 1) return 0;
124120

125-
const { hidden } = applyVisibilityEvents(compactedEvents);
126-
127121
const inRange = compactedEvents.filter((ev) => {
128122
if (ev.type === 'session_meta') return false;
129-
if ('uuid' in ev && hidden.has((ev as any).uuid)) return false;
130123
if ('turnId' in ev && (ev as any).turnId >= 1 && (ev as any).turnId <= endTurn) return true;
131124
return false;
132125
});
@@ -135,15 +128,15 @@ async function tryCompaction(
135128
const targetEvents = getIncrementalEvents(inRange);
136129
if (targetEvents.length === 0) return 0;
137130

138-
const totalTokens = targetEvents.reduce((sum, e) => sum + estimateEventTokens(e), 0);
131+
const msgs = buildMessagesFromEvents(targetEvents, compactedTurnIds);
132+
const totalTokens = estimateTokens(msgs);
139133

140134
let compactionLlm = await resolveLLM(config.compactionModel, llm);
141135
if (compactionLlm && compactionLlm.modelInfo.maxTokens < totalTokens + 25000) {
142136
compactionLlm = llm;
143137
}
144138

145-
const transcript = buildTranscript(targetEvents);
146-
const summary = await callLLMForCompaction(transcript, compactionLlm, config);
139+
const summary = await callLLMForCompaction(msgs, compactionLlm, config);
147140
if (!summary) return 0;
148141

149142
const replacedUuids: string[] = [];
@@ -164,10 +157,7 @@ async function tryCompaction(
164157
lastSummarizedTurnId: lastTurnId,
165158
timestamp: new Date().toISOString(),
166159
};
167-
const dir = resolveSessionDir(sessionId);
168-
if (!dir) throw new Error(`Session ${sessionId} not found`);
169-
appendLine(join(dir, `${sessionId}.jsonl`), event);
170-
for (const u of replacedUuids) hidden.add(u);
160+
appendLine(resolveSessionJsonlPath(sessionId), event);
171161

172162
const summaryMsg: Message = { role: 'system', name: 'compacted_history', content: summary };
173163
return Math.max(0, totalTokens - estimateMessageTokens(summaryMsg));
@@ -184,64 +174,6 @@ function getIncrementalEvents(inRange: SessionEvent[]): SessionEvent[] {
184174
return inRange.filter((e) => 'turnId' in e && (e as any).turnId > lastTurn);
185175
}
186176

187-
function buildTranscript(events: SessionEvent[]): Message[] {
188-
const transcript: Message[] = [];
189-
for (const ev of events) {
190-
switch (ev.type) {
191-
case 'user':
192-
transcript.push({ role: 'user', content: ev.content });
193-
break;
194-
case 'assistant':
195-
transcript.push({ role: 'assistant', content: ev.content });
196-
break;
197-
case 'tool_result': {
198-
let content = ev.output;
199-
const tokens = estimateTokensForContent(content);
200-
if (tokens > MAX_TOOL_RESULT_TOKENS) {
201-
const ratio = MAX_TOOL_RESULT_TOKENS / tokens;
202-
const keepChars = Math.floor(content.length * ratio);
203-
content =
204-
content.slice(0, keepChars) +
205-
`\n\n[...truncated: ${tokens} tokens total, showing first ${MAX_TOOL_RESULT_TOKENS}]`;
206-
}
207-
transcript.push({
208-
role: 'tool',
209-
content,
210-
tool_call_id: ev.toolCallId,
211-
tool_name: ev.toolName,
212-
} as any);
213-
break;
214-
}
215-
case 'summary':
216-
transcript.push({ role: 'system', name: 'compacted_history', content: ev.summaryText });
217-
break;
218-
}
219-
}
220-
return transcript;
221-
}
222-
223-
function estimateEventTokens(e: SessionEvent): number {
224-
if (e.type === 'user') return estimateMessageTokens({ role: 'user', content: e.content });
225-
if (e.type === 'assistant')
226-
return estimateMessageTokens({ role: 'assistant', content: e.content });
227-
if (e.type === 'tool_result') {
228-
return estimateMessageTokens({
229-
role: 'tool',
230-
content: e.output,
231-
tool_call_id: e.toolCallId,
232-
tool_name: e.toolName,
233-
} as any);
234-
}
235-
if (e.type === 'summary') {
236-
return estimateMessageTokens({
237-
role: 'system',
238-
name: 'compacted_history',
239-
content: e.summaryText,
240-
});
241-
}
242-
return 0;
243-
}
244-
245177
async function callLLMForCompaction(
246178
transcript: Message[],
247179
fallbackLlm: LLMClient | null,
@@ -251,7 +183,7 @@ async function callLLMForCompaction(
251183
if (!llm) return null;
252184

253185
const transcriptText = transcript
254-
.map((m) => `[${m.role}${m.tool_name ? ':' + m.tool_name : ''}]\n${m.content}`)
186+
.map((m) => `[${m.role}${(m as any).tool_name ? ':' + (m as any).tool_name : ''}]\n${m.content}`)
255187
.join('\n\n');
256188

257189
const system = COMPACTION_SYSTEM_PROMPT;

packages/codingcode/src/context/organizer.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import type { ContextConfig } from './config.js';
22
import type { Message } from '../core/types.js';
3-
import { findSessionIndex, resolveSessionDir, readHistory, appendLine } from '../session/io.js';
3+
import { findSessionIndex, resolveSessionJsonlPath, readHistory, appendLine } from '../session/io.js';
44
import { applyVisibilityEvents, buildMessagesFromEvents } from '../session/messages.js';
55
import { estimateTokens } from './util.js';
6-
import { join } from 'path';
76
import { randomUUID } from 'crypto';
87
import type { SessionEvent, ToolResultEvent, CompactEvent } from '../session/types.js';
98

@@ -23,6 +22,7 @@ export interface BuildResult {
2322
compactedEvents: SessionEvent[];
2423
promptEstimate: number;
2524
currentTurnId: number;
25+
compactedTurnIds: Set<number>;
2626
}
2727

2828
export function assemblePayload(
@@ -31,16 +31,15 @@ export function assemblePayload(
3131
config: ContextConfig,
3232
contextWindow: number = 128000
3333
): BuildResult {
34-
const dir = resolveSessionDir(sessionId);
35-
if (!dir) throw new Error(`Session ${sessionId} not found`);
36-
const jsonlPath = join(dir, `${sessionId}.jsonl`);
34+
const jsonlPath = resolveSessionJsonlPath(sessionId);
3735
let events = readHistory(jsonlPath);
3836

3937
const idx = findSessionIndex(sessionId);
4038
const currentTurnId = idx?.currentTurnId ?? 0;
4139

42-
const { hidden } = applyVisibilityEvents(events);
40+
const { hidden, compactedTurnIds: initialCompactedTurnIds } = applyVisibilityEvents(events);
4341
let visible = filterVisible(events, hidden);
42+
let compactedTurnIds = initialCompactedTurnIds;
4443

4544
const preEstimate = estimateTokensFromEvents(visible);
4645

@@ -57,6 +56,7 @@ export function assemblePayload(
5756
events = readHistory(jsonlPath);
5857
const updated = applyVisibilityEvents(events);
5958
visible = filterVisible(events, updated.hidden);
59+
compactedTurnIds = updated.compactedTurnIds;
6060
}
6161

6262
const messages = buildMessagesFromEvents(visible);
@@ -65,6 +65,7 @@ export function assemblePayload(
6565
compactedEvents: visible,
6666
promptEstimate: estimateTokens(messages),
6767
currentTurnId,
68+
compactedTurnIds,
6869
};
6970
}
7071

packages/codingcode/src/session/io.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ export function resolveSessionDir(sessionId: string): string | null {
5959
return null;
6060
}
6161

62+
export function resolveSessionJsonlPath(sessionId: string): string {
63+
const dir = resolveSessionDir(sessionId);
64+
if (!dir) throw new Error(`Session ${sessionId} not found`);
65+
return join(dir, `${sessionId}.jsonl`);
66+
}
67+
6268
export function ensureDirs(transcriptPath: string): void {
6369
if (!existsSync(CODINGCODE_DIR)) mkdirSync(CODINGCODE_DIR, { recursive: true });
6470
const dir = dirname(transcriptPath);

packages/codingcode/src/session/messages.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,12 @@ export function applyVisibilityEvents(events: SessionEvent[]): VisibilityResult
6969
return { hidden, compactedTurnIds };
7070
}
7171

72-
export function buildMessagesFromEvents(events: SessionEvent[]): Message[] {
73-
const { hidden, compactedTurnIds } = applyVisibilityEvents(events);
72+
export function buildMessagesFromEvents(
73+
events: SessionEvent[],
74+
externalCompactedTurnIds?: Set<number>,
75+
): Message[] {
76+
const { hidden, compactedTurnIds: derivedIds } = applyVisibilityEvents(events);
77+
const compactedTurnIds = externalCompactedTurnIds ?? derivedIds;
7478

7579
const visible: SessionEvent[] = [];
7680
for (const ev of events) {

packages/codingcode/test/context/compressor/compact-if-needed.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ const { mockCompactWithLLM, mockLLM } = vi.hoisted(() => ({
2828

2929
vi.mock('../../../src/session/io.js', async (importOriginal) => {
3030
const actual = await importOriginal();
31+
const mockResolveSessionDir = vi.fn(() => '/tmp/sessions');
3132
return {
3233
...(actual as any),
3334
findSessionIndex: vi.fn(() => ({ currentTurnId: 10 })),
34-
resolveSessionDir: vi.fn(() => '/tmp/sessions'),
35+
resolveSessionDir: mockResolveSessionDir,
36+
resolveSessionJsonlPath: vi.fn((sessionId: string) => {
37+
const dir = mockResolveSessionDir(sessionId);
38+
if (!dir) throw new Error(`Session ${sessionId} not found`);
39+
return `${dir}/${sessionId}.jsonl`;
40+
}),
3541
readHistory: vi.fn(() => [
3642
{ type: 'user', content: 'a'.repeat(200), uuid: 'u1', turnId: 1 },
3743
{ type: 'assistant', content: 'b'.repeat(200), uuid: 'a1', turnId: 1 },

0 commit comments

Comments
 (0)