summaryrefslogtreecommitdiff
path: root/packages/core/src/tools
diff options
context:
space:
mode:
authorjoshualitt <[email protected]>2025-08-06 13:52:04 -0700
committerGitHub <[email protected]>2025-08-06 20:52:04 +0000
commit43510ed212ea29b7bd752277de525f7821551b22 (patch)
treeb49f31ec75804e2b742b2651714066211421c3ab /packages/core/src/tools
parentad5d2af4e34fd23391bb6a0270cc320a0e56ba88 (diff)
bug(core): Prompt engineering for truncated read_file. (#5161)
Diffstat (limited to 'packages/core/src/tools')
-rw-r--r--packages/core/src/tools/read-file.test.ts27
-rw-r--r--packages/core/src/tools/read-file.ts24
-rw-r--r--packages/core/src/tools/read-many-files.test.ts28
-rw-r--r--packages/core/src/tools/read-many-files.ts12
4 files changed, 74 insertions, 17 deletions
diff --git a/packages/core/src/tools/read-file.test.ts b/packages/core/src/tools/read-file.test.ts
index bb9317fd..8c11afab 100644
--- a/packages/core/src/tools/read-file.test.ts
+++ b/packages/core/src/tools/read-file.test.ts
@@ -222,7 +222,7 @@ describe('ReadFileTool', () => {
});
});
- it('should pass offset and limit to read a slice of a text file', async () => {
+ it('should return a structured message when a slice of a text file is read', async () => {
const filePath = path.join(tempRootDir, 'paginated.txt');
const fileContent = Array.from(
{ length: 20 },
@@ -240,15 +240,22 @@ describe('ReadFileTool', () => {
ToolResult
>;
- expect(await invocation.execute(abortSignal)).toEqual({
- llmContent: [
- '[File content truncated: showing lines 6-8 of 20 total lines. Use offset/limit parameters to view more.]',
- 'Line 6',
- 'Line 7',
- 'Line 8',
- ].join('\n'),
- returnDisplay: 'Read lines 6-8 of 20 from paginated.txt',
- });
+ const result = await invocation.execute(abortSignal);
+
+ const expectedLlmContent = `
+IMPORTANT: The file content has been truncated.
+Status: Showing lines 6-8 of 20 total lines.
+Action: To read more of the file, you can use the 'offset' and 'limit' parameters in a subsequent 'read_file' call. For example, to read the next section of the file, use offset: 8.
+
+--- FILE CONTENT (truncated) ---
+Line 6
+Line 7
+Line 8`;
+
+ expect(result.llmContent).toEqual(expectedLlmContent);
+ expect(result.returnDisplay).toBe(
+ 'Read lines 6-8 of 20 from paginated.txt',
+ );
});
describe('with .geminiignore', () => {
diff --git a/packages/core/src/tools/read-file.ts b/packages/core/src/tools/read-file.ts
index 3a05da06..7ef9d2b5 100644
--- a/packages/core/src/tools/read-file.ts
+++ b/packages/core/src/tools/read-file.ts
@@ -14,7 +14,7 @@ import {
ToolLocation,
ToolResult,
} from './tools.js';
-import { Type } from '@google/genai';
+import { PartUnion, Type } from '@google/genai';
import {
processSingleFileContent,
getSpecificMimeType,
@@ -84,6 +84,24 @@ class ReadFileToolInvocation
};
}
+ let llmContent: PartUnion;
+ if (result.isTruncated) {
+ const [start, end] = result.linesShown!;
+ const total = result.originalLineCount!;
+ const nextOffset = this.params.offset
+ ? this.params.offset + end - start + 1
+ : end;
+ llmContent = `
+IMPORTANT: The file content has been truncated.
+Status: Showing lines ${start}-${end} of ${total} total lines.
+Action: To read more of the file, you can use the 'offset' and 'limit' parameters in a subsequent 'read_file' call. For example, to read the next section of the file, use offset: ${nextOffset}.
+
+--- FILE CONTENT (truncated) ---
+${result.llmContent}`;
+ } else {
+ llmContent = result.llmContent || '';
+ }
+
const lines =
typeof result.llmContent === 'string'
? result.llmContent.split('\n').length
@@ -98,7 +116,7 @@ class ReadFileToolInvocation
);
return {
- llmContent: result.llmContent || '',
+ llmContent,
returnDisplay: result.returnDisplay || '',
};
}
@@ -117,7 +135,7 @@ export class ReadFileTool extends BaseDeclarativeTool<
super(
ReadFileTool.Name,
'ReadFile',
- 'Reads and returns the content of a specified file from the local filesystem. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges.',
+ `Reads and returns the content of a specified file. If the file is large, the content will be truncated. The tool's response will clearly indicate if truncation has occurred and will provide details on how to read more of the file using the 'offset' and 'limit' parameters. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges.`,
Icon.FileSearch,
{
properties: {
diff --git a/packages/core/src/tools/read-many-files.test.ts b/packages/core/src/tools/read-many-files.test.ts
index 6ddd2a08..4035a6b7 100644
--- a/packages/core/src/tools/read-many-files.test.ts
+++ b/packages/core/src/tools/read-many-files.test.ts
@@ -476,6 +476,34 @@ describe('ReadManyFilesTool', () => {
fs.rmSync(tempDir1, { recursive: true, force: true });
fs.rmSync(tempDir2, { recursive: true, force: true });
});
+
+ it('should add a warning for truncated files', async () => {
+ createFile('file1.txt', 'Content1');
+ // Create a file that will be "truncated" by making it long
+ const longContent = Array.from({ length: 2500 }, (_, i) => `L${i}`).join(
+ '\n',
+ );
+ createFile('large-file.txt', longContent);
+
+ const params = { paths: ['*.txt'] };
+ const result = await tool.execute(params, new AbortController().signal);
+ const content = result.llmContent as string[];
+
+ const normalFileContent = content.find((c) => c.includes('file1.txt'));
+ const truncatedFileContent = content.find((c) =>
+ c.includes('large-file.txt'),
+ );
+
+ expect(normalFileContent).not.toContain(
+ '[WARNING: This file was truncated.',
+ );
+ expect(truncatedFileContent).toContain(
+ "[WARNING: This file was truncated. To view the full content, use the 'read_file' tool on this specific file.]",
+ );
+ // Check that the actual content is still there but truncated
+ expect(truncatedFileContent).toContain('L200');
+ expect(truncatedFileContent).not.toContain('L2400');
+ });
});
describe('Batch Processing', () => {
diff --git a/packages/core/src/tools/read-many-files.ts b/packages/core/src/tools/read-many-files.ts
index 1fa2e15c..a380ea91 100644
--- a/packages/core/src/tools/read-many-files.ts
+++ b/packages/core/src/tools/read-many-files.ts
@@ -524,11 +524,15 @@ Use this tool when the user's query implies needing the content of several files
'{filePath}',
filePath,
);
- contentParts.push(
- `${separator}\n\n${fileReadResult.llmContent}\n\n`,
- );
+ let fileContentForLlm = '';
+ if (fileReadResult.isTruncated) {
+ fileContentForLlm += `[WARNING: This file was truncated. To view the full content, use the 'read_file' tool on this specific file.]\n\n`;
+ }
+ fileContentForLlm += fileReadResult.llmContent;
+ contentParts.push(`${separator}\n\n${fileContentForLlm}\n\n`);
} else {
- contentParts.push(fileReadResult.llmContent); // This is a Part for image/pdf
+ // This is a Part for image/pdf, which we don't add the separator to.
+ contentParts.push(fileReadResult.llmContent);
}
processedFilesRelativePaths.push(relativePathForDisplay);