mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: make read_file optional params nullable for proper strict mode support
Models (especially non-OpenAI) were setting inefficiently low limit values (100-200) on read_file calls instead of using the 2000-line default. Root cause: read_file had strict: true but only required: ['path']. The ensureAllRequired transform forces ALL properties into required at runtime, meaning models MUST provide explicit values for every parameter — they cannot omit optional params like limit, offset, or mode. Fix: Follow the pattern used by other strict: true tools (codebase_search, execute_command, skill, generate_image) — make optional parameters nullable using type: ['integer', 'null'] so models can send null to get defaults. Changes: - mode: type ['string', 'null'] (null → default slice mode) - offset: type ['integer', 'null'] (null → start from line 1) - limit: type ['integer', 'null'] (null → use 2000 default) - indentation: type ['object', 'null'] (null → not used in slice mode) - All indentation sub-properties: nullable types - All properties explicitly listed in required arrays - Updated descriptions to say 'Pass null for default' instead of 'Omit' The ReadFileTool handler already uses || and ?? operators which handle null values identically to undefined, so no handler changes needed. Closes #11191
This commit is contained in:
parent
2d5e633781
commit
6ad9a84ca9
2 changed files with 63 additions and 24 deletions
|
|
@ -33,21 +33,59 @@ describe("createReadFileTool", () => {
|
|||
expect(schema.properties).toHaveProperty("indentation")
|
||||
})
|
||||
|
||||
it("should include mode parameter in schema", () => {
|
||||
it("should include mode parameter in schema with nullable type", () => {
|
||||
const tool = createReadFileTool()
|
||||
const schema = getFunctionDef(tool).parameters as any
|
||||
|
||||
expect(schema.properties).toHaveProperty("mode")
|
||||
expect(schema.properties.mode.type).toEqual(["string", "null"])
|
||||
expect(schema.properties.mode.enum).toContain("slice")
|
||||
expect(schema.properties.mode.enum).toContain("indentation")
|
||||
})
|
||||
|
||||
it("should include offset and limit parameters in schema", () => {
|
||||
it("should include nullable offset and limit parameters in schema", () => {
|
||||
const tool = createReadFileTool()
|
||||
const schema = getFunctionDef(tool).parameters as any
|
||||
|
||||
expect(schema.properties).toHaveProperty("offset")
|
||||
expect(schema.properties.offset.type).toEqual(["integer", "null"])
|
||||
expect(schema.properties).toHaveProperty("limit")
|
||||
expect(schema.properties.limit.type).toEqual(["integer", "null"])
|
||||
})
|
||||
|
||||
it("should have nullable indentation object type", () => {
|
||||
const tool = createReadFileTool()
|
||||
const schema = getFunctionDef(tool).parameters as any
|
||||
|
||||
expect(schema.properties.indentation.type).toEqual(["object", "null"])
|
||||
})
|
||||
|
||||
it("should have nullable indentation sub-properties", () => {
|
||||
const tool = createReadFileTool()
|
||||
const schema = getFunctionDef(tool).parameters as any
|
||||
const indentProps = schema.properties.indentation.properties
|
||||
|
||||
expect(indentProps.anchor_line.type).toEqual(["integer", "null"])
|
||||
expect(indentProps.max_levels.type).toEqual(["integer", "null"])
|
||||
expect(indentProps.include_siblings.type).toEqual(["boolean", "null"])
|
||||
expect(indentProps.include_header.type).toEqual(["boolean", "null"])
|
||||
expect(indentProps.max_lines.type).toEqual(["integer", "null"])
|
||||
})
|
||||
|
||||
it("should require all indentation sub-properties for strict mode", () => {
|
||||
const tool = createReadFileTool()
|
||||
const schema = getFunctionDef(tool).parameters as any
|
||||
const indentation = schema.properties.indentation
|
||||
|
||||
expect(indentation.required).toEqual(
|
||||
expect.arrayContaining([
|
||||
"anchor_line",
|
||||
"max_levels",
|
||||
"include_siblings",
|
||||
"include_header",
|
||||
"max_lines",
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -112,11 +150,11 @@ describe("createReadFileTool", () => {
|
|||
expect(getFunctionDef(tool).strict).toBe(true)
|
||||
})
|
||||
|
||||
it("should require path parameter", () => {
|
||||
it("should require all parameters for strict mode compatibility", () => {
|
||||
const tool = createReadFileTool()
|
||||
const schema = getFunctionDef(tool).parameters as any
|
||||
|
||||
expect(schema.required).toContain("path")
|
||||
expect(schema.required).toEqual(expect.arrayContaining(["path", "mode", "offset", "limit", "indentation"]))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Ch
|
|||
` PREFER indentation mode when you have a specific line number from search results, error messages, or definition lookups - it guarantees complete, syntactically valid code blocks without mid-function truncation.` +
|
||||
` IMPORTANT: Indentation mode requires anchor_line to be useful. Without it, only header content (imports) is returned.`
|
||||
|
||||
const limitNote = ` By default, returns up to ${DEFAULT_LINE_LIMIT} lines per file. Lines longer than ${MAX_LINE_LENGTH} characters are truncated.`
|
||||
const limitNote = ` Default limit is ${DEFAULT_LINE_LIMIT} lines. Pass null for limit to use the default — only specify a value when paginating files larger than ${DEFAULT_LINE_LIMIT} lines. Lines longer than ${MAX_LINE_LENGTH} characters are truncated.`
|
||||
|
||||
const description =
|
||||
descriptionIntro +
|
||||
|
|
@ -83,28 +83,28 @@ export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Ch
|
|||
|
||||
const indentationProperties: Record<string, unknown> = {
|
||||
anchor_line: {
|
||||
type: "integer",
|
||||
type: ["integer", "null"],
|
||||
description:
|
||||
"1-based line number to anchor the extraction. REQUIRED for meaningful indentation mode results. The extractor finds the semantic block (function, method, class) containing this line and returns it completely. Without anchor_line, indentation mode defaults to line 1 and returns only imports/header content. Obtain anchor_line from: search results, error stack traces, definition lookups, codebase_search results, or condensed file summaries (e.g., '14--28 | export class UserService' means anchor_line=14).",
|
||||
},
|
||||
max_levels: {
|
||||
type: "integer",
|
||||
description: `Maximum indentation levels to include above the anchor (indentation mode, 0 = unlimited (default)). Higher values include more parent context.`,
|
||||
type: ["integer", "null"],
|
||||
description: `Maximum indentation levels to include above the anchor (indentation mode, 0 = unlimited (default)). Higher values include more parent context. Pass null for default (unlimited).`,
|
||||
},
|
||||
include_siblings: {
|
||||
type: "boolean",
|
||||
type: ["boolean", "null"],
|
||||
description:
|
||||
"Include sibling blocks at the same indentation level as the anchor block (indentation mode, default: false). Useful for seeing related methods in a class.",
|
||||
"Include sibling blocks at the same indentation level as the anchor block (indentation mode, default: false). Useful for seeing related methods in a class. Pass null for default (false).",
|
||||
},
|
||||
include_header: {
|
||||
type: "boolean",
|
||||
type: ["boolean", "null"],
|
||||
description:
|
||||
"Include file header content (imports, module-level comments) at the top of output (indentation mode, default: true).",
|
||||
"Include file header content (imports, module-level comments) at the top of output (indentation mode, default: true). Pass null for default (true).",
|
||||
},
|
||||
max_lines: {
|
||||
type: "integer",
|
||||
type: ["integer", "null"],
|
||||
description:
|
||||
"Hard cap on lines returned for indentation mode. Acts as a separate limit from the top-level 'limit' parameter.",
|
||||
"Hard cap on lines returned for indentation mode. Acts as a separate limit from the top-level 'limit' parameter. Pass null for no cap.",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -114,25 +114,26 @@ export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Ch
|
|||
description: "Path to the file to read, relative to the workspace",
|
||||
},
|
||||
mode: {
|
||||
type: "string",
|
||||
type: ["string", "null"],
|
||||
enum: ["slice", "indentation"],
|
||||
description:
|
||||
"Reading mode. 'slice' (default): read lines sequentially with offset/limit - use for general file exploration or when you don't have a target line number (may truncate code mid-function). 'indentation': extract complete semantic code blocks containing anchor_line - PREFERRED when you have a line number because it guarantees complete, valid code blocks. WARNING: Do not use indentation mode without specifying indentation.anchor_line, or you will only get header content.",
|
||||
"Reading mode. 'slice' (default): read lines sequentially with offset/limit - use for general file exploration or when you don't have a target line number (may truncate code mid-function). 'indentation': extract complete semantic code blocks containing anchor_line - PREFERRED when you have a line number because it guarantees complete, valid code blocks (ignores offset/limit entirely). WARNING: Do not use indentation mode without specifying indentation.anchor_line, or you will only get header content. Pass null for default (slice).",
|
||||
},
|
||||
offset: {
|
||||
type: "integer",
|
||||
description: "1-based line offset to start reading from (slice mode, default: 1)",
|
||||
type: ["integer", "null"],
|
||||
description:
|
||||
"1-based line offset to start reading from (slice mode, default: 1). Pass null to start from line 1.",
|
||||
},
|
||||
limit: {
|
||||
type: "integer",
|
||||
description: `Maximum number of lines to return (slice mode, default: ${DEFAULT_LINE_LIMIT})`,
|
||||
type: ["integer", "null"],
|
||||
description: `Maximum number of lines to return (slice mode). Default: ${DEFAULT_LINE_LIMIT}. Pass null to use the default — only specify a value when paginating files larger than ${DEFAULT_LINE_LIMIT} lines.`,
|
||||
},
|
||||
indentation: {
|
||||
type: "object",
|
||||
type: ["object", "null"],
|
||||
description:
|
||||
"Indentation mode options. Only used when mode='indentation'. You MUST specify anchor_line for useful results - it determines which code block to extract.",
|
||||
"Indentation mode options. Only used when mode='indentation'. You MUST specify anchor_line for useful results - it determines which code block to extract. Pass null when using slice mode.",
|
||||
properties: indentationProperties,
|
||||
required: [],
|
||||
required: ["anchor_line", "max_levels", "include_siblings", "include_header", "max_lines"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
}
|
||||
|
|
@ -146,7 +147,7 @@ export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Ch
|
|||
parameters: {
|
||||
type: "object",
|
||||
properties,
|
||||
required: ["path"],
|
||||
required: ["path", "mode", "offset", "limit", "indentation"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue