diff --git a/.roo/rules-issue-fixer/1_Workflow.xml b/.roo/rules-issue-fixer/1_Workflow.xml index 6bc7750527..8fe7778448 100644 --- a/.roo/rules-issue-fixer/1_Workflow.xml +++ b/.roo/rules-issue-fixer/1_Workflow.xml @@ -91,47 +91,6 @@ - Create Implementation Plan - - 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: - - - 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? - - Yes, proceed with the implementation - Let me review the issue first - Modify the approach for: [specific aspect] - Focus only on: [specific part] - - - - - - Explore Codebase and Related Files Use codebase_search FIRST to understand the codebase structure and find ALL related files: @@ -188,6 +147,47 @@ + + Create Implementation Plan + + 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: + + + 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? + + Yes, proceed with the implementation + Let me review the issue first + Modify the approach for: [specific aspect] + Focus only on: [specific part] + + + + + Implement the Solution diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 81c0b45e41..ba0913c2b2 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -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" }, + ) + }) + }) }) diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 62aa4cc8a3..b4f256f43a 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -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 { 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) { diff --git a/src/package.json b/src/package.json index 8212f3da17..1b32cf5a69 100644 --- a/src/package.json +++ b/src/package.json @@ -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": [ diff --git a/src/services/code-index/processors/__tests__/file-watcher.spec.ts b/src/services/code-index/processors/__tests__/file-watcher.spec.ts new file mode 100644 index 0000000000..5564b0329a --- /dev/null +++ b/src/services/code-index/processors/__tests__/file-watcher.spec.ts @@ -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() + }) + }) +}) diff --git a/src/services/code-index/processors/__tests__/scanner.spec.ts b/src/services/code-index/processors/__tests__/scanner.spec.ts index 5e7b168388..b22e90fdf9 100644 --- a/src/services/code-index/processors/__tests__/scanner.spec.ts +++ b/src/services/code-index/processors/__tests__/scanner.spec.ts @@ -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) + }) }) }) diff --git a/src/services/code-index/processors/file-watcher.ts b/src/services/code-index/processors/file-watcher.ts index dfbf0169e3..9a1fc3c9af 100644 --- a/src/services/code-index/processors/file-watcher.ts +++ b/src/services/code-index/processors/file-watcher.ts @@ -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 { 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 ( diff --git a/src/services/code-index/processors/scanner.ts b/src/services/code-index/processors/scanner.ts index f0dafb60c3..24d3e7dbba 100644 --- a/src/services/code-index/processors/scanner.ts +++ b/src/services/code-index/processors/scanner.ts @@ -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) }) diff --git a/src/services/glob/constants.ts b/src/services/glob/constants.ts new file mode 100644 index 0000000000..1ddcc37df9 --- /dev/null +++ b/src/services/glob/constants.ts @@ -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", + ".*", +] diff --git a/src/services/glob/ignore-utils.ts b/src/services/glob/ignore-utils.ts new file mode 100644 index 0000000000..9c80375e66 --- /dev/null +++ b/src/services/glob/ignore-utils.ts @@ -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 +} diff --git a/src/services/glob/list-files.ts b/src/services/glob/list-files.ts index e1809ba4e8..d615360a09 100644 --- a/src/services/glob/list-files.ts +++ b/src/services/glob/list-files.ts @@ -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 diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index 43fea540c3..12ddaf77a7 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -164,6 +164,16 @@ export const OpenAICompatible = ({ onChange={handleInputChange("openAiStreamingEnabled", noTransform)}> {t("settings:modelInfo.enableStreaming")} + + + {t("settings:includeMaxOutputTokens")} + + + {t("settings:includeMaxOutputTokensDescription")} + + diff --git a/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx new file mode 100644 index 0000000000..f7e26c19b2 --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/OpenAICompatible.spec.tsx @@ -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) => ( + + onChange(!checked)} // Toggle the checked state + data-testid={`checkbox-input-${children?.toString().replace(/\s+/g, "-").toLowerCase()}`} + /> + {children} + + ), +})) + +// 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 ( + + {children} + onInput && onInput(e)} + placeholder={placeholder} + data-testid={dataTestId} + {...rest} + /> + + ) + }, + VSCodeButton: ({ children, onClick, appearance, title }: any) => ( + + {children} + + ), +})) + +// 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) => {children}, +})) + +// Mock other components +jest.mock("../../ModelPicker", () => ({ + ModelPicker: () => Model Picker, +})) + +jest.mock("../../R1FormatSetting", () => ({ + R1FormatSetting: () => R1 Format Setting, +})) + +jest.mock("../../ThinkingBudget", () => ({ + ThinkingBudget: () => Thinking Budget, +})) + +// 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 = { + includeMaxTokens: true, + } + + render( + , + ) + + // 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 = { + includeMaxTokens: true, + } + + render( + , + ) + + // 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 = { + includeMaxTokens: true, + } + + render( + , + ) + + const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens") + expect(checkboxInput).toBeChecked() + }) + + it("should show checkbox as unchecked when includeMaxTokens is false", () => { + const apiConfiguration: Partial = { + includeMaxTokens: false, + } + + render( + , + ) + + const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens") + expect(checkboxInput).not.toBeChecked() + }) + + it("should default to checked when includeMaxTokens is undefined", () => { + const apiConfiguration: Partial = { + // includeMaxTokens is not defined + } + + render( + , + ) + + const checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens") + expect(checkboxInput).toBeChecked() + }) + + it("should default to checked when includeMaxTokens is null", () => { + const apiConfiguration: Partial = { + includeMaxTokens: null as any, + } + + render( + , + ) + + 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 = { + includeMaxTokens: true, + } + + render( + , + ) + + 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 = { + includeMaxTokens: false, + } + + render( + , + ) + + 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 = { + includeMaxTokens: true, + } + + const { rerender } = render( + , + ) + + // Verify initial state + let checkboxInput = screen.getByTestId("checkbox-input-settings:includemaxoutputtokens") + expect(checkboxInput).toBeChecked() + + // Update with new configuration + const apiConfigurationUpdated: Partial = { + includeMaxTokens: false, + } + + rerender( + , + ) + + // 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 = { + includeMaxTokens: true, + } + + render( + , + ) + + // 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") + }) + }) +}) diff --git a/webview-ui/src/i18n/locales/ca/marketplace.json b/webview-ui/src/i18n/locales/ca/marketplace.json index 4190379620..8653762d4f 100644 --- a/webview-ui/src/i18n/locales/ca/marketplace.json +++ b/webview-ui/src/i18n/locales/ca/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Instal·lat", "settings": "Configuració", diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index d376bae6f4..88cd891486 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -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ò." } diff --git a/webview-ui/src/i18n/locales/de/marketplace.json b/webview-ui/src/i18n/locales/de/marketplace.json index 9bd6c4c849..c9bc9f9c43 100644 --- a/webview-ui/src/i18n/locales/de/marketplace.json +++ b/webview-ui/src/i18n/locales/de/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Installiert", "settings": "Einstellungen", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 43404638ab..4de34ea44f 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/en/marketplace.json b/webview-ui/src/i18n/locales/en/marketplace.json index 32c64f9bda..6a5e877b2a 100644 --- a/webview-ui/src/i18n/locales/en/marketplace.json +++ b/webview-ui/src/i18n/locales/en/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Installed", "settings": "Settings", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index d28309f254..555aed1c84 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/es/marketplace.json b/webview-ui/src/i18n/locales/es/marketplace.json index f2a7de86fa..38056f32ea 100644 --- a/webview-ui/src/i18n/locales/es/marketplace.json +++ b/webview-ui/src/i18n/locales/es/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Instalado", "settings": "Configuración", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 21ee952d48..0d787a2c53 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/fr/marketplace.json b/webview-ui/src/i18n/locales/fr/marketplace.json index 132951245d..cabac260ef 100644 --- a/webview-ui/src/i18n/locales/fr/marketplace.json +++ b/webview-ui/src/i18n/locales/fr/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Installé", "settings": "Paramètres", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 053ddfdad5..df8d67848d 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/hi/marketplace.json b/webview-ui/src/i18n/locales/hi/marketplace.json index eb5132f73a..34924d3686 100644 --- a/webview-ui/src/i18n/locales/hi/marketplace.json +++ b/webview-ui/src/i18n/locales/hi/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "इंस्टॉल किया गया", "settings": "सेटिंग्स", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 5f84a776a0..b90dc83e69 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -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 अनुरोधों में अधिकतम आउटपुट टोकन पैरामीटर भेजें। कुछ प्रदाता इसका समर्थन नहीं कर सकते हैं।" } diff --git a/webview-ui/src/i18n/locales/id/marketplace.json b/webview-ui/src/i18n/locales/id/marketplace.json index 1873a8c51f..9d80ebe326 100644 --- a/webview-ui/src/i18n/locales/id/marketplace.json +++ b/webview-ui/src/i18n/locales/id/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Terinstal", "settings": "Pengaturan", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 163c8ca84a..9989d89ec6 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/it/marketplace.json b/webview-ui/src/i18n/locales/it/marketplace.json index a3bbc76405..875db2685c 100644 --- a/webview-ui/src/i18n/locales/it/marketplace.json +++ b/webview-ui/src/i18n/locales/it/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Installati", "settings": "Impostazioni", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 9a906c6c5f..509c4a9f2d 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/ja/marketplace.json b/webview-ui/src/i18n/locales/ja/marketplace.json index b6d843c82c..c8fe42d8cf 100644 --- a/webview-ui/src/i18n/locales/ja/marketplace.json +++ b/webview-ui/src/i18n/locales/ja/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "インストール済み", "settings": "設定", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 5a09f7bd4b..7f1481ac56 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -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リクエストで最大出力トークンパラメータを送信します。一部のプロバイダーはこれをサポートしていない場合があります。" } diff --git a/webview-ui/src/i18n/locales/ko/marketplace.json b/webview-ui/src/i18n/locales/ko/marketplace.json index d29022624b..004b90cb31 100644 --- a/webview-ui/src/i18n/locales/ko/marketplace.json +++ b/webview-ui/src/i18n/locales/ko/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "설치됨", "settings": "설정", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index c94802a941..6fb5155d8b 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -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 요청에서 최대 출력 토큰 매개변수를 전송합니다. 일부 제공업체는 이를 지원하지 않을 수 있습니다." } diff --git a/webview-ui/src/i18n/locales/nl/marketplace.json b/webview-ui/src/i18n/locales/nl/marketplace.json index 56ef3c4ca5..b9effed30f 100644 --- a/webview-ui/src/i18n/locales/nl/marketplace.json +++ b/webview-ui/src/i18n/locales/nl/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Geïnstalleerd", "settings": "Instellingen", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index ce9b989d7c..d42a02724a 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/pl/marketplace.json b/webview-ui/src/i18n/locales/pl/marketplace.json index 7b8d686b86..fe663c7e31 100644 --- a/webview-ui/src/i18n/locales/pl/marketplace.json +++ b/webview-ui/src/i18n/locales/pl/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Zainstalowane", "settings": "Ustawienia", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 99341b3566..265165b118 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -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ć." } diff --git a/webview-ui/src/i18n/locales/pt-BR/marketplace.json b/webview-ui/src/i18n/locales/pt-BR/marketplace.json index 8ae297473b..088adc850a 100644 --- a/webview-ui/src/i18n/locales/pt-BR/marketplace.json +++ b/webview-ui/src/i18n/locales/pt-BR/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Instalado", "settings": "Configurações", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 4850726afb..5174c7f15e 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/ru/marketplace.json b/webview-ui/src/i18n/locales/ru/marketplace.json index 41b6df9a49..4f87737722 100644 --- a/webview-ui/src/i18n/locales/ru/marketplace.json +++ b/webview-ui/src/i18n/locales/ru/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Установлено", "settings": "Настройки", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 58e673c761..a4f3ac4365 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -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-запросах. Некоторые провайдеры могут не поддерживать это." } diff --git a/webview-ui/src/i18n/locales/tr/marketplace.json b/webview-ui/src/i18n/locales/tr/marketplace.json index c5e646afa8..a034f7876f 100644 --- a/webview-ui/src/i18n/locales/tr/marketplace.json +++ b/webview-ui/src/i18n/locales/tr/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Yüklü", "settings": "Ayarlar", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 256632ffee..914db0edc7 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/vi/marketplace.json b/webview-ui/src/i18n/locales/vi/marketplace.json index a6ef816f4b..6539177161 100644 --- a/webview-ui/src/i18n/locales/vi/marketplace.json +++ b/webview-ui/src/i18n/locales/vi/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "Đã cài đặt", "settings": "Cài đặt", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index a8706cc31f..a43894e106 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -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." } diff --git a/webview-ui/src/i18n/locales/zh-CN/marketplace.json b/webview-ui/src/i18n/locales/zh-CN/marketplace.json index 31c83e7bf6..ccf1873ca6 100644 --- a/webview-ui/src/i18n/locales/zh-CN/marketplace.json +++ b/webview-ui/src/i18n/locales/zh-CN/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "已安装", "settings": "设置", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index dd978528a4..b57409626e 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -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 参数。某些提供商可能不支持此功能。" } diff --git a/webview-ui/src/i18n/locales/zh-TW/marketplace.json b/webview-ui/src/i18n/locales/zh-TW/marketplace.json index 0e11f2a23e..201d3b2bb0 100644 --- a/webview-ui/src/i18n/locales/zh-TW/marketplace.json +++ b/webview-ui/src/i18n/locales/zh-TW/marketplace.json @@ -1,5 +1,5 @@ { - "title": "Marketplace", + "title": "Roo Marketplace", "tabs": { "installed": "已安裝", "settings": "設定", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index dd36737031..14fec6be6b 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -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 參數。某些提供商可能不支援此功能。" }