mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: add provider-specific file reading limits (maxReadFileLine)
Add a per-provider maxReadFileLine setting that allows users to override the default 2000-line limit when reading files. This helps users with slower local providers (e.g. CPU-based inference) avoid timeouts by reducing prompt size. Changes: - Add maxReadFileLine to baseProviderSettingsSchema (packages/types) - Update createReadFileTool() to reflect the effective limit in tool description - Thread the setting through getNativeTools() and buildNativeToolsArray() - Enforce the limit at execution time in ReadFileTool.processTextFile() - Apply the limit to @ mention file reads via parseMentions chain - Add MaxReadFileLineControl UI component in provider advanced settings - Add English localization keys for the new setting - Fix affected tests to account for new parameter Closes #11407
This commit is contained in:
parent
dcb33c47ad
commit
63e3f769ac
12 changed files with 113 additions and 17 deletions
|
|
@ -189,6 +189,9 @@ const baseProviderSettingsSchema = z.object({
|
||||||
|
|
||||||
// Model verbosity.
|
// Model verbosity.
|
||||||
verbosity: verbosityLevelsSchema.optional(),
|
verbosity: verbosityLevelsSchema.optional(),
|
||||||
|
|
||||||
|
// File reading limits.
|
||||||
|
maxReadFileLine: z.number().int().min(1).optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Several of the providers share common model config properties.
|
// Several of the providers share common model config properties.
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ describe("processUserContentMentions", () => {
|
||||||
false, // showRooIgnoredFiles should default to false
|
false, // showRooIgnoredFiles should default to false
|
||||||
true, // includeDiagnosticMessages
|
true, // includeDiagnosticMessages
|
||||||
50, // maxDiagnosticMessages
|
50, // maxDiagnosticMessages
|
||||||
|
undefined, // maxReadFileLine
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -251,6 +252,7 @@ describe("processUserContentMentions", () => {
|
||||||
false,
|
false,
|
||||||
true, // includeDiagnosticMessages
|
true, // includeDiagnosticMessages
|
||||||
50, // maxDiagnosticMessages
|
50, // maxDiagnosticMessages
|
||||||
|
undefined, // maxReadFileLine
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,8 @@ export interface ParseMentionsResult {
|
||||||
* Formats file content to look like a read_file tool result.
|
* Formats file content to look like a read_file tool result.
|
||||||
* Includes Gemini-style truncation warning when content is truncated.
|
* Includes Gemini-style truncation warning when content is truncated.
|
||||||
*/
|
*/
|
||||||
function formatFileReadResult(filePath: string, result: ExtractTextResult): string {
|
function formatFileReadResult(filePath: string, result: ExtractTextResult, maxReadFileLine?: number): string {
|
||||||
|
const effectiveLimit = maxReadFileLine ?? DEFAULT_LINE_LIMIT
|
||||||
const header = `[read_file for '${filePath}']`
|
const header = `[read_file for '${filePath}']`
|
||||||
|
|
||||||
if (result.wasTruncated && result.linesShown) {
|
if (result.wasTruncated && result.linesShown) {
|
||||||
|
|
@ -114,7 +115,7 @@ function formatFileReadResult(filePath: string, result: ExtractTextResult): stri
|
||||||
return `${header}
|
return `${header}
|
||||||
IMPORTANT: File content truncated.
|
IMPORTANT: File content truncated.
|
||||||
Status: Showing lines ${start}-${end} of ${result.totalLines} total lines.
|
Status: Showing lines ${start}-${end} of ${result.totalLines} total lines.
|
||||||
To read more: Use the read_file tool with offset=${nextOffset} and limit=${DEFAULT_LINE_LIMIT}.
|
To read more: Use the read_file tool with offset=${nextOffset} and limit=${effectiveLimit}.
|
||||||
|
|
||||||
File: ${filePath}
|
File: ${filePath}
|
||||||
${result.content}`
|
${result.content}`
|
||||||
|
|
@ -134,6 +135,7 @@ export async function parseMentions(
|
||||||
showRooIgnoredFiles: boolean = false,
|
showRooIgnoredFiles: boolean = false,
|
||||||
includeDiagnosticMessages: boolean = true,
|
includeDiagnosticMessages: boolean = true,
|
||||||
maxDiagnosticMessages: number = 50,
|
maxDiagnosticMessages: number = 50,
|
||||||
|
maxReadFileLine?: number,
|
||||||
): Promise<ParseMentionsResult> {
|
): Promise<ParseMentionsResult> {
|
||||||
const mentions: Set<string> = new Set()
|
const mentions: Set<string> = new Set()
|
||||||
const validCommands: Map<string, Command> = new Map()
|
const validCommands: Map<string, Command> = new Map()
|
||||||
|
|
@ -249,6 +251,7 @@ export async function parseMentions(
|
||||||
rooIgnoreController,
|
rooIgnoreController,
|
||||||
showRooIgnoredFiles,
|
showRooIgnoredFiles,
|
||||||
fileContextTracker,
|
fileContextTracker,
|
||||||
|
maxReadFileLine,
|
||||||
)
|
)
|
||||||
contentBlocks.push(fileResult)
|
contentBlocks.push(fileResult)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -331,6 +334,7 @@ async function getFileOrFolderContentWithMetadata(
|
||||||
rooIgnoreController?: any,
|
rooIgnoreController?: any,
|
||||||
showRooIgnoredFiles: boolean = false,
|
showRooIgnoredFiles: boolean = false,
|
||||||
fileContextTracker?: FileContextTracker,
|
fileContextTracker?: FileContextTracker,
|
||||||
|
maxReadFileLine?: number,
|
||||||
): Promise<MentionContentBlock> {
|
): Promise<MentionContentBlock> {
|
||||||
const unescapedPath = unescapeSpaces(mentionPath)
|
const unescapedPath = unescapeSpaces(mentionPath)
|
||||||
const absPath = path.resolve(cwd, unescapedPath)
|
const absPath = path.resolve(cwd, unescapedPath)
|
||||||
|
|
@ -358,7 +362,8 @@ async function getFileOrFolderContentWithMetadata(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const result = await extractTextFromFileWithMetadata(absPath)
|
const effectiveLimit = maxReadFileLine ?? DEFAULT_LINE_LIMIT
|
||||||
|
const result = await extractTextFromFileWithMetadata(absPath, effectiveLimit)
|
||||||
|
|
||||||
// Track file context
|
// Track file context
|
||||||
if (fileContextTracker) {
|
if (fileContextTracker) {
|
||||||
|
|
@ -368,7 +373,7 @@ async function getFileOrFolderContentWithMetadata(
|
||||||
return {
|
return {
|
||||||
type: "file",
|
type: "file",
|
||||||
path: mentionPath,
|
path: mentionPath,
|
||||||
content: formatFileReadResult(mentionPath, result),
|
content: formatFileReadResult(mentionPath, result, maxReadFileLine),
|
||||||
metadata: {
|
metadata: {
|
||||||
totalLines: result.totalLines,
|
totalLines: result.totalLines,
|
||||||
returnedLines: result.returnedLines,
|
returnedLines: result.returnedLines,
|
||||||
|
|
@ -415,8 +420,12 @@ async function getFileOrFolderContentWithMetadata(
|
||||||
try {
|
try {
|
||||||
const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false)
|
const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false)
|
||||||
if (!isBinary) {
|
if (!isBinary) {
|
||||||
const result = await extractTextFromFileWithMetadata(absoluteFilePath)
|
const effectiveFolderLimit = maxReadFileLine ?? DEFAULT_LINE_LIMIT
|
||||||
fileReadResults.push(formatFileReadResult(filePath.toPosix(), result))
|
const result = await extractTextFromFileWithMetadata(
|
||||||
|
absoluteFilePath,
|
||||||
|
effectiveFolderLimit,
|
||||||
|
)
|
||||||
|
fileReadResults.push(formatFileReadResult(filePath.toPosix(), result, maxReadFileLine))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Skip files that can't be read
|
// Skip files that can't be read
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ export async function processUserContentMentions({
|
||||||
showRooIgnoredFiles = false,
|
showRooIgnoredFiles = false,
|
||||||
includeDiagnosticMessages = true,
|
includeDiagnosticMessages = true,
|
||||||
maxDiagnosticMessages = 50,
|
maxDiagnosticMessages = 50,
|
||||||
|
maxReadFileLine,
|
||||||
}: {
|
}: {
|
||||||
userContent: Anthropic.Messages.ContentBlockParam[]
|
userContent: Anthropic.Messages.ContentBlockParam[]
|
||||||
cwd: string
|
cwd: string
|
||||||
|
|
@ -45,6 +46,7 @@ export async function processUserContentMentions({
|
||||||
showRooIgnoredFiles?: boolean
|
showRooIgnoredFiles?: boolean
|
||||||
includeDiagnosticMessages?: boolean
|
includeDiagnosticMessages?: boolean
|
||||||
maxDiagnosticMessages?: number
|
maxDiagnosticMessages?: number
|
||||||
|
maxReadFileLine?: number
|
||||||
}): Promise<ProcessUserContentMentionsResult> {
|
}): Promise<ProcessUserContentMentionsResult> {
|
||||||
// Track the first mode found from slash commands
|
// Track the first mode found from slash commands
|
||||||
let commandMode: string | undefined
|
let commandMode: string | undefined
|
||||||
|
|
@ -72,6 +74,7 @@ export async function processUserContentMentions({
|
||||||
showRooIgnoredFiles,
|
showRooIgnoredFiles,
|
||||||
includeDiagnosticMessages,
|
includeDiagnosticMessages,
|
||||||
maxDiagnosticMessages,
|
maxDiagnosticMessages,
|
||||||
|
maxReadFileLine,
|
||||||
)
|
)
|
||||||
// Capture the first mode found
|
// Capture the first mode found
|
||||||
if (!commandMode && result.mode) {
|
if (!commandMode && result.mode) {
|
||||||
|
|
@ -116,6 +119,7 @@ export async function processUserContentMentions({
|
||||||
showRooIgnoredFiles,
|
showRooIgnoredFiles,
|
||||||
includeDiagnosticMessages,
|
includeDiagnosticMessages,
|
||||||
maxDiagnosticMessages,
|
maxDiagnosticMessages,
|
||||||
|
maxReadFileLine,
|
||||||
)
|
)
|
||||||
// Capture the first mode found
|
// Capture the first mode found
|
||||||
if (!commandMode && result.mode) {
|
if (!commandMode && result.mode) {
|
||||||
|
|
@ -166,6 +170,7 @@ export async function processUserContentMentions({
|
||||||
showRooIgnoredFiles,
|
showRooIgnoredFiles,
|
||||||
includeDiagnosticMessages,
|
includeDiagnosticMessages,
|
||||||
maxDiagnosticMessages,
|
maxDiagnosticMessages,
|
||||||
|
maxReadFileLine,
|
||||||
)
|
)
|
||||||
// Capture the first mode found
|
// Capture the first mode found
|
||||||
if (!commandMode && result.mode) {
|
if (!commandMode && result.mode) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ export type { ReadFileToolOptions } from "./read_file"
|
||||||
export interface NativeToolsOptions {
|
export interface NativeToolsOptions {
|
||||||
/** Whether the model supports image processing (default: false) */
|
/** Whether the model supports image processing (default: false) */
|
||||||
supportsImages?: boolean
|
supportsImages?: boolean
|
||||||
|
/** Provider-specific override for the maximum lines returned per read */
|
||||||
|
maxReadFileLine?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,10 +43,11 @@ export interface NativeToolsOptions {
|
||||||
* @returns Array of native tool definitions
|
* @returns Array of native tool definitions
|
||||||
*/
|
*/
|
||||||
export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.ChatCompletionTool[] {
|
export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.ChatCompletionTool[] {
|
||||||
const { supportsImages = false } = options
|
const { supportsImages = false, maxReadFileLine } = options
|
||||||
|
|
||||||
const readFileOptions: ReadFileToolOptions = {
|
const readFileOptions: ReadFileToolOptions = {
|
||||||
supportsImages,
|
supportsImages,
|
||||||
|
maxReadFileLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ function getReadFileSupportsNote(supportsImages: boolean): string {
|
||||||
export interface ReadFileToolOptions {
|
export interface ReadFileToolOptions {
|
||||||
/** Whether the model supports image processing (default: false) */
|
/** Whether the model supports image processing (default: false) */
|
||||||
supportsImages?: boolean
|
supportsImages?: boolean
|
||||||
|
/** Provider-specific override for the maximum lines returned per read (default: DEFAULT_LINE_LIMIT) */
|
||||||
|
maxReadFileLine?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Schema Builder ───────────────────────────────────────────────────────────
|
// ─── Schema Builder ───────────────────────────────────────────────────────────
|
||||||
|
|
@ -58,7 +60,10 @@ export interface ReadFileToolOptions {
|
||||||
* @returns Native tool definition for read_file
|
* @returns Native tool definition for read_file
|
||||||
*/
|
*/
|
||||||
export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Chat.ChatCompletionTool {
|
export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Chat.ChatCompletionTool {
|
||||||
const { supportsImages = false } = options
|
const { supportsImages = false, maxReadFileLine } = options
|
||||||
|
|
||||||
|
// Compute the effective line limit for the tool description
|
||||||
|
const effectiveLineLimit = maxReadFileLine ?? DEFAULT_LINE_LIMIT
|
||||||
|
|
||||||
// Build description based on capabilities
|
// Build description based on capabilities
|
||||||
const descriptionIntro =
|
const descriptionIntro =
|
||||||
|
|
@ -70,7 +75,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.` +
|
` 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.`
|
` 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 = ` By default, returns up to ${effectiveLineLimit} lines per file. Lines longer than ${MAX_LINE_LENGTH} characters are truncated.`
|
||||||
|
|
||||||
const description =
|
const description =
|
||||||
descriptionIntro +
|
descriptionIntro +
|
||||||
|
|
@ -125,7 +130,7 @@ export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Ch
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: "integer",
|
type: "integer",
|
||||||
description: `Maximum number of lines to return (slice mode, default: ${DEFAULT_LINE_LIMIT})`,
|
description: `Maximum number of lines to return (slice mode, default: ${effectiveLineLimit})`,
|
||||||
},
|
},
|
||||||
indentation: {
|
indentation: {
|
||||||
type: "object",
|
type: "object",
|
||||||
|
|
|
||||||
|
|
@ -2800,6 +2800,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
||||||
showRooIgnoredFiles,
|
showRooIgnoredFiles,
|
||||||
includeDiagnosticMessages,
|
includeDiagnosticMessages,
|
||||||
maxDiagnosticMessages,
|
maxDiagnosticMessages,
|
||||||
|
maxReadFileLine: this.apiConfiguration?.maxReadFileLine,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Switch mode if specified in a slash command's frontmatter
|
// Switch mode if specified in a slash command's frontmatter
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ export async function buildNativeToolsArrayWithRestrictions(options: BuildToolsO
|
||||||
// Build native tools with dynamic read_file tool based on settings.
|
// Build native tools with dynamic read_file tool based on settings.
|
||||||
const nativeTools = getNativeTools({
|
const nativeTools = getNativeTools({
|
||||||
supportsImages,
|
supportsImages,
|
||||||
|
maxReadFileLine: apiConfiguration?.maxReadFileLine,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Filter native tools based on mode restrictions.
|
// Filter native tools based on mode restrictions.
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,8 @@ export class ReadFileTool extends BaseTool<"read_file"> {
|
||||||
// (they become U+FFFD replacement characters instead of throwing)
|
// (they become U+FFFD replacement characters instead of throwing)
|
||||||
const buffer = await fs.readFile(fullPath)
|
const buffer = await fs.readFile(fullPath)
|
||||||
const fileContent = buffer.toString("utf-8")
|
const fileContent = buffer.toString("utf-8")
|
||||||
const result = this.processTextFile(fileContent, entry)
|
const providerMaxReadFileLine = task.apiConfiguration?.maxReadFileLine
|
||||||
|
const result = this.processTextFile(fileContent, entry, providerMaxReadFileLine)
|
||||||
|
|
||||||
await task.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
|
await task.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
|
||||||
|
|
||||||
|
|
@ -265,20 +266,30 @@ export class ReadFileTool extends BaseTool<"read_file"> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a text file according to the requested mode.
|
* Process a text file according to the requested mode.
|
||||||
|
*
|
||||||
|
* @param content - The raw file content
|
||||||
|
* @param entry - The parsed file entry parameters
|
||||||
|
* @param providerMaxReadFileLine - Optional provider-level cap on returned lines
|
||||||
*/
|
*/
|
||||||
private processTextFile(content: string, entry: InternalFileEntry): string {
|
private processTextFile(content: string, entry: InternalFileEntry, providerMaxReadFileLine?: number): string {
|
||||||
const mode = entry.mode || "slice"
|
const mode = entry.mode || "slice"
|
||||||
|
const defaultLimit = providerMaxReadFileLine ?? DEFAULT_LINE_LIMIT
|
||||||
|
|
||||||
if (mode === "indentation") {
|
if (mode === "indentation") {
|
||||||
// Indentation mode: semantic block extraction
|
// Indentation mode: semantic block extraction
|
||||||
// When anchor_line is not provided, default to offset (which defaults to 1)
|
// When anchor_line is not provided, default to offset (which defaults to 1)
|
||||||
const anchorLine = entry.anchor_line ?? entry.offset ?? 1
|
const anchorLine = entry.anchor_line ?? entry.offset ?? 1
|
||||||
|
// Clamp the limit: if the provider has a max, enforce it even when the model requests more
|
||||||
|
const requestedLimit = entry.limit ?? defaultLimit
|
||||||
|
const effectiveLimit = providerMaxReadFileLine
|
||||||
|
? Math.min(requestedLimit, providerMaxReadFileLine)
|
||||||
|
: requestedLimit
|
||||||
const result = readWithIndentation(content, {
|
const result = readWithIndentation(content, {
|
||||||
anchorLine,
|
anchorLine,
|
||||||
maxLevels: entry.max_levels,
|
maxLevels: entry.max_levels,
|
||||||
includeSiblings: entry.include_siblings,
|
includeSiblings: entry.include_siblings,
|
||||||
includeHeader: entry.include_header,
|
includeHeader: entry.include_header,
|
||||||
limit: entry.limit ?? DEFAULT_LINE_LIMIT,
|
limit: effectiveLimit,
|
||||||
maxLines: entry.max_lines,
|
maxLines: entry.max_lines,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -287,7 +298,6 @@ export class ReadFileTool extends BaseTool<"read_file"> {
|
||||||
if (result.wasTruncated && result.includedRanges.length > 0) {
|
if (result.wasTruncated && result.includedRanges.length > 0) {
|
||||||
const [start, end] = result.includedRanges[0]
|
const [start, end] = result.includedRanges[0]
|
||||||
const nextOffset = end + 1
|
const nextOffset = end + 1
|
||||||
const effectiveLimit = entry.limit ?? DEFAULT_LINE_LIMIT
|
|
||||||
// Put truncation warning at TOP (before content) to match @ mention format
|
// Put truncation warning at TOP (before content) to match @ mention format
|
||||||
output = `IMPORTANT: File content truncated.
|
output = `IMPORTANT: File content truncated.
|
||||||
Status: Showing lines ${start}-${end} of ${result.totalLines} total lines.
|
Status: Showing lines ${start}-${end} of ${result.totalLines} total lines.
|
||||||
|
|
@ -306,7 +316,11 @@ export class ReadFileTool extends BaseTool<"read_file"> {
|
||||||
// NOTE: read_file offset is 1-based externally; convert to 0-based for readWithSlice.
|
// NOTE: read_file offset is 1-based externally; convert to 0-based for readWithSlice.
|
||||||
const offset1 = entry.offset ?? 1
|
const offset1 = entry.offset ?? 1
|
||||||
const offset0 = Math.max(0, offset1 - 1)
|
const offset0 = Math.max(0, offset1 - 1)
|
||||||
const limit = entry.limit ?? DEFAULT_LINE_LIMIT
|
// Clamp the limit: if the provider has a max, enforce it even when the model requests more
|
||||||
|
const requestedSliceLimit = entry.limit ?? defaultLimit
|
||||||
|
const limit = providerMaxReadFileLine
|
||||||
|
? Math.min(requestedSliceLimit, providerMaxReadFileLine)
|
||||||
|
: requestedSliceLimit
|
||||||
|
|
||||||
const result = readWithSlice(content, offset0, limit)
|
const result = readWithSlice(content, offset0, limit)
|
||||||
|
|
||||||
|
|
@ -786,8 +800,10 @@ export class ReadFileTool extends BaseTool<"read_file"> {
|
||||||
}
|
}
|
||||||
content = selectedLines.join("\n")
|
content = selectedLines.join("\n")
|
||||||
} else {
|
} else {
|
||||||
// Read with default limits using slice mode
|
// Read with default limits using slice mode, clamped by provider setting
|
||||||
const result = readWithSlice(rawContent, 0, DEFAULT_LINE_LIMIT)
|
const providerMaxReadFileLine = task.apiConfiguration?.maxReadFileLine
|
||||||
|
const legacyLimit = providerMaxReadFileLine ?? DEFAULT_LINE_LIMIT
|
||||||
|
const result = readWithSlice(rawContent, 0, legacyLimit)
|
||||||
content = result.content
|
content = result.content
|
||||||
if (result.wasTruncated) {
|
if (result.wasTruncated) {
|
||||||
content += `\n\n[File truncated: showing ${result.returnedLines} of ${result.totalLines} total lines]`
|
content += `\n\n[File truncated: showing ${result.returnedLines} of ${result.totalLines} total lines]`
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ import { Verbosity } from "./Verbosity"
|
||||||
import { TodoListSettingsControl } from "./TodoListSettingsControl"
|
import { TodoListSettingsControl } from "./TodoListSettingsControl"
|
||||||
import { TemperatureControl } from "./TemperatureControl"
|
import { TemperatureControl } from "./TemperatureControl"
|
||||||
import { RateLimitSecondsControl } from "./RateLimitSecondsControl"
|
import { RateLimitSecondsControl } from "./RateLimitSecondsControl"
|
||||||
|
import { MaxReadFileLineControl } from "./MaxReadFileLineControl"
|
||||||
import { ConsecutiveMistakeLimitControl } from "./ConsecutiveMistakeLimitControl"
|
import { ConsecutiveMistakeLimitControl } from "./ConsecutiveMistakeLimitControl"
|
||||||
import { BedrockCustomArn } from "./providers/BedrockCustomArn"
|
import { BedrockCustomArn } from "./providers/BedrockCustomArn"
|
||||||
import { RooBalanceDisplay } from "./providers/RooBalanceDisplay"
|
import { RooBalanceDisplay } from "./providers/RooBalanceDisplay"
|
||||||
|
|
@ -786,6 +787,10 @@ const ApiOptions = ({
|
||||||
value={apiConfiguration.rateLimitSeconds || 0}
|
value={apiConfiguration.rateLimitSeconds || 0}
|
||||||
onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)}
|
onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)}
|
||||||
/>
|
/>
|
||||||
|
<MaxReadFileLineControl
|
||||||
|
value={apiConfiguration.maxReadFileLine}
|
||||||
|
onChange={(value) => setApiConfigurationField("maxReadFileLine", value)}
|
||||||
|
/>
|
||||||
<ConsecutiveMistakeLimitControl
|
<ConsecutiveMistakeLimitControl
|
||||||
value={
|
value={
|
||||||
apiConfiguration.consecutiveMistakeLimit !== undefined
|
apiConfiguration.consecutiveMistakeLimit !== undefined
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { useAppTranslation } from "@/i18n/TranslationContext"
|
||||||
|
|
||||||
|
interface MaxReadFileLineControlProps {
|
||||||
|
value: number | undefined
|
||||||
|
onChange: (value: number | undefined) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MaxReadFileLineControl = ({ value, onChange }: MaxReadFileLineControlProps) => {
|
||||||
|
const { t } = useAppTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<label className="block font-medium mb-1">{t("settings:providers.maxReadFileLine.label")}</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="w-24 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border px-2 py-1 rounded text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||||
|
value={value ?? ""}
|
||||||
|
min={1}
|
||||||
|
max={100000}
|
||||||
|
placeholder="2000"
|
||||||
|
onChange={(e) => {
|
||||||
|
const raw = e.target.value
|
||||||
|
if (raw === "") {
|
||||||
|
onChange(undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newValue = parseInt(raw, 10)
|
||||||
|
if (!isNaN(newValue) && newValue >= 1) {
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>{t("settings:providers.maxReadFileLine.unit")}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-vscode-descriptionForeground">
|
||||||
|
{t("settings:providers.maxReadFileLine.description")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -598,6 +598,11 @@
|
||||||
"label": "Rate limit",
|
"label": "Rate limit",
|
||||||
"description": "Minimum time between API requests."
|
"description": "Minimum time between API requests."
|
||||||
},
|
},
|
||||||
|
"maxReadFileLine": {
|
||||||
|
"label": "Max file read lines",
|
||||||
|
"unit": "lines",
|
||||||
|
"description": "Maximum number of lines returned when reading a file. Lower values reduce prompt size and help slower providers avoid timeouts. Leave empty to use the default (2000)."
|
||||||
|
},
|
||||||
"consecutiveMistakeLimit": {
|
"consecutiveMistakeLimit": {
|
||||||
"label": "Error & Repetition Limit",
|
"label": "Error & Repetition Limit",
|
||||||
"description": "Number of consecutive errors or repeated actions before showing 'Roo is having trouble' dialog. Set to 0 to disable this safety mechanism (it will never trigger).",
|
"description": "Number of consecutive errors or repeated actions before showing 'Roo is having trouble' dialog. Set to 0 to disable this safety mechanism (it will never trigger).",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue