Merge branch 'main' into fix/issue-2379-diagnostics-settings

Resolved conflicts by keeping both diagnostics translations and new includeMaxOutputTokens translations
This commit is contained in:
hannesrudolph 2025-06-16 11:00:42 -06:00
commit eb7317c6c0
49 changed files with 1223 additions and 124 deletions

View file

@ -91,47 +91,6 @@
</step>
<step number="3">
<name>Create Implementation Plan</name>
<instructions>
Based on the issue analysis, create a detailed implementation plan:
For Bug Fixes:
1. Reproduce the bug locally (if possible)
2. Identify root cause
3. Plan the fix approach. The plan should be focused on resolving the issue with a high-quality, targeted fix, while avoiding unrelated changes.
4. Identify files to modify.
5. Plan test cases to prevent regression.
For Feature Implementation:
1. Break down the feature into components
2. Identify all files that need changes
3. Plan the implementation approach
4. Consider edge cases and error handling
5. Plan test coverage
Present the plan to the user:
<ask_followup_question>
<question>I've analyzed issue #[number]: "[title]"
Here's my implementation plan to resolve the issue:
[Detailed plan with steps and affected files]
This plan focuses on providing a quality fix for the reported problem without introducing unrelated changes.
Would you like me to proceed with this implementation?</question>
<follow_up>
<suggest>Yes, proceed with the implementation</suggest>
<suggest>Let me review the issue first</suggest>
<suggest>Modify the approach for: [specific aspect]</suggest>
<suggest>Focus only on: [specific part]</suggest>
</follow_up>
</ask_followup_question>
</instructions>
</step>
<step number="4">
<name>Explore Codebase and Related Files</name>
<instructions>
Use codebase_search FIRST to understand the codebase structure and find ALL related files:
@ -188,6 +147,47 @@
</instructions>
</step>
<step number="4">
<name>Create Implementation Plan</name>
<instructions>
Based on the issue analysis, create a detailed implementation plan:
For Bug Fixes:
1. Reproduce the bug locally (if possible)
2. Identify root cause
3. Plan the fix approach. The plan should be focused on resolving the issue with a high-quality, targeted fix, while avoiding unrelated changes.
4. Identify files to modify.
5. Plan test cases to prevent regression.
For Feature Implementation:
1. Break down the feature into components
2. Identify all files that need changes
3. Plan the implementation approach
4. Consider edge cases and error handling
5. Plan test coverage
Present the plan to the user:
<ask_followup_question>
<question>I've analyzed issue #[number]: "[title]"
Here's my implementation plan to resolve the issue:
[Detailed plan with steps and affected files]
This plan focuses on providing a quality fix for the reported problem without introducing unrelated changes.
Would you like me to proceed with this implementation?</question>
<follow_up>
<suggest>Yes, proceed with the implementation</suggest>
<suggest>Let me review the issue first</suggest>
<suggest>Modify the approach for: [specific aspect]</suggest>
<suggest>Focus only on: [specific part]</suggest>
</follow_up>
</ask_followup_question>
</instructions>
</step>
<step number="5">
<name>Implement the Solution</name>
<instructions>

View file

@ -5,6 +5,7 @@ import { OpenAiHandler } from "../openai"
import { ApiHandlerOptions } from "../../../shared/api"
import { Anthropic } from "@anthropic-ai/sdk"
import OpenAI from "openai"
import { openAiModelInfoSaneDefaults } from "@roo-code/types"
const mockCreate = vitest.fn()
@ -197,6 +198,113 @@ describe("OpenAiHandler", () => {
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.reasoning_effort).toBeUndefined()
})
it("should include max_tokens when includeMaxTokens is true", async () => {
const optionsWithMaxTokens: ApiHandlerOptions = {
...mockOptions,
includeMaxTokens: true,
openAiCustomModelInfo: {
contextWindow: 128_000,
maxTokens: 4096,
supportsPromptCache: false,
},
}
const handlerWithMaxTokens = new OpenAiHandler(optionsWithMaxTokens)
const stream = handlerWithMaxTokens.createMessage(systemPrompt, messages)
// Consume the stream to trigger the API call
for await (const _chunk of stream) {
}
// Assert the mockCreate was called with max_tokens
expect(mockCreate).toHaveBeenCalled()
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.max_completion_tokens).toBe(4096)
})
it("should not include max_tokens when includeMaxTokens is false", async () => {
const optionsWithoutMaxTokens: ApiHandlerOptions = {
...mockOptions,
includeMaxTokens: false,
openAiCustomModelInfo: {
contextWindow: 128_000,
maxTokens: 4096,
supportsPromptCache: false,
},
}
const handlerWithoutMaxTokens = new OpenAiHandler(optionsWithoutMaxTokens)
const stream = handlerWithoutMaxTokens.createMessage(systemPrompt, messages)
// Consume the stream to trigger the API call
for await (const _chunk of stream) {
}
// Assert the mockCreate was called without max_tokens
expect(mockCreate).toHaveBeenCalled()
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.max_completion_tokens).toBeUndefined()
})
it("should not include max_tokens when includeMaxTokens is undefined", async () => {
const optionsWithUndefinedMaxTokens: ApiHandlerOptions = {
...mockOptions,
// includeMaxTokens is not set, should not include max_tokens
openAiCustomModelInfo: {
contextWindow: 128_000,
maxTokens: 4096,
supportsPromptCache: false,
},
}
const handlerWithDefaultMaxTokens = new OpenAiHandler(optionsWithUndefinedMaxTokens)
const stream = handlerWithDefaultMaxTokens.createMessage(systemPrompt, messages)
// Consume the stream to trigger the API call
for await (const _chunk of stream) {
}
// Assert the mockCreate was called without max_tokens
expect(mockCreate).toHaveBeenCalled()
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.max_completion_tokens).toBeUndefined()
})
it("should use user-configured modelMaxTokens instead of model default maxTokens", async () => {
const optionsWithUserMaxTokens: ApiHandlerOptions = {
...mockOptions,
includeMaxTokens: true,
modelMaxTokens: 32000, // User-configured value
openAiCustomModelInfo: {
contextWindow: 128_000,
maxTokens: 4096, // Model's default value (should not be used)
supportsPromptCache: false,
},
}
const handlerWithUserMaxTokens = new OpenAiHandler(optionsWithUserMaxTokens)
const stream = handlerWithUserMaxTokens.createMessage(systemPrompt, messages)
// Consume the stream to trigger the API call
for await (const _chunk of stream) {
}
// Assert the mockCreate was called with user-configured modelMaxTokens (32000), not model default maxTokens (4096)
expect(mockCreate).toHaveBeenCalled()
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.max_completion_tokens).toBe(32000)
})
it("should fallback to model default maxTokens when user modelMaxTokens is not set", async () => {
const optionsWithoutUserMaxTokens: ApiHandlerOptions = {
...mockOptions,
includeMaxTokens: true,
// modelMaxTokens is not set
openAiCustomModelInfo: {
contextWindow: 128_000,
maxTokens: 4096, // Model's default value (should be used as fallback)
supportsPromptCache: false,
},
}
const handlerWithoutUserMaxTokens = new OpenAiHandler(optionsWithoutUserMaxTokens)
const stream = handlerWithoutUserMaxTokens.createMessage(systemPrompt, messages)
// Consume the stream to trigger the API call
for await (const _chunk of stream) {
}
// Assert the mockCreate was called with model default maxTokens (4096) as fallback
expect(mockCreate).toHaveBeenCalled()
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.max_completion_tokens).toBe(4096)
})
})
describe("error handling", () => {
@ -336,6 +444,10 @@ describe("OpenAiHandler", () => {
},
{ path: "/models/chat/completions" },
)
// Verify max_tokens is NOT included when includeMaxTokens is not set
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs).not.toHaveProperty("max_completion_tokens")
})
it("should handle non-streaming responses with Azure AI Inference Service", async () => {
@ -378,6 +490,10 @@ describe("OpenAiHandler", () => {
},
{ path: "/models/chat/completions" },
)
// Verify max_tokens is NOT included when includeMaxTokens is not set
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs).not.toHaveProperty("max_completion_tokens")
})
it("should handle completePrompt with Azure AI Inference Service", async () => {
@ -391,6 +507,10 @@ describe("OpenAiHandler", () => {
},
{ path: "/models/chat/completions" },
)
// Verify max_tokens is NOT included when includeMaxTokens is not set
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs).not.toHaveProperty("max_completion_tokens")
})
})
@ -433,4 +553,225 @@ describe("OpenAiHandler", () => {
expect(lastCall[0]).not.toHaveProperty("stream_options")
})
})
describe("O3 Family Models", () => {
const o3Options = {
...mockOptions,
openAiModelId: "o3-mini",
openAiCustomModelInfo: {
contextWindow: 128_000,
maxTokens: 65536,
supportsPromptCache: false,
reasoningEffort: "medium" as "low" | "medium" | "high",
},
}
it("should handle O3 model with streaming and include max_completion_tokens when includeMaxTokens is true", async () => {
const o3Handler = new OpenAiHandler({
...o3Options,
includeMaxTokens: true,
modelMaxTokens: 32000,
modelTemperature: 0.5,
})
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello!",
},
]
const stream = o3Handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "o3-mini",
messages: [
{
role: "developer",
content: "Formatting re-enabled\nYou are a helpful assistant.",
},
{ role: "user", content: "Hello!" },
],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "medium",
temperature: 0.5,
// O3 models do not support deprecated max_tokens but do support max_completion_tokens
max_completion_tokens: 32000,
}),
{},
)
})
it("should handle O3 model with streaming and exclude max_tokens when includeMaxTokens is false", async () => {
const o3Handler = new OpenAiHandler({
...o3Options,
includeMaxTokens: false,
modelTemperature: 0.7,
})
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello!",
},
]
const stream = o3Handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "o3-mini",
messages: [
{
role: "developer",
content: "Formatting re-enabled\nYou are a helpful assistant.",
},
{ role: "user", content: "Hello!" },
],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: "medium",
temperature: 0.7,
}),
{},
)
// Verify max_tokens is NOT included
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs).not.toHaveProperty("max_completion_tokens")
})
it("should handle O3 model non-streaming with reasoning_effort and max_completion_tokens when includeMaxTokens is true", async () => {
const o3Handler = new OpenAiHandler({
...o3Options,
openAiStreamingEnabled: false,
includeMaxTokens: true,
modelTemperature: 0.3,
})
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello!",
},
]
const stream = o3Handler.createMessage(systemPrompt, messages)
const chunks: any[] = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "o3-mini",
messages: [
{
role: "developer",
content: "Formatting re-enabled\nYou are a helpful assistant.",
},
{ role: "user", content: "Hello!" },
],
reasoning_effort: "medium",
temperature: 0.3,
// O3 models do not support deprecated max_tokens but do support max_completion_tokens
max_completion_tokens: 65536, // Using default maxTokens from o3Options
}),
{},
)
// Verify stream is not set
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs).not.toHaveProperty("stream")
})
it("should use default temperature of 0 when not specified for O3 models", async () => {
const o3Handler = new OpenAiHandler({
...o3Options,
// No modelTemperature specified
})
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello!",
},
]
const stream = o3Handler.createMessage(systemPrompt, messages)
await stream.next()
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
temperature: 0, // Default temperature
}),
{},
)
})
it("should handle O3 model with Azure AI Inference Service respecting includeMaxTokens", async () => {
const o3AzureHandler = new OpenAiHandler({
...o3Options,
openAiBaseUrl: "https://test.services.ai.azure.com",
includeMaxTokens: false, // Should NOT include max_tokens
})
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello!",
},
]
const stream = o3AzureHandler.createMessage(systemPrompt, messages)
await stream.next()
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "o3-mini",
}),
{ path: "/models/chat/completions" },
)
// Verify max_tokens is NOT included when includeMaxTokens is false
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs).not.toHaveProperty("max_completion_tokens")
})
it("should NOT include max_tokens for O3 model with Azure AI Inference Service even when includeMaxTokens is true", async () => {
const o3AzureHandler = new OpenAiHandler({
...o3Options,
openAiBaseUrl: "https://test.services.ai.azure.com",
includeMaxTokens: true, // Should include max_tokens
})
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "Hello!",
},
]
const stream = o3AzureHandler.createMessage(systemPrompt, messages)
await stream.next()
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "o3-mini",
// O3 models do not support max_tokens
}),
{ path: "/models/chat/completions" },
)
})
})
})

View file

@ -158,10 +158,8 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
...(reasoning && reasoning),
}
// @TODO: Move this to the `getModelParams` function.
if (this.options.includeMaxTokens) {
requestOptions.max_tokens = modelInfo.maxTokens
}
// Add max_tokens if needed
this.addMaxTokensIfNeeded(requestOptions, modelInfo)
const stream = await this.client.chat.completions.create(
requestOptions,
@ -222,6 +220,9 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
: [systemMessage, ...convertToOpenAiMessages(messages)],
}
// Add max_tokens if needed
this.addMaxTokensIfNeeded(requestOptions, modelInfo)
const response = await this.client.chat.completions.create(
requestOptions,
this._isAzureAiInference(modelUrl) ? { path: OPENAI_AZURE_AI_INFERENCE_PATH } : {},
@ -256,12 +257,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
async completePrompt(prompt: string): Promise<string> {
try {
const isAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl)
const model = this.getModel()
const modelInfo = model.info
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model: this.getModel().id,
model: model.id,
messages: [{ role: "user", content: prompt }],
}
// Add max_tokens if needed
this.addMaxTokensIfNeeded(requestOptions, modelInfo)
const response = await this.client.chat.completions.create(
requestOptions,
isAzureAiInference ? { path: OPENAI_AZURE_AI_INFERENCE_PATH } : {},
@ -282,25 +288,34 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
): ApiStream {
if (this.options.openAiStreamingEnabled ?? true) {
const methodIsAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl)
const modelInfo = this.getModel().info
const methodIsAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl)
if (this.options.openAiStreamingEnabled ?? true) {
const isGrokXAI = this._isGrokXAI(this.options.openAiBaseUrl)
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
model: modelId,
messages: [
{
role: "developer",
content: `Formatting re-enabled\n${systemPrompt}`,
},
...convertToOpenAiMessages(messages),
],
stream: true,
...(isGrokXAI ? {} : { stream_options: { include_usage: true } }),
reasoning_effort: modelInfo.reasoningEffort,
temperature: this.options.modelTemperature ?? 0,
}
// O3 family models do not support the deprecated max_tokens parameter
// but they do support max_completion_tokens (the modern OpenAI parameter)
// This allows O3 models to limit response length when includeMaxTokens is enabled
this.addMaxTokensIfNeeded(requestOptions, modelInfo)
const stream = await this.client.chat.completions.create(
{
model: modelId,
messages: [
{
role: "developer",
content: `Formatting re-enabled\n${systemPrompt}`,
},
...convertToOpenAiMessages(messages),
],
stream: true,
...(isGrokXAI ? {} : { stream_options: { include_usage: true } }),
reasoning_effort: this.getModel().info.reasoningEffort,
},
requestOptions,
methodIsAzureAiInference ? { path: OPENAI_AZURE_AI_INFERENCE_PATH } : {},
)
@ -315,9 +330,14 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
},
...convertToOpenAiMessages(messages),
],
reasoning_effort: modelInfo.reasoningEffort,
temperature: this.options.modelTemperature ?? 0,
}
const methodIsAzureAiInference = this._isAzureAiInference(this.options.openAiBaseUrl)
// O3 family models do not support the deprecated max_tokens parameter
// but they do support max_completion_tokens (the modern OpenAI parameter)
// This allows O3 models to limit response length when includeMaxTokens is enabled
this.addMaxTokensIfNeeded(requestOptions, modelInfo)
const response = await this.client.chat.completions.create(
requestOptions,
@ -369,6 +389,25 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
const urlHost = this._getUrlHost(baseUrl)
return urlHost.endsWith(".services.ai.azure.com")
}
/**
* Adds max_completion_tokens to the request body if needed based on provider configuration
* Note: max_tokens is deprecated in favor of max_completion_tokens as per OpenAI documentation
* O3 family models handle max_tokens separately in handleO3FamilyMessage
*/
private addMaxTokensIfNeeded(
requestOptions:
| OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
| OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming,
modelInfo: ModelInfo,
): void {
// Only add max_completion_tokens if includeMaxTokens is true
if (this.options.includeMaxTokens === true) {
// Use user-configured modelMaxTokens if available, otherwise fall back to model's default maxTokens
// Using max_completion_tokens as max_tokens is deprecated
requestOptions.max_completion_tokens = this.options.modelMaxTokens || modelInfo.maxTokens
}
}
}
export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiHeaders?: Record<string, string>) {

View file

@ -176,7 +176,7 @@
"editor/context": [
{
"submenu": "roo-cline.contextMenu",
"group": "navigation"
"group": "1"
}
],
"roo-cline.contextMenu": [
@ -196,7 +196,7 @@
"terminal/context": [
{
"submenu": "roo-cline.terminalMenu",
"group": "navigation"
"group": "2"
}
],
"roo-cline.terminalMenu": [

View file

@ -0,0 +1,262 @@
// npx vitest services/code-index/processors/__tests__/file-watcher.spec.ts
import { vi, describe, it, expect, beforeEach } from "vitest"
import { FileWatcher } from "../file-watcher"
import * as vscode from "vscode"
// Mock dependencies
vi.mock("../../cache-manager")
vi.mock("../../../core/ignore/RooIgnoreController")
vi.mock("ignore")
// Mock vscode module
vi.mock("vscode", () => ({
workspace: {
createFileSystemWatcher: vi.fn(),
workspaceFolders: [
{
uri: {
fsPath: "/mock/workspace",
},
},
],
},
RelativePattern: vi.fn().mockImplementation((base, pattern) => ({ base, pattern })),
Uri: {
file: vi.fn().mockImplementation((path) => ({ fsPath: path })),
},
EventEmitter: vi.fn().mockImplementation(() => ({
event: vi.fn(),
fire: vi.fn(),
dispose: vi.fn(),
})),
ExtensionContext: vi.fn(),
}))
describe("FileWatcher", () => {
let fileWatcher: FileWatcher
let mockWatcher: any
let mockOnDidCreate: any
let mockOnDidChange: any
let mockOnDidDelete: any
let mockContext: any
let mockCacheManager: any
let mockEmbedder: any
let mockVectorStore: any
let mockIgnoreInstance: any
beforeEach(() => {
// Reset all mocks
vi.clearAllMocks()
// Create mock event handlers
mockOnDidCreate = vi.fn()
mockOnDidChange = vi.fn()
mockOnDidDelete = vi.fn()
// Create mock watcher
mockWatcher = {
onDidCreate: vi.fn().mockImplementation((handler) => {
mockOnDidCreate = handler
return { dispose: vi.fn() }
}),
onDidChange: vi.fn().mockImplementation((handler) => {
mockOnDidChange = handler
return { dispose: vi.fn() }
}),
onDidDelete: vi.fn().mockImplementation((handler) => {
mockOnDidDelete = handler
return { dispose: vi.fn() }
}),
dispose: vi.fn(),
}
// Mock createFileSystemWatcher to return our mock watcher
vi.mocked(vscode.workspace.createFileSystemWatcher).mockReturnValue(mockWatcher)
// Create mock dependencies
mockContext = {
subscriptions: [],
}
mockCacheManager = {
getHash: vi.fn(),
updateHash: vi.fn(),
deleteHash: vi.fn(),
}
mockEmbedder = {
createEmbeddings: vi.fn().mockResolvedValue({ embeddings: [[0.1, 0.2, 0.3]] }),
}
mockVectorStore = {
upsertPoints: vi.fn().mockResolvedValue(undefined),
deletePointsByFilePath: vi.fn().mockResolvedValue(undefined),
}
mockIgnoreInstance = {
ignores: vi.fn().mockReturnValue(false),
}
fileWatcher = new FileWatcher(
"/mock/workspace",
mockContext,
mockCacheManager,
mockEmbedder,
mockVectorStore,
mockIgnoreInstance,
)
})
describe("file filtering", () => {
it("should ignore files in hidden directories on create events", async () => {
// Initialize the file watcher
await fileWatcher.initialize()
// Spy on the vector store to see which files are actually processed
const processedFiles: string[] = []
mockVectorStore.upsertPoints.mockImplementation(async (points: any[]) => {
points.forEach((point) => {
if (point.payload?.file_path) {
processedFiles.push(point.payload.file_path)
}
})
})
// Simulate file creation events
const testCases = [
{ path: "/mock/workspace/src/file.ts", shouldProcess: true },
{ path: "/mock/workspace/.git/config", shouldProcess: false },
{ path: "/mock/workspace/.hidden/file.ts", shouldProcess: false },
{ path: "/mock/workspace/src/.next/static/file.js", shouldProcess: false },
{ path: "/mock/workspace/node_modules/package/index.js", shouldProcess: false },
{ path: "/mock/workspace/normal/file.js", shouldProcess: true },
]
// Trigger file creation events
for (const { path } of testCases) {
await mockOnDidCreate({ fsPath: path })
}
// Wait for batch processing
await new Promise((resolve) => setTimeout(resolve, 600))
// Check that files in hidden directories were not processed
expect(processedFiles).not.toContain("src/.next/static/file.js")
expect(processedFiles).not.toContain(".git/config")
expect(processedFiles).not.toContain(".hidden/file.ts")
})
it("should ignore files in hidden directories on change events", async () => {
// Initialize the file watcher
await fileWatcher.initialize()
// Track which files are processed
const processedFiles: string[] = []
mockVectorStore.upsertPoints.mockImplementation(async (points: any[]) => {
points.forEach((point) => {
if (point.payload?.file_path) {
processedFiles.push(point.payload.file_path)
}
})
})
// Simulate file change events
const testCases = [
{ path: "/mock/workspace/src/file.ts", shouldProcess: true },
{ path: "/mock/workspace/.vscode/settings.json", shouldProcess: false },
{ path: "/mock/workspace/src/.cache/data.json", shouldProcess: false },
{ path: "/mock/workspace/dist/bundle.js", shouldProcess: false },
]
// Trigger file change events
for (const { path } of testCases) {
await mockOnDidChange({ fsPath: path })
}
// Wait for batch processing
await new Promise((resolve) => setTimeout(resolve, 600))
// Check that files in hidden directories were not processed
expect(processedFiles).not.toContain(".vscode/settings.json")
expect(processedFiles).not.toContain("src/.cache/data.json")
})
it("should ignore files in hidden directories on delete events", async () => {
// Initialize the file watcher
await fileWatcher.initialize()
// Track which files are deleted
const deletedFiles: string[] = []
mockVectorStore.deletePointsByFilePath.mockImplementation(async (filePath: string) => {
deletedFiles.push(filePath)
})
// Simulate file deletion events
const testCases = [
{ path: "/mock/workspace/src/file.ts", shouldProcess: true },
{ path: "/mock/workspace/.git/objects/abc123", shouldProcess: false },
{ path: "/mock/workspace/.DS_Store", shouldProcess: false },
{ path: "/mock/workspace/build/.cache/temp.js", shouldProcess: false },
]
// Trigger file deletion events
for (const { path } of testCases) {
await mockOnDidDelete({ fsPath: path })
}
// Wait for batch processing
await new Promise((resolve) => setTimeout(resolve, 600))
// Check that files in hidden directories were not processed
expect(deletedFiles).not.toContain(".git/objects/abc123")
expect(deletedFiles).not.toContain(".DS_Store")
expect(deletedFiles).not.toContain("build/.cache/temp.js")
})
it("should handle nested hidden directories correctly", async () => {
// Initialize the file watcher
await fileWatcher.initialize()
// Track which files are processed
const processedFiles: string[] = []
mockVectorStore.upsertPoints.mockImplementation(async (points: any[]) => {
points.forEach((point) => {
if (point.payload?.file_path) {
processedFiles.push(point.payload.file_path)
}
})
})
// Test deeply nested hidden directories
const testCases = [
{ path: "/mock/workspace/src/components/Button.tsx", shouldProcess: true },
{ path: "/mock/workspace/src/.hidden/components/Button.tsx", shouldProcess: false },
{ path: "/mock/workspace/.hidden/src/components/Button.tsx", shouldProcess: false },
{ path: "/mock/workspace/src/components/.hidden/Button.tsx", shouldProcess: false },
]
// Trigger file creation events
for (const { path } of testCases) {
await mockOnDidCreate({ fsPath: path })
}
// Wait for batch processing
await new Promise((resolve) => setTimeout(resolve, 600))
// Check that files in hidden directories were not processed
expect(processedFiles).not.toContain("src/.hidden/components/Button.tsx")
expect(processedFiles).not.toContain(".hidden/src/components/Button.tsx")
expect(processedFiles).not.toContain("src/components/.hidden/Button.tsx")
})
})
describe("dispose", () => {
it("should dispose of the watcher when disposed", async () => {
await fileWatcher.initialize()
fileWatcher.dispose()
expect(mockWatcher.dispose).toHaveBeenCalled()
})
})
})

View file

@ -209,5 +209,38 @@ describe("DirectoryScanner", () => {
expect(mockVectorStore.deletePointsByFilePath).toHaveBeenCalledWith("old/file.js")
expect(mockCacheManager.deleteHash).toHaveBeenCalledWith("old/file.js")
})
it("should filter out files in hidden directories", async () => {
const { listFiles } = await import("../../../glob/list-files")
// Mock listFiles to return files including some in hidden directories
vi.mocked(listFiles).mockResolvedValue([
[
"test/file1.js",
"test/.hidden/file2.js",
".git/config",
"src/.next/static/file3.js",
"normal/file4.js",
],
false,
])
// Mock parseFile to track which files are actually processed
const processedFiles: string[] = []
;(mockCodeParser.parseFile as any).mockImplementation((filePath: string) => {
processedFiles.push(filePath)
return []
})
await scanner.scanDirectory("/test")
// Verify that only non-hidden files were processed
expect(processedFiles).toEqual(["test/file1.js", "normal/file4.js"])
expect(processedFiles).not.toContain("test/.hidden/file2.js")
expect(processedFiles).not.toContain(".git/config")
expect(processedFiles).not.toContain("src/.next/static/file3.js")
// Verify the stats
expect(mockCodeParser.parseFile).toHaveBeenCalledTimes(2)
})
})
})

View file

@ -22,6 +22,7 @@ import {
import { codeParser } from "./parser"
import { CacheManager } from "../cache-manager"
import { generateNormalizedAbsolutePath, generateRelativeFilePath } from "../shared/get-relative-path"
import { isPathInIgnoredDirectory } from "../../glob/ignore-utils"
/**
* Implementation of the file watcher interface
@ -453,6 +454,15 @@ export class FileWatcher implements IFileWatcher {
*/
async processFile(filePath: string): Promise<FileProcessingResult> {
try {
// Check if file is in an ignored directory
if (isPathInIgnoredDirectory(filePath)) {
return {
path: filePath,
status: "skipped" as const,
reason: "File is in an ignored directory",
}
}
// Check if file should be ignored
const relativeFilePath = generateRelativeFilePath(filePath)
if (

View file

@ -22,6 +22,7 @@ import {
PARSING_CONCURRENCY,
BATCH_PROCESSING_CONCURRENCY,
} from "../constants"
import { isPathInIgnoredDirectory } from "../../glob/ignore-utils"
export class DirectoryScanner implements IDirectoryScanner {
constructor(
@ -61,10 +62,16 @@ export class DirectoryScanner implements IDirectoryScanner {
// Filter paths using .rooignore
const allowedPaths = ignoreController.filterPaths(filePaths)
// Filter by supported extensions and ignore patterns
// Filter by supported extensions, ignore patterns, and excluded directories
const supportedPaths = allowedPaths.filter((filePath) => {
const ext = path.extname(filePath).toLowerCase()
const relativeFilePath = generateRelativeFilePath(filePath)
// Check if file is in an ignored directory using the shared helper
if (isPathInIgnoredDirectory(filePath)) {
return false
}
return scannerExtensions.includes(ext) && !this.ignoreInstance.ignores(relativeFilePath)
})

View file

@ -0,0 +1,24 @@
/**
* List of directories that are typically large and should be ignored
* when showing recursive file listings or scanning for code indexing.
* This list is shared between list-files.ts and the codebase indexing scanner
* to ensure consistent behavior across the application.
*/
export const DIRS_TO_IGNORE = [
"node_modules",
"__pycache__",
"env",
"venv",
"target/dependency",
"build/dependencies",
"dist",
"out",
"bundle",
"vendor",
"tmp",
"temp",
"deps",
"pkg",
"Pods",
".*",
]

View file

@ -0,0 +1,45 @@
import { DIRS_TO_IGNORE } from "./constants"
/**
* Checks if a file path should be ignored based on the DIRS_TO_IGNORE patterns.
* This function handles special patterns like ".*" for hidden directories.
*
* @param filePath The file path to check
* @returns true if the path should be ignored, false otherwise
*/
export function isPathInIgnoredDirectory(filePath: string): boolean {
// Normalize path separators
const normalizedPath = filePath.replace(/\\/g, "/")
const pathParts = normalizedPath.split("/")
// Check each directory in the path against DIRS_TO_IGNORE
for (const part of pathParts) {
// Skip empty parts (from leading or trailing slashes)
if (!part) continue
// Handle the ".*" pattern for hidden directories
if (DIRS_TO_IGNORE.includes(".*") && part.startsWith(".") && part !== ".") {
return true
}
// Check for exact matches
if (DIRS_TO_IGNORE.includes(part)) {
return true
}
}
// Check if path contains any ignored directory pattern
for (const dir of DIRS_TO_IGNORE) {
if (dir === ".*") {
// Already handled above
continue
}
// Check if the directory appears in the path
if (normalizedPath.includes(`/${dir}/`)) {
return true
}
}
return false
}

View file

@ -5,29 +5,7 @@ import * as childProcess from "child_process"
import * as vscode from "vscode"
import { arePathsEqual } from "../../utils/path"
import { getBinPath } from "../../services/ripgrep"
/**
* List of directories that are typically large and should be ignored
* when showing recursive file listings
*/
const DIRS_TO_IGNORE = [
"node_modules",
"__pycache__",
"env",
"venv",
"target/dependency",
"build/dependencies",
"dist",
"out",
"bundle",
"vendor",
"tmp",
"temp",
"deps",
"pkg",
"Pods",
".*",
]
import { DIRS_TO_IGNORE } from "./constants"
/**
* List files in a directory, with optional recursive traversal

View file

@ -164,6 +164,16 @@ export const OpenAICompatible = ({
onChange={handleInputChange("openAiStreamingEnabled", noTransform)}>
{t("settings:modelInfo.enableStreaming")}
</Checkbox>
<div>
<Checkbox
checked={apiConfiguration?.includeMaxTokens ?? true}
onChange={handleInputChange("includeMaxTokens", noTransform)}>
{t("settings:includeMaxOutputTokens")}
</Checkbox>
<div className="text-sm text-vscode-descriptionForeground ml-6">
{t("settings:includeMaxOutputTokensDescription")}
</div>
</div>
<Checkbox
checked={apiConfiguration?.openAiUseAzure ?? false}
onChange={handleInputChange("openAiUseAzure", noTransform)}>

View file

@ -0,0 +1,314 @@
import React from "react"
import { render, screen, fireEvent } from "@testing-library/react"
import { OpenAICompatible } from "../OpenAICompatible"
import { ProviderSettings } from "@roo-code/types"
// Mock the vscrui Checkbox component
jest.mock("vscrui", () => ({
Checkbox: ({ children, checked, onChange }: any) => (
<label data-testid={`checkbox-${children?.toString().replace(/\s+/g, "-").toLowerCase()}`}>
<input
type="checkbox"
checked={checked}
onChange={() => onChange(!checked)} // Toggle the checked state
data-testid={`checkbox-input-${children?.toString().replace(/\s+/g, "-").toLowerCase()}`}
/>
{children}
</label>
),
}))
// Mock the VSCodeTextField and VSCodeButton components
jest.mock("@vscode/webview-ui-toolkit/react", () => ({
VSCodeTextField: ({
children,
value,
onInput,
placeholder,
className,
style,
"data-testid": dataTestId,
...rest
}: any) => {
return (
<div
data-testid={dataTestId ? `${dataTestId}-text-field` : "vscode-text-field"}
className={className}
style={style}>
{children}
<input
type="text"
value={value}
onChange={(e) => onInput && onInput(e)}
placeholder={placeholder}
data-testid={dataTestId}
{...rest}
/>
</div>
)
},
VSCodeButton: ({ children, onClick, appearance, title }: any) => (
<button onClick={onClick} title={title} data-testid={`vscode-button-${appearance}`}>
{children}
</button>
),
}))
// Mock the translation hook
jest.mock("@src/i18n/TranslationContext", () => ({
useAppTranslation: () => ({
t: (key: string) => key,
}),
}))
// Mock the UI components
jest.mock("@src/components/ui", () => ({
Button: ({ children, onClick }: any) => <button onClick={onClick}>{children}</button>,
}))
// Mock other components
jest.mock("../../ModelPicker", () => ({
ModelPicker: () => <div data-testid="model-picker">Model Picker</div>,
}))
jest.mock("../../R1FormatSetting", () => ({
R1FormatSetting: () => <div data-testid="r1-format-setting">R1 Format Setting</div>,
}))
jest.mock("../../ThinkingBudget", () => ({
ThinkingBudget: () => <div data-testid="thinking-budget">Thinking Budget</div>,
}))
// Mock react-use
jest.mock("react-use", () => ({
useEvent: jest.fn(),
}))
describe("OpenAICompatible Component - includeMaxTokens checkbox", () => {
const mockSetApiConfigurationField = jest.fn()
const mockOrganizationAllowList = {
allowAll: true,
providers: {},
}
beforeEach(() => {
jest.clearAllMocks()
})
describe("Checkbox Rendering", () => {
it("should render the includeMaxTokens checkbox", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: true,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
// Check that the checkbox is rendered
const checkbox = screen.getByTestId("checkbox-settings:includemaxoutputtokens")
expect(checkbox).toBeInTheDocument()
// Check that the description text is rendered
expect(screen.getByText("settings:includeMaxOutputTokensDescription")).toBeInTheDocument()
})
it("should render the checkbox with correct translation keys", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: true,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
// Check that the correct translation key is used for the label
expect(screen.getByText("settings:includeMaxOutputTokens")).toBeInTheDocument()
// Check that the correct translation key is used for the description
expect(screen.getByText("settings:includeMaxOutputTokensDescription")).toBeInTheDocument()
})
})
describe("Initial State", () => {
it("should show checkbox as checked when includeMaxTokens is true", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: true,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
expect(checkboxInput).toBeChecked()
})
it("should show checkbox as unchecked when includeMaxTokens is false", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: false,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
expect(checkboxInput).not.toBeChecked()
})
it("should default to checked when includeMaxTokens is undefined", () => {
const apiConfiguration: Partial<ProviderSettings> = {
// includeMaxTokens is not defined
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
expect(checkboxInput).toBeChecked()
})
it("should default to checked when includeMaxTokens is null", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: null as any,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
expect(checkboxInput).toBeChecked()
})
})
describe("User Interaction", () => {
it("should call handleInputChange with correct parameters when checkbox is clicked from checked to unchecked", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: true,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
fireEvent.click(checkboxInput)
// Verify setApiConfigurationField was called with correct parameters
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("includeMaxTokens", false)
})
it("should call handleInputChange with correct parameters when checkbox is clicked from unchecked to checked", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: false,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
fireEvent.click(checkboxInput)
// Verify setApiConfigurationField was called with correct parameters
expect(mockSetApiConfigurationField).toHaveBeenCalledWith("includeMaxTokens", true)
})
})
describe("Component Updates", () => {
it("should update checkbox state when apiConfiguration changes", () => {
const apiConfigurationInitial: Partial<ProviderSettings> = {
includeMaxTokens: true,
}
const { rerender } = render(
<OpenAICompatible
apiConfiguration={apiConfigurationInitial as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
// Verify initial state
let checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
expect(checkboxInput).toBeChecked()
// Update with new configuration
const apiConfigurationUpdated: Partial<ProviderSettings> = {
includeMaxTokens: false,
}
rerender(
<OpenAICompatible
apiConfiguration={apiConfigurationUpdated as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
// Verify updated state
checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens")
expect(checkboxInput).not.toBeChecked()
})
})
describe("UI Structure", () => {
it("should render the checkbox with description in correct structure", () => {
const apiConfiguration: Partial<ProviderSettings> = {
includeMaxTokens: true,
}
render(
<OpenAICompatible
apiConfiguration={apiConfiguration as ProviderSettings}
setApiConfigurationField={mockSetApiConfigurationField}
organizationAllowList={mockOrganizationAllowList}
/>,
)
// Check that the checkbox and description are in a div container
const checkbox = screen.getByTestId("checkbox-settings:includemaxoutputtokens")
const parentDiv = checkbox.closest("div")
expect(parentDiv).toBeInTheDocument()
// Check that the description has the correct styling classes
const description = screen.getByText("settings:includeMaxOutputTokensDescription")
expect(description).toHaveClass("text-sm", "text-vscode-descriptionForeground", "ml-6")
})
})
})

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Instal·lat",
"settings": "Configuració",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Incloure tokens màxims de sortida",
"includeMaxOutputTokensDescription": "Enviar el paràmetre de tokens màxims de sortida a les sol·licituds API. Alguns proveïdors poden no admetre això."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Installiert",
"settings": "Einstellungen",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Maximale Ausgabe-Tokens einbeziehen",
"includeMaxOutputTokensDescription": "Sende den Parameter für maximale Ausgabe-Tokens in API-Anfragen. Einige Anbieter unterstützen dies möglicherweise nicht."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Installed",
"settings": "Settings",

View file

@ -629,5 +629,7 @@
"labels": {
"customArn": "Custom ARN",
"useCustomArn": "Use custom ARN..."
}
},
"includeMaxOutputTokens": "Include max output tokens",
"includeMaxOutputTokensDescription": "Send max output tokens parameter in API requests. Some providers may not support this."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Instalado",
"settings": "Configuración",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Incluir tokens máximos de salida",
"includeMaxOutputTokensDescription": "Enviar parámetro de tokens máximos de salida en solicitudes API. Algunos proveedores pueden no soportar esto."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Installé",
"settings": "Paramètres",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Inclure les tokens de sortie maximum",
"includeMaxOutputTokensDescription": "Envoyer le paramètre de tokens de sortie maximum dans les requêtes API. Certains fournisseurs peuvent ne pas supporter cela."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "इंस्टॉल किया गया",
"settings": "सेटिंग्स",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "अधिकतम आउटपुट टोकन शामिल करें",
"includeMaxOutputTokensDescription": "API अनुरोधों में अधिकतम आउटपुट टोकन पैरामीटर भेजें। कुछ प्रदाता इसका समर्थन नहीं कर सकते हैं।"
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Terinstal",
"settings": "Pengaturan",

View file

@ -658,5 +658,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Sertakan token output maksimum",
"includeMaxOutputTokensDescription": "Kirim parameter token output maksimum dalam permintaan API. Beberapa provider mungkin tidak mendukung ini."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Installati",
"settings": "Impostazioni",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Includi token di output massimi",
"includeMaxOutputTokensDescription": "Invia il parametro dei token di output massimi nelle richieste API. Alcuni provider potrebbero non supportarlo."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "インストール済み",
"settings": "設定",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "最大出力トークンを含める",
"includeMaxOutputTokensDescription": "APIリクエストで最大出力トークンパラメータを送信します。一部のプロバイダーはこれをサポートしていない場合があります。"
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "설치됨",
"settings": "설정",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "최대 출력 토큰 포함",
"includeMaxOutputTokensDescription": "API 요청에서 최대 출력 토큰 매개변수를 전송합니다. 일부 제공업체는 이를 지원하지 않을 수 있습니다."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Geïnstalleerd",
"settings": "Instellingen",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Maximale output tokens opnemen",
"includeMaxOutputTokensDescription": "Stuur maximale output tokens parameter in API-verzoeken. Sommige providers ondersteunen dit mogelijk niet."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Zainstalowane",
"settings": "Ustawienia",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Uwzględnij maksymalne tokeny wyjściowe",
"includeMaxOutputTokensDescription": "Wyślij parametr maksymalnych tokenów wyjściowych w żądaniach API. Niektórzy dostawcy mogą tego nie obsługiwać."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Instalado",
"settings": "Configurações",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Incluir tokens máximos de saída",
"includeMaxOutputTokensDescription": "Enviar parâmetro de tokens máximos de saída nas solicitações de API. Alguns provedores podem não suportar isso."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Установлено",
"settings": "Настройки",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Включить максимальные выходные токены",
"includeMaxOutputTokensDescription": "Отправлять параметр максимальных выходных токенов в API-запросах. Некоторые провайдеры могут не поддерживать это."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Yüklü",
"settings": "Ayarlar",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Maksimum çıktı tokenlerini dahil et",
"includeMaxOutputTokensDescription": "API isteklerinde maksimum çıktı token parametresini gönder. Bazı sağlayıcılar bunu desteklemeyebilir."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "Đã cài đặt",
"settings": "Cài đặt",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "Bao gồm token đầu ra tối đa",
"includeMaxOutputTokensDescription": "Gửi tham số token đầu ra tối đa trong các yêu cầu API. Một số nhà cung cấp có thể không hỗ trợ điều này."
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "已安装",
"settings": "设置",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "包含最大输出 Token 数",
"includeMaxOutputTokensDescription": "在 API 请求中发送最大输出 Token 参数。某些提供商可能不支持此功能。"
}

View file

@ -1,5 +1,5 @@
{
"title": "Marketplace",
"title": "Roo Marketplace",
"tabs": {
"installed": "已安裝",
"settings": "設定",

View file

@ -629,5 +629,7 @@
"label": "Diagnostics filter",
"description": "Filter diagnostics by source and code (e.g., 'eslint', 'typescript', 'dart Error'). Leave empty to include all diagnostics"
}
}
},
"includeMaxOutputTokens": "包含最大輸出 Token 數",
"includeMaxOutputTokensDescription": "在 API 請求中傳送最大輸出 Token 參數。某些提供商可能不支援此功能。"
}