Skip to content

Commit ff04142

Browse files
committed
compact on history prompt
1 parent 1924e10 commit ff04142

File tree

1 file changed

+117
-4
lines changed

1 file changed

+117
-4
lines changed

src/core/Cline.ts

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,10 +1031,123 @@ export class Cline extends EventEmitter<ClineEvents> {
10311031
}
10321032
}
10331033

1034-
// Clean conversation history by:
1035-
// 1. Converting to Anthropic.MessageParam by spreading only the API-required properties
1036-
// 2. Converting image blocks to text descriptions if model doesn't support images
1037-
const cleanConversationHistory = this.apiConversationHistory.map(({ role, content }) => {
1034+
// Apply filtering logic to keep only the most recent environment_detail and file<open> content blocks
1035+
let latestEnvDetailsTs: number | undefined = undefined
1036+
// Key: "filePath" or "filePath:lineRange"
1037+
const latestFileOpenTimestamps = new Map<string, number>()
1038+
const latestWriteToFileTimestamps = new Map<string, number>()
1039+
1040+
// Helper function to extract file path and line range from text blocks
1041+
const extractFileInfo = (text: string): { filePath: string; fileKey: string } | null => {
1042+
try {
1043+
const pathMatch = text.match(/<path>(.*?)<\/path>/)
1044+
if (!pathMatch || !pathMatch[1]) return null
1045+
1046+
const filePath = pathMatch[1]
1047+
const contentMatch = text.match(/<content lines="(.*?)">/)
1048+
const lineRange = contentMatch && contentMatch[1] ? contentMatch[1] : null
1049+
// Use path:lines as key if lines exist, otherwise just path
1050+
const fileKey = lineRange ? `${filePath}:${lineRange}` : filePath
1051+
1052+
return { filePath, fileKey }
1053+
} catch (e) {
1054+
console.error("Failed to parse file info from block:", e)
1055+
return null
1056+
}
1057+
}
1058+
1059+
// Iterate from newest to oldest to find the latest timestamps for blocks
1060+
for (let i = this.apiConversationHistory.length - 1; i >= 0; i--) {
1061+
const message = this.apiConversationHistory[i]
1062+
if (!message.ts) continue // Skip messages without timestamps if any
1063+
if (Array.isArray(message.content)) {
1064+
for (const block of message.content) {
1065+
if (block.type === "text") {
1066+
if (message.role === "user") {
1067+
if (block.text.startsWith("<environment_details>") && latestEnvDetailsTs === undefined) {
1068+
latestEnvDetailsTs = message.ts
1069+
} else if (block.text.startsWith("<file>")) {
1070+
const fileInfo = extractFileInfo(block.text)
1071+
if (fileInfo && !latestFileOpenTimestamps.has(fileInfo.fileKey)) {
1072+
latestFileOpenTimestamps.set(fileInfo.fileKey, message.ts)
1073+
}
1074+
}
1075+
} else if (message.role === "assistant") {
1076+
if (block.text.startsWith("<write_to_file>")) {
1077+
const fileInfo = extractFileInfo(block.text)
1078+
if (fileInfo && !latestWriteToFileTimestamps.has(fileInfo.filePath)) {
1079+
latestWriteToFileTimestamps.set(fileInfo.filePath, message.ts!)
1080+
}
1081+
}
1082+
}
1083+
}
1084+
}
1085+
}
1086+
}
1087+
1088+
// Helper function to filter environment details blocks
1089+
const shouldKeepEnvDetails = (block: Anthropic.TextBlockParam, messageTs?: number): boolean => {
1090+
if (block.text.startsWith("<environment_details>")) {
1091+
return messageTs === latestEnvDetailsTs
1092+
}
1093+
return true
1094+
}
1095+
1096+
// Helper function to filter file blocks
1097+
const shouldKeepFileBlock = (block: Anthropic.TextBlockParam, messageTs?: number): boolean => {
1098+
if (block.text.startsWith("<file>")) {
1099+
const fileInfo = extractFileInfo(block.text)
1100+
if (fileInfo) {
1101+
return messageTs === latestFileOpenTimestamps.get(fileInfo.fileKey)
1102+
}
1103+
}
1104+
return true
1105+
}
1106+
1107+
// Helper function to filter write_to_file blocks
1108+
const shouldKeepWriteToFileBlock = (block: Anthropic.TextBlockParam, messageTs?: number): boolean => {
1109+
if (block.text.startsWith("<write_to_file>")) {
1110+
const fileInfo = extractFileInfo(block.text)
1111+
if (fileInfo) {
1112+
return messageTs === latestWriteToFileTimestamps.get(fileInfo.filePath)
1113+
}
1114+
}
1115+
return true
1116+
}
1117+
1118+
// Create a new history array with filtered content blocks
1119+
const historyWithFilteredBlocks: (Anthropic.MessageParam & { ts?: number })[] = []
1120+
for (const message of this.apiConversationHistory) {
1121+
if (message.role === "user" && Array.isArray(message.content)) {
1122+
const newContent = message.content.filter((block) => {
1123+
if (block.type === "text") {
1124+
return shouldKeepEnvDetails(block, message.ts) && shouldKeepFileBlock(block, message.ts)
1125+
}
1126+
return true // Keep all other block types
1127+
})
1128+
// Only add the message if it still has content
1129+
if (newContent.length > 0) {
1130+
historyWithFilteredBlocks.push({ ...message, content: newContent })
1131+
}
1132+
} else if (message.role === "assistant" && Array.isArray(message.content)) {
1133+
const newContent = message.content.filter((block) => {
1134+
if (block.type === "text") {
1135+
return shouldKeepWriteToFileBlock(block, message.ts)
1136+
}
1137+
return true // Keep all other block types
1138+
})
1139+
// Only add the message if it still has content
1140+
if (newContent.length > 0) {
1141+
historyWithFilteredBlocks.push({ ...message, content: newContent })
1142+
}
1143+
} else {
1144+
// Keep other message types (like non-array content)
1145+
historyWithFilteredBlocks.push(message)
1146+
}
1147+
}
1148+
1149+
// The existing image handling logic should be applied to historyWithFilteredBlocks
1150+
const cleanConversationHistory = historyWithFilteredBlocks.map(({ role, content }) => {
10381151
// Handle array content (could contain image blocks)
10391152
if (Array.isArray(content)) {
10401153
if (!this.api.getModel().info.supportsImages) {

0 commit comments

Comments
 (0)