summaryrefslogtreecommitdiff
path: root/packages/core/src/tools/read-file.ts
diff options
context:
space:
mode:
authorTommaso Sciortino <[email protected]>2025-05-30 18:25:47 -0700
committerGitHub <[email protected]>2025-05-30 18:25:47 -0700
commit21fba832d1b4ea7af43fb887d9b2b38fcf8210d0 (patch)
tree7200d2fac3a55c385e0a2dac34b5282c942364bc /packages/core/src/tools/read-file.ts
parentc81148a0cc8489f657901c2cc7247c0834075e1a (diff)
Rename server->core (#638)
Diffstat (limited to 'packages/core/src/tools/read-file.ts')
-rw-r--r--packages/core/src/tools/read-file.ts131
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,
+ };
+ }
+}