2025-04-19 19:45:42 +01:00
/ * *
* @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' ;
2025-05-29 22:30:18 +00:00
import { isWithinRoot , processSingleFileContent } from '../utils/fileUtils.js' ;
2025-04-19 19:45:42 +01:00
/ * *
* Parameters for the ReadFile tool
* /
export interface ReadFileToolParams {
/ * *
* The absolute path to the file to read
* /
2025-04-20 22:10:23 -04:00
path : string ;
2025-04-19 19:45:42 +01:00
/ * *
* 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
* /
2025-04-21 10:53:11 -04:00
export class ReadFileTool extends BaseTool < ReadFileToolParams , ToolResult > {
2025-04-19 19:45:42 +01:00
static readonly Name : string = 'read_file' ;
2025-05-02 09:31:18 -07:00
constructor ( private rootDirectory : string ) {
2025-04-19 19:45:42 +01:00
super (
2025-04-21 10:53:11 -04:00
ReadFileTool . Name ,
'ReadFile' ,
2025-05-29 22:30:18 +00:00
'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.' ,
2025-04-19 19:45:42 +01:00
{
properties : {
2025-04-20 22:10:23 -04:00
path : {
2025-04-19 19:45:42 +01:00
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 :
2025-05-29 22:30:18 +00:00
"Optional: For text files, the 0-based line number to start reading from. Requires 'limit' to be set. Use for paginating through large files." ,
2025-04-19 19:45:42 +01:00
type : 'number' ,
} ,
limit : {
description :
2025-05-29 22:30:18 +00:00
"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)." ,
2025-04-19 19:45:42 +01:00
type : 'number' ,
} ,
} ,
2025-04-20 22:10:23 -04:00
required : [ 'path' ] ,
2025-04-19 19:45:42 +01:00
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.' ;
}
2025-04-20 22:10:23 -04:00
const filePath = params . path ;
2025-04-19 19:45:42 +01:00
if ( ! path . isAbsolute ( filePath ) ) {
return ` File path must be absolute: ${ filePath } ` ;
}
2025-05-29 22:30:18 +00:00
if ( ! isWithinRoot ( filePath , this . rootDirectory ) ) {
2025-04-19 19:45:42 +01:00
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 {
2025-06-01 00:02:00 -07:00
if (
! params ||
typeof params . path !== 'string' ||
params . path . trim ( ) === ''
) {
return ` Path unavailable ` ;
}
2025-04-20 22:10:23 -04:00
const relativePath = makeRelative ( params . path , this . rootDirectory ) ;
2025-04-19 19:45:42 +01:00
return shortenPath ( relativePath ) ;
}
2025-05-09 23:29:02 -07:00
async execute (
params : ReadFileToolParams ,
_signal : AbortSignal ,
) : Promise < ToolResult > {
2025-04-19 19:45:42 +01:00
const validationError = this . validateToolParams ( params ) ;
if ( validationError ) {
return {
llmContent : ` Error: Invalid parameters provided. Reason: ${ validationError } ` ,
2025-04-20 22:10:23 -04:00
returnDisplay : validationError ,
2025-04-19 19:45:42 +01:00
} ;
}
2025-05-29 22:30:18 +00:00
const result = await processSingleFileContent (
params . path ,
this . rootDirectory ,
params . offset ,
params . limit ,
) ;
2025-04-19 19:45:42 +01:00
2025-05-29 22:30:18 +00:00
if ( result . error ) {
2025-04-19 19:45:42 +01:00
return {
2025-05-29 22:30:18 +00:00
llmContent : result.error , // The detailed error for LLM
returnDisplay : result.returnDisplay , // User-friendly error
2025-04-19 19:45:42 +01:00
} ;
}
2025-05-29 22:30:18 +00:00
return {
llmContent : result.llmContent ,
returnDisplay : result.returnDisplay ,
} ;
2025-04-19 19:45:42 +01:00
}
}