diff options
| author | Tommaso Sciortino <[email protected]> | 2025-05-30 18:25:47 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-05-30 18:25:47 -0700 |
| commit | 21fba832d1b4ea7af43fb887d9b2b38fcf8210d0 (patch) | |
| tree | 7200d2fac3a55c385e0a2dac34b5282c942364bc /packages/core/src/tools/read-file.ts | |
| parent | c81148a0cc8489f657901c2cc7247c0834075e1a (diff) | |
Rename server->core (#638)
Diffstat (limited to 'packages/core/src/tools/read-file.ts')
| -rw-r--r-- | packages/core/src/tools/read-file.ts | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/packages/core/src/tools/read-file.ts b/packages/core/src/tools/read-file.ts new file mode 100644 index 00000000..4bb3bd56 --- /dev/null +++ b/packages/core/src/tools/read-file.ts @@ -0,0 +1,131 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import path from 'path'; +import { SchemaValidator } from '../utils/schemaValidator.js'; +import { makeRelative, shortenPath } from '../utils/paths.js'; +import { BaseTool, ToolResult } from './tools.js'; +import { isWithinRoot, processSingleFileContent } from '../utils/fileUtils.js'; + +/** + * Parameters for the ReadFile tool + */ +export interface ReadFileToolParams { + /** + * The absolute path to the file to read + */ + path: string; + + /** + * The line number to start reading from (optional) + */ + offset?: number; + + /** + * The number of lines to read (optional) + */ + limit?: number; +} + +/** + * Implementation of the ReadFile tool logic + */ +export class ReadFileTool extends BaseTool<ReadFileToolParams, ToolResult> { + static readonly Name: string = 'read_file'; + + constructor(private rootDirectory: string) { + 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.', + { + properties: { + path: { + description: + "The absolute path to the file to read (e.g., '/home/user/project/file.txt'). Relative paths are not supported.", + type: 'string', + }, + offset: { + description: + "Optional: For text files, the 0-based line number to start reading from. Requires 'limit' to be set. Use for paginating through large files.", + type: 'number', + }, + limit: { + description: + "Optional: For text files, maximum number of lines to read. Use with 'offset' to paginate through large files. If omitted, reads the entire file (if feasible, up to a default limit).", + type: 'number', + }, + }, + required: ['path'], + type: 'object', + }, + ); + this.rootDirectory = path.resolve(rootDirectory); + } + + validateToolParams(params: ReadFileToolParams): string | null { + if ( + this.schema.parameters && + !SchemaValidator.validate( + this.schema.parameters as Record<string, unknown>, + params, + ) + ) { + return 'Parameters failed schema validation.'; + } + const filePath = params.path; + if (!path.isAbsolute(filePath)) { + return `File path must be absolute: ${filePath}`; + } + if (!isWithinRoot(filePath, this.rootDirectory)) { + return `File path must be within the root directory (${this.rootDirectory}): ${filePath}`; + } + if (params.offset !== undefined && params.offset < 0) { + return 'Offset must be a non-negative number'; + } + if (params.limit !== undefined && params.limit <= 0) { + return 'Limit must be a positive number'; + } + return null; + } + + getDescription(params: ReadFileToolParams): string { + const relativePath = makeRelative(params.path, this.rootDirectory); + return shortenPath(relativePath); + } + + async execute( + params: ReadFileToolParams, + _signal: AbortSignal, + ): Promise<ToolResult> { + const validationError = this.validateToolParams(params); + if (validationError) { + return { + llmContent: `Error: Invalid parameters provided. Reason: ${validationError}`, + returnDisplay: validationError, + }; + } + + const result = await processSingleFileContent( + params.path, + this.rootDirectory, + params.offset, + params.limit, + ); + + if (result.error) { + return { + llmContent: result.error, // The detailed error for LLM + returnDisplay: result.returnDisplay, // User-friendly error + }; + } + + return { + llmContent: result.llmContent, + returnDisplay: result.returnDisplay, + }; + } +} |
