Skip to content

Commit 544d450

Browse files
committed
current progress
1 parent a2572ea commit 544d450

File tree

10 files changed

+310
-241
lines changed

10 files changed

+310
-241
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/Cline.ts

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ export class Cline {
11401140
const contextWindow = this.api.getModel().info.contextWindow || 64_000 // minimum context (Deepseek)
11411141
const maxAllowedSize = getMaxAllowedSize(contextWindow)
11421142

1143-
const terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)
1143+
const terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd, contextWindow)
11441144
terminalInfo.terminal.show() // weird visual bug when creating new terminals (even manually) where there's an empty space at the top.
11451145
const process = this.terminalManager.runCommand(terminalInfo, command, maxAllowedSize)
11461146

@@ -1188,7 +1188,7 @@ export class Cline {
11881188
// the correct order of messages (although the webview is smart about
11891189
// grouping command_output messages despite any gaps anyways)
11901190
await delay(50)
1191-
1191+
// AKF TODO -> investigate failed results
11921192
result = result.trim()
11931193

11941194
if (userFeedback) {
@@ -1314,10 +1314,6 @@ export class Cline {
13141314
const { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse(previousRequest.text)
13151315
const totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)
13161316
let contextWindow = this.api.getModel().info.contextWindow || 64_000 // minimum context (Deepseek)
1317-
// FIXME: hack to get anyone using openai compatible with deepseek to have the proper context window instead of the default 128k. We need a way for the user to specify the context window for models they input through openai compatible
1318-
if (this.api instanceof OpenAiHandler && this.api.getModel().id.toLowerCase().includes("deepseek")) {
1319-
contextWindow = 64_000
1320-
}
13211317
const maxAllowedSize = getMaxAllowedSize(contextWindow)
13221318

13231319
// This is the most reliable way to know when we're close to hitting the context window.
@@ -1964,17 +1960,15 @@ export class Cline {
19641960
}
19651961
// Get context window and used context from API model
19661962
const contextWindow = this.api.getModel().info.contextWindow || 64_000 // minimum context (Deepseek)
1967-
const maxAllowedSize = getMaxAllowedSize(contextWindow)
19681963

1969-
// now execute the tool like normal
1970-
const content = await extractTextFromFile(absolutePath, maxAllowedSize)
1964+
// Pass the raw context window size - extractTextFromFile will calculate the appropriate limit
1965+
const content = await extractTextFromFile(absolutePath, contextWindow)
19711966
pushToolResult(content)
19721967

19731968
break
19741969
}
19751970
} catch (error) {
19761971
await handleError("reading file", error)
1977-
19781972
break
19791973
}
19801974
}
@@ -2969,6 +2963,16 @@ export class Cline {
29692963
throw new Error("Cline instance aborted")
29702964
}
29712965

2966+
// Log file content being passed to model
2967+
userContent.forEach((block) => {
2968+
if (block.type === "text" && block.text) {
2969+
// Look for file content markers
2970+
if (block.text.includes("<file_content") || block.text.includes("<final_file_content")) {
2971+
console.log(`[MODEL_INPUT] File content being passed to model. Content length: ${block.text.length} chars`)
2972+
}
2973+
}
2974+
})
2975+
29722976
if (this.consecutiveMistakeCount >= 3) {
29732977
if (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {
29742978
showSystemNotification({
@@ -3438,33 +3442,44 @@ export class Cline {
34383442
if (busyTerminals.length > 0) {
34393443
// terminals are cool, let's retrieve their output
34403444
terminalDetails += "\n\n# Actively Running Terminals"
3441-
for (const busyTerminal of busyTerminals) {
3442-
terminalDetails += `\n## Original command: \`${busyTerminal.lastCommand}\``
3443-
const newOutput = this.terminalManager.getUnretrievedOutput(busyTerminal.id)
3444-
if (newOutput) {
3445-
terminalDetails += `\n### New Output\n${newOutput}`
3446-
} else {
3447-
// details += `\n(Still running, no new output)` // don't want to show this right after running the command
3445+
const contextWindow = this.api.getModel().info.contextWindow || 64_000 // minimum context (Deepseek)
3446+
3447+
// Get output from all busy terminals
3448+
const busyOutputs = await Promise.all(
3449+
busyTerminals.map(async (terminal) => {
3450+
const output = await this.terminalManager.getUnretrievedOutput(terminal.id, contextWindow)
3451+
return { terminal, output }
3452+
}),
3453+
)
3454+
3455+
// Add output to details
3456+
for (const { terminal, output } of busyOutputs) {
3457+
terminalDetails += `\n## Original command: \`${terminal.lastCommand}\``
3458+
if (output) {
3459+
terminalDetails += `\n### New Output\n${output}`
34483460
}
34493461
}
34503462
}
3463+
34513464
// only show inactive terminals if there's output to show
34523465
if (inactiveTerminals.length > 0) {
3453-
const inactiveTerminalOutputs = new Map<number, string>()
3454-
for (const inactiveTerminal of inactiveTerminals) {
3455-
const newOutput = this.terminalManager.getUnretrievedOutput(inactiveTerminal.id)
3456-
if (newOutput) {
3457-
inactiveTerminalOutputs.set(inactiveTerminal.id, newOutput)
3458-
}
3459-
}
3460-
if (inactiveTerminalOutputs.size > 0) {
3466+
const contextWindow = this.api.getModel().info.contextWindow || 64_000 // minimum context (Deepseek)
3467+
3468+
// Get output from all inactive terminals
3469+
const inactiveOutputs = await Promise.all(
3470+
inactiveTerminals.map(async (terminal) => {
3471+
const output = await this.terminalManager.getUnretrievedOutput(terminal.id, contextWindow)
3472+
return { terminal, output }
3473+
}),
3474+
)
3475+
3476+
// Filter and add outputs that have content
3477+
const outputsWithContent = inactiveOutputs.filter(({ output }) => output)
3478+
if (outputsWithContent.length > 0) {
34613479
terminalDetails += "\n\n# Inactive Terminals"
3462-
for (const [terminalId, newOutput] of inactiveTerminalOutputs) {
3463-
const inactiveTerminal = inactiveTerminals.find((t) => t.id === terminalId)
3464-
if (inactiveTerminal) {
3465-
terminalDetails += `\n## ${inactiveTerminal.lastCommand}`
3466-
terminalDetails += `\n### New Output\n${newOutput}`
3467-
}
3480+
for (const { terminal, output } of outputsWithContent) {
3481+
terminalDetails += `\n## ${terminal.lastCommand}`
3482+
terminalDetails += `\n### New Output\n${output}`
34683483
}
34693484
}
34703485
}

src/core/mentions/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ export async function parseMentions(
117117
}
118118
} else if (mention === "terminal") {
119119
try {
120-
const terminalOutput = await getLatestTerminalOutput()
120+
const contextWindow = api.getModel().info.contextWindow || 64_000 // minimum context (Deepseek)
121+
const terminalOutput = await getLatestTerminalOutput(contextWindow)
121122
parsedText += `\n\n<terminal_output>\n${terminalOutput}\n</terminal_output>`
122123
} catch (error) {
123124
parsedText += `\n\n<terminal_output>\nError fetching terminal output: ${error.message}\n</terminal_output>`

src/integrations/misc/extract-text.ts

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,99 @@ import pdf from "pdf-parse/lib/pdf-parse"
44
import mammoth from "mammoth"
55
import fs from "fs/promises"
66
import { isBinaryFile } from "isbinaryfile"
7-
import { estimateFileSize, wouldExceedSizeLimit } from "../../utils/content-size"
7+
import { estimateContentSize, estimateFileSize, wouldExceedSizeLimit, getMaxAllowedSize } from "../../utils/content-size"
88
import { ContentTooLargeError } from "../../shared/errors"
99

10-
export async function extractTextFromFile(filePath: string, contextLimit: number): Promise<string> {
10+
/**
11+
* Checks if terminal output would exceed size limits and returns the content if safe
12+
* @param content The terminal output content to check
13+
* @param contextWindow Context window limit in tokens
14+
* @param command The command that generated this output (for error reporting)
15+
* @returns The validated content
16+
* @throws ContentTooLargeError if content exceeds size limit
17+
*/
18+
export async function extractTextFromTerminal(content: string | Buffer, contextWindow: number, command: string): Promise<string> {
19+
console.log(`[TERMINAL_SIZE_CHECK] Checking size for command output: ${command}`)
20+
21+
// Convert to string but don't trim yet
22+
const rawContent = content.toString()
23+
console.log(`[TERMINAL_SIZE_CHECK] Raw content length: ${rawContent.length}`)
24+
25+
// Check size before trimming
26+
const sizeEstimate = estimateContentSize(rawContent, contextWindow)
27+
console.log(`[TERMINAL_SIZE_CHECK] Content size: ${sizeEstimate.bytes} bytes`)
28+
console.log(`[TERMINAL_SIZE_CHECK] Estimated tokens: ${sizeEstimate.estimatedTokens}`)
29+
console.log(`[TERMINAL_SIZE_CHECK] Context window: ${contextWindow}`)
30+
31+
if (sizeEstimate.wouldExceedLimit) {
32+
console.log(`[TERMINAL_SIZE_CHECK] Output exceeds size limit`)
33+
throw new ContentTooLargeError({
34+
type: "terminal",
35+
command,
36+
size: sizeEstimate,
37+
})
38+
}
39+
40+
// Only trim after size check passes
41+
const cleanContent = rawContent.trim()
42+
console.log(`[TERMINAL_SIZE_CHECK] Clean content length: ${cleanContent.length}`)
43+
console.log(`[TERMINAL_SIZE_CHECK] Size check passed`)
44+
return cleanContent
45+
}
46+
47+
export async function extractTextFromFile(filePath: string, contextWindow: number): Promise<string> {
1148
try {
1249
await fs.access(filePath)
1350
} catch (error) {
1451
throw new Error(`File not found: ${filePath}`)
1552
}
1653

54+
console.log(`[FILE_READ_CHECK] Checking size for file: ${filePath}`)
55+
1756
// Get file stats to check size
1857
const stats = await fs.stat(filePath)
58+
console.log(`[FILE_SIZE_CHECK] File size: ${stats.size} bytes`)
59+
60+
// Calculate max allowed size from context window
61+
const maxAllowedSize = getMaxAllowedSize(contextWindow)
62+
console.log(`[FILE_SIZE_CHECK] Max allowed size: ${maxAllowedSize} tokens`)
1963

2064
// Check if file size would exceed limit before attempting to read
2165
// This is more efficient than creating a full SizeEstimate object when we just need a boolean check
22-
if (wouldExceedSizeLimit(stats.size, contextLimit)) {
66+
if (wouldExceedSizeLimit(stats.size, contextWindow)) {
67+
console.log(`[FILE_SIZE_CHECK] File exceeds size limit`)
2368
// Only create the full size estimate when we need it for the error
24-
const sizeEstimate = await estimateFileSize(filePath, contextLimit)
69+
const sizeEstimate = await estimateFileSize(filePath, maxAllowedSize)
2570
throw new ContentTooLargeError({
2671
type: "file",
2772
path: filePath,
2873
size: sizeEstimate,
2974
})
3075
}
76+
console.log(`[FILE_SIZE_CHECK] File size check passed`)
3177
const fileExtension = path.extname(filePath).toLowerCase()
78+
console.log(`[FILE_READ] Reading file: ${filePath}`)
79+
let content: string
3280
switch (fileExtension) {
3381
case ".pdf":
34-
return extractTextFromPDF(filePath)
82+
content = await extractTextFromPDF(filePath)
83+
break
3584
case ".docx":
36-
return extractTextFromDOCX(filePath)
85+
content = await extractTextFromDOCX(filePath)
86+
break
3787
case ".ipynb":
38-
return extractTextFromIPYNB(filePath)
88+
content = await extractTextFromIPYNB(filePath)
89+
break
3990
default:
4091
const isBinary = await isBinaryFile(filePath).catch(() => false)
4192
if (!isBinary) {
42-
return await fs.readFile(filePath, "utf8")
93+
content = await fs.readFile(filePath, "utf8")
4394
} else {
4495
throw new Error(`Cannot read text for file type: ${fileExtension}`)
4596
}
4697
}
98+
console.log(`[FILE_READ_COMPLETE] File read complete. Content length: ${content.length} chars`)
99+
return content
47100
}
48101

49102
async function extractTextFromPDF(filePath: string): Promise<string> {

src/integrations/terminal/TerminalManager.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as vscode from "vscode"
33
import { arePathsEqual } from "../../utils/path"
44
import { mergePromise, TerminalProcess, TerminalProcessResultPromise } from "./TerminalProcess"
55
import { TerminalInfo, TerminalRegistry } from "./TerminalRegistry"
6+
import { extractTextFromTerminal } from "../../integrations/misc/extract-text"
67

78
/*
89
TerminalManager:
@@ -109,7 +110,7 @@ export class TerminalManager {
109110
}
110111
}
111112

112-
runCommand(terminalInfo: TerminalInfo, command: string, contextLimit?: number): TerminalProcessResultPromise {
113+
runCommand(terminalInfo: TerminalInfo, command: string, contextLimit: number): TerminalProcessResultPromise {
113114
terminalInfo.busy = true
114115
terminalInfo.lastCommand = command
115116
const process = new TerminalProcess()
@@ -156,7 +157,7 @@ export class TerminalManager {
156157
return mergePromise(process, promise)
157158
}
158159

159-
async getOrCreateTerminal(cwd: string): Promise<TerminalInfo> {
160+
async getOrCreateTerminal(cwd: string, contextLimit: number): Promise<TerminalInfo> {
160161
const terminals = TerminalRegistry.getAllTerminals()
161162

162163
// Find available terminal from our pool first (created for this task)
@@ -179,7 +180,7 @@ export class TerminalManager {
179180
const availableTerminal = terminals.find((t) => !t.busy)
180181
if (availableTerminal) {
181182
// Navigate back to the desired directory
182-
await this.runCommand(availableTerminal, `cd "${cwd}"`)
183+
await this.runCommand(availableTerminal, `cd "${cwd}"`, contextLimit)
183184
this.terminalIds.add(availableTerminal.id)
184185
return availableTerminal
185186
}
@@ -197,12 +198,19 @@ export class TerminalManager {
197198
.map((t) => ({ id: t.id, lastCommand: t.lastCommand }))
198199
}
199200

200-
getUnretrievedOutput(terminalId: number): string {
201+
async getUnretrievedOutput(terminalId: number, contextLimit: number): Promise<string> {
201202
if (!this.terminalIds.has(terminalId)) {
202203
return ""
203204
}
204205
const process = this.processes.get(terminalId)
205-
return process ? process.getUnretrievedOutput() : ""
206+
if (!process) {
207+
return ""
208+
}
209+
const output = process.getUnretrievedOutput()
210+
if (!output) {
211+
return ""
212+
}
213+
return await extractTextFromTerminal(output, contextLimit, `Terminal ${terminalId} output`)
206214
}
207215

208216
isProcessHot(terminalId: number): boolean {

0 commit comments

Comments
 (0)