diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 5b729a125f..bfd071a755 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -103,6 +103,7 @@ export const globalSettingsSchema = z.object({ customSupportPrompts: customSupportPromptsSchema.optional(), enhancementApiConfigId: z.string().optional(), historyPreviewCollapsed: z.boolean().optional(), + profileThresholds: z.record(z.string(), z.number()).optional(), }) export type GlobalSettings = z.infer diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 8a8b57bb0c..3b73b1915c 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -8,6 +8,8 @@ import { ApiMessage } from "../task-persistence/apiMessages" import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" export const N_MESSAGES_TO_KEEP = 3 +export const MIN_CONDENSE_THRESHOLD = 5 // Minimum percentage of context window to trigger condensing +export const MAX_CONDENSE_THRESHOLD = 100 // Maximum percentage of context window to trigger condensing const SUMMARY_PROMPT = `\ Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. diff --git a/src/core/sliding-window/__tests__/sliding-window.spec.ts b/src/core/sliding-window/__tests__/sliding-window.spec.ts index 0f41942547..3bda5351d4 100644 --- a/src/core/sliding-window/__tests__/sliding-window.spec.ts +++ b/src/core/sliding-window/__tests__/sliding-window.spec.ts @@ -19,7 +19,14 @@ import { // Create a mock ApiHandler for testing class MockApiHandler extends BaseProvider { createMessage(): any { - throw new Error("Method not implemented.") + // Mock implementation for testing - returns an async iterable stream + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { type: "text", text: "Mock summary content" } + yield { type: "usage", inputTokens: 100, outputTokens: 50 } + }, + } + return mockStream } getModel(): { id: string; info: ModelInfo } { @@ -265,6 +272,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) // Check the new return type @@ -304,6 +313,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result).toEqual({ @@ -337,6 +348,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) const result2 = await truncateConversationIfNeeded({ @@ -349,6 +362,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result1.messages).toEqual(result2.messages) @@ -368,6 +383,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) const result4 = await truncateConversationIfNeeded({ @@ -380,6 +397,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result3.messages).toEqual(result4.messages) @@ -414,6 +433,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(resultWithSmall).toEqual({ messages: messagesWithSmallContent, @@ -447,6 +468,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(resultWithLarge.messages).not.toEqual(messagesWithLargeContent) // Should truncate expect(resultWithLarge.summary).toBe("") @@ -473,6 +496,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(resultWithVeryLarge.messages).not.toEqual(messagesWithVeryLargeContent) // Should truncate expect(resultWithVeryLarge.summary).toBe("") @@ -509,6 +534,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result).toEqual({ messages: expectedResult, @@ -554,6 +581,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) // Verify summarizeConversation was called with the right parameters @@ -619,6 +648,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) // Verify summarizeConversation was called @@ -664,6 +695,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 50, // This shouldn't matter since autoCondenseContext is false systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) // Verify summarizeConversation was not called @@ -719,6 +752,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 60% systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) // Verify summarizeConversation was called with the right parameters @@ -769,6 +804,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 40% systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) // Verify summarizeConversation was not called @@ -787,6 +824,212 @@ describe("Sliding Window", () => { }) }) + /** + * Tests for profile-specific thresholds functionality + */ + describe("profile-specific thresholds", () => { + const createModelInfo = (contextWindow: number, maxTokens?: number): ModelInfo => ({ + contextWindow, + supportsPromptCache: true, + maxTokens, + }) + + const messages: ApiMessage[] = [ + { role: "user", content: "First message" }, + { role: "assistant", content: "Second message" }, + { role: "user", content: "Third message" }, + { role: "assistant", content: "Fourth message" }, + { role: "user", content: "Fifth message" }, + ] + + /** + * Test that a profile's specific threshold is correctly used instead of the global threshold + * when defined in profileThresholds + */ + it("should use profile-specific threshold when enabled and profile has specific threshold", async () => { + const modelInfo = createModelInfo(100000, 30000) + const profileThresholds = { + "test-profile": 60, // Profile-specific threshold of 60% + } + const currentProfileId = "test-profile" + const contextWindow = modelInfo.contextWindow + + // Set tokens to 65% of context window - above profile threshold (60%) but below global default (100%) + const totalTokens = Math.floor(contextWindow * 0.65) // 65000 tokens + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // Mock the summarizeConversation function + const mockSummary = "Profile-specific threshold summary" + const mockCost = 0.03 + const mockSummarizeResponse: condenseModule.SummarizeResponse = { + messages: [ + { role: "user", content: "First message" }, + { role: "assistant", content: mockSummary, isSummary: true }, + { role: "user", content: "Last message" }, + ], + summary: mockSummary, + cost: mockCost, + newContextTokens: 100, + } + + const summarizeSpy = vi + .spyOn(condenseModule, "summarizeConversation") + .mockResolvedValue(mockSummarizeResponse) + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 100, // Global threshold of 100% + systemPrompt: "System prompt", + taskId, + profileThresholds, + currentProfileId, + }) + + // Should use summarization because 65% > 60% (profile threshold) + expect(summarizeSpy).toHaveBeenCalled() + expect(result).toMatchObject({ + messages: mockSummarizeResponse.messages, + summary: mockSummary, + cost: mockCost, + prevContextTokens: totalTokens, + }) + + // Clean up + summarizeSpy.mockRestore() + }) + + /** + * Test that when a profile's threshold is set to -1, + * the function correctly falls back to using the global autoCondenseContextPercent + */ + it("should fall back to global threshold when profile threshold is -1", async () => { + const modelInfo = createModelInfo(100000, 30000) + const profileThresholds = { + "test-profile": -1, // Profile threshold set to -1 (use global) + } + const currentProfileId = "test-profile" + const contextWindow = modelInfo.contextWindow + + // Set tokens to 80% of context window - above global threshold (75%) but would be below if profile had its own + const totalTokens = Math.floor(contextWindow * 0.8) // 80000 tokens + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // Mock the summarizeConversation function + const mockSummary = "Global threshold fallback summary" + const mockCost = 0.04 + const mockSummarizeResponse: condenseModule.SummarizeResponse = { + messages: [ + { role: "user", content: "First message" }, + { role: "assistant", content: mockSummary, isSummary: true }, + { role: "user", content: "Last message" }, + ], + summary: mockSummary, + cost: mockCost, + newContextTokens: 120, + } + + const summarizeSpy = vi + .spyOn(condenseModule, "summarizeConversation") + .mockResolvedValue(mockSummarizeResponse) + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 75, // Global threshold of 75% + systemPrompt: "System prompt", + taskId, + profileThresholds, + currentProfileId, + }) + + // Should use summarization because 80% > 75% (global threshold, since profile is -1) + expect(summarizeSpy).toHaveBeenCalled() + expect(result).toMatchObject({ + messages: mockSummarizeResponse.messages, + summary: mockSummary, + cost: mockCost, + prevContextTokens: totalTokens, + }) + + // Clean up + summarizeSpy.mockRestore() + }) + + /** + * Test that when a profile does not have a specific threshold defined, + * the function correctly falls back to the global default + */ + it("should fall back to global threshold when profile has no specific threshold", async () => { + const modelInfo = createModelInfo(100000, 30000) + const profileThresholds = { + "other-profile": 50, // Different profile has a threshold + } + const currentProfileId = "test-profile" // This profile is not in profileThresholds + const contextWindow = modelInfo.contextWindow + + // Calculate allowedTokens: contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens + // allowedTokens = 100000 * 0.9 - 30000 = 60000 + // Set tokens to be below both the global threshold (80%) and allowedTokens + const totalTokens = 50000 // 50% of context window, well below 60000 allowedTokens and 80% threshold + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // Reset any previous mock calls + vi.clearAllMocks() + const summarizeSpy = vi.spyOn(condenseModule, "summarizeConversation") + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 80, // Global threshold of 80% + systemPrompt: "System prompt", + taskId, + profileThresholds, + currentProfileId, + }) + + // Should NOT use summarization because 50% < 80% (global threshold, since profile has no specific threshold) + // and totalTokens (50000) < allowedTokens (60000) + expect(summarizeSpy).not.toHaveBeenCalled() + expect(result).toEqual({ + messages: messagesWithSmallContent, + summary: "", + cost: 0, + prevContextTokens: totalTokens, + }) + + // Clean up + summarizeSpy.mockRestore() + }) + }) + /** * Tests for the getMaxTokens function (private but tested through truncateConversationIfNeeded) */ @@ -829,6 +1072,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result1).toEqual({ messages: messagesWithSmallContent, @@ -848,6 +1093,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result2.messages).not.toEqual(messagesWithSmallContent) expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction @@ -878,6 +1125,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result1).toEqual({ messages: messagesWithSmallContent, @@ -897,6 +1146,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result2.messages).not.toEqual(messagesWithSmallContent) expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction @@ -926,6 +1177,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result1.messages).toEqual(messagesWithSmallContent) @@ -940,6 +1193,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result2).not.toEqual(messagesWithSmallContent) expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction @@ -967,6 +1222,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result1.messages).toEqual(messagesWithSmallContent) @@ -981,6 +1238,8 @@ describe("Sliding Window", () => { autoCondenseContextPercent: 100, systemPrompt: "System prompt", taskId, + profileThresholds: {}, + currentProfileId: "default", }) expect(result2).not.toEqual(messagesWithSmallContent) expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index dc9eaf718d..ae26f51a52 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -3,7 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { TelemetryService } from "@roo-code/telemetry" import { ApiHandler } from "../../api" -import { summarizeConversation, SummarizeResponse } from "../condense" +import { MAX_CONDENSE_THRESHOLD, MIN_CONDENSE_THRESHOLD, summarizeConversation, SummarizeResponse } from "../condense" import { ApiMessage } from "../task-persistence/apiMessages" /** @@ -74,6 +74,8 @@ type TruncateOptions = { taskId: string customCondensingPrompt?: string condensingApiHandler?: ApiHandler + profileThresholds: Record + currentProfileId: string } type TruncateResponse = SummarizeResponse & { prevContextTokens: number } @@ -97,6 +99,8 @@ export async function truncateConversationIfNeeded({ taskId, customCondensingPrompt, condensingApiHandler, + profileThresholds, + currentProfileId, }: TruncateOptions): Promise { let error: string | undefined let cost = 0 @@ -117,9 +121,29 @@ export async function truncateConversationIfNeeded({ // Truncate if we're within TOKEN_BUFFER_PERCENTAGE of the context window const allowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens + // Determine the effective threshold to use + let effectiveThreshold = autoCondenseContextPercent + const profileThreshold = profileThresholds[currentProfileId] + if (profileThreshold !== undefined) { + if (profileThreshold === -1) { + // Special case: -1 means inherit from global setting + effectiveThreshold = autoCondenseContextPercent + } else if (profileThreshold >= MIN_CONDENSE_THRESHOLD && profileThreshold <= MAX_CONDENSE_THRESHOLD) { + // Valid custom threshold + effectiveThreshold = profileThreshold + } else { + // Invalid threshold value, fall back to global setting + console.warn( + `Invalid profile threshold ${profileThreshold} for profile "${currentProfileId}". Using global default of ${autoCondenseContextPercent}%`, + ) + effectiveThreshold = autoCondenseContextPercent + } + } + // If no specific threshold is found for the profile, fall back to global setting + if (autoCondenseContext) { const contextPercent = (100 * prevContextTokens) / contextWindow - if (contextPercent >= autoCondenseContextPercent || prevContextTokens > allowedTokens) { + if (contextPercent >= effectiveThreshold || prevContextTokens > allowedTokens) { // Attempt to intelligently condense the context const result = await summarizeConversation( messages, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 6f6f2d684a..46da7485ed 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1643,6 +1643,7 @@ export class Task extends EventEmitter { mode, autoCondenseContext = true, autoCondenseContextPercent = 100, + profileThresholds = {}, } = state ?? {} // Get condensing configuration for automatic triggers @@ -1719,6 +1720,8 @@ export class Task extends EventEmitter { taskId: this.taskId, customCondensingPrompt, condensingApiHandler, + profileThresholds, + currentProfileId: state?.currentApiConfigName || "default", }) if (truncateResult.messages !== this.apiConversationHistory) { await this.overwriteApiConversationHistory(truncateResult.messages) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 49fd0c1258..dff2263a06 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1376,6 +1376,7 @@ export class ClineProvider customCondensingPrompt, codebaseIndexConfig, codebaseIndexModels, + profileThresholds, } = await this.getState() const telemetryKey = process.env.POSTHOG_API_KEY @@ -1483,6 +1484,7 @@ export class ClineProvider codebaseIndexEmbedderModelId: "", }, mdmCompliant: this.checkMdmCompliance(), + profileThresholds: profileThresholds ?? {}, } } @@ -1632,6 +1634,7 @@ export class ClineProvider codebaseIndexEmbedderBaseUrl: "", codebaseIndexEmbedderModelId: "", }, + profileThresholds: stateValues.profileThresholds ?? {}, } } diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index de4f34a12a..2944e3618e 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -522,6 +522,7 @@ describe("ClineProvider", () => { autoCondenseContextPercent: 100, cloudIsAuthenticated: false, sharingEnabled: false, + profileThresholds: {}, } const message: ExtensionMessage = { diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index c6ef3c839c..ce64a39c88 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1048,6 +1048,10 @@ export const webviewMessageHandler = async ( await updateGlobalState("customCondensingPrompt", message.text) await provider.postStateToWebview() break + case "profileThresholds": + await updateGlobalState("profileThresholds", message.values) + await provider.postStateToWebview() + break case "autoApprovalEnabled": await updateGlobalState("autoApprovalEnabled", message.bool ?? false) await provider.postStateToWebview() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index a515a2d004..9a2c9230bd 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -213,6 +213,7 @@ export type ExtensionState = Pick< | "customCondensingPrompt" | "codebaseIndexConfig" | "codebaseIndexModels" + | "profileThresholds" > & { version: string clineMessages: ClineMessage[] @@ -259,6 +260,7 @@ export type ExtensionState = Pick< autoCondenseContextPercent: number marketplaceItems?: MarketplaceItem[] marketplaceInstalledMetadata?: { project: Record; global: Record } + profileThresholds: Record } export interface ClineSayTool { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 9ea0e192eb..d4ed1cddf6 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -158,6 +158,7 @@ export interface WebviewMessage { | "indexCleared" | "focusPanelRequest" | "codebaseIndexConfig" + | "profileThresholds" | "setHistoryPreviewCollapsed" | "openExternal" | "filterMarketplaceItems" @@ -169,6 +170,7 @@ export interface WebviewMessage { | "marketplaceInstallResult" | "fetchMarketplaceData" | "switchTab" + | "profileThresholds" text?: string tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account" disabled?: boolean diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx index 548020159e..6fbd2f1dab 100644 --- a/webview-ui/src/components/settings/ContextManagementSettings.tsx +++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx @@ -1,4 +1,5 @@ import { HTMLAttributes } from "react" +import React from "react" import { useAppTranslation } from "@/i18n/TranslationContext" import { VSCodeCheckbox, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { Database, FoldVertical } from "lucide-react" @@ -62,6 +63,7 @@ type ContextManagementSettingsProps = HTMLAttributes & { showRooIgnoredFiles?: boolean maxReadFileLine?: number maxConcurrentFileReads?: number + profileThresholds?: Record setCachedStateField: SetCachedStateField< | "autoCondenseContext" | "autoCondenseContextPercent" @@ -72,6 +74,7 @@ type ContextManagementSettingsProps = HTMLAttributes & { | "showRooIgnoredFiles" | "maxReadFileLine" | "maxConcurrentFileReads" + | "profileThresholds" > } @@ -87,10 +90,41 @@ export const ContextManagementSettings = ({ setCachedStateField, maxReadFileLine, maxConcurrentFileReads, + profileThresholds = {}, className, ...props }: ContextManagementSettingsProps) => { const { t } = useAppTranslation() + const [selectedThresholdProfile, setSelectedThresholdProfile] = React.useState("default") + + // Helper function to get the current threshold value based on selected profile + const getCurrentThresholdValue = () => { + if (selectedThresholdProfile === "default") { + return autoCondenseContextPercent + } + const profileThreshold = profileThresholds[selectedThresholdProfile] + if (profileThreshold === undefined || profileThreshold === -1) { + return autoCondenseContextPercent // Use default if profile not configured or set to -1 + } + return profileThreshold + } + + // Helper function to handle threshold changes + const handleThresholdChange = (value: number) => { + if (selectedThresholdProfile === "default") { + setCachedStateField("autoCondenseContextPercent", value) + } else { + const newThresholds = { + ...profileThresholds, + [selectedThresholdProfile]: value, + } + setCachedStateField("profileThresholds", newThresholds) + vscode.postMessage({ + type: "profileThresholds", + values: newThresholds, + }) + } + } return (
@@ -220,29 +254,76 @@ export const ContextManagementSettings = ({
-
{t("settings:contextManagement.autoCondenseContextPercent.label")}
+
{t("settings:contextManagement.condensingThreshold.label")}
+
+ +
+ + {/* Threshold Slider */}
- setCachedStateField("autoCondenseContextPercent", value) - } - data-testid="auto-condense-percent-slider" + value={[getCurrentThresholdValue()]} + onValueChange={([value]) => handleThresholdChange(value)} + data-testid="condense-threshold-slider" /> - {autoCondenseContextPercent}% + {getCurrentThresholdValue()}%
- {t("settings:contextManagement.autoCondenseContextPercent.description")} + {selectedThresholdProfile === "default" + ? t("settings:contextManagement.condensingThreshold.defaultDescription", { + threshold: autoCondenseContextPercent, + }) + : t("settings:contextManagement.condensingThreshold.profileDescription")}
{/* API Configuration Selection */} -
+
{t("settings:contextManagement.condensingApiConfiguration.label")}
@@ -286,7 +367,7 @@ export const ContextManagementSettings = ({
{/* Custom Prompt Section */} -
+
{t("settings:contextManagement.customCondensingPrompt.label")}
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 5a330c8996..839ce25b69 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -172,6 +172,7 @@ const SettingsView = forwardRef(({ onDone, t codebaseIndexConfig, codebaseIndexModels, customSupportPrompts, + profileThresholds, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -315,6 +316,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration }) vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting }) vscode.postMessage({ type: "codebaseIndexConfig", values: codebaseIndexConfig }) + vscode.postMessage({ type: "profileThresholds", values: profileThresholds }) setChangeDetected(false) } } @@ -644,6 +646,7 @@ const SettingsView = forwardRef(({ onDone, t showRooIgnoredFiles={showRooIgnoredFiles} maxReadFileLine={maxReadFileLine} maxConcurrentFileReads={maxConcurrentFileReads} + profileThresholds={profileThresholds} setCachedStateField={setCachedStateField} /> )} diff --git a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx index d1c8f64f57..9a7d451d1d 100644 --- a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx @@ -49,6 +49,7 @@ describe("ContextManagementSettings", () => { maxWorkspaceFiles: 200, showRooIgnoredFiles: false, setCachedStateField: vitest.fn(), + profileThresholds: {}, } beforeEach(() => { @@ -173,18 +174,17 @@ describe("ContextManagementSettings", () => { } render() - // Should render the auto condense section // Should render the auto condense section const autoCondenseCheckbox = screen.getByTestId("auto-condense-context-checkbox") expect(autoCondenseCheckbox).toBeInTheDocument() - // Should render the slider with correct value - const slider = screen.getByTestId("auto-condense-percent-slider") + // Should render the threshold slider with correct value + const slider = screen.getByTestId("condense-threshold-slider") expect(slider).toBeInTheDocument() - // Should render the API config select - const apiSelect = screen.getByRole("combobox") - expect(apiSelect).toBeInTheDocument() + // Should render both select dropdowns (profile and API config) + const selects = screen.getAllByRole("combobox") + expect(selects).toHaveLength(2) // Should render the custom prompt textarea const textarea = screen.getByRole("textbox") @@ -207,7 +207,7 @@ describe("ContextManagementSettings", () => { it("toggles auto condense context setting", () => { const mockSetCachedStateField = vitest.fn() const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } - const { rerender } = render() + render() const checkbox = screen.getByTestId("auto-condense-context-checkbox") expect(checkbox).toBeChecked() @@ -215,42 +215,25 @@ describe("ContextManagementSettings", () => { // Toggle off fireEvent.click(checkbox) expect(mockSetCachedStateField).toHaveBeenCalledWith("autoCondenseContext", false) - - // Re-render with updated props to simulate the state change - rerender() - - // Additional settings should not be visible when disabled - expect(screen.queryByTestId("auto-condense-percent-slider")).not.toBeInTheDocument() - expect(screen.queryByRole("combobox")).not.toBeInTheDocument() - expect(screen.queryByRole("textbox")).not.toBeInTheDocument() }) it("shows additional settings when auto condense is enabled", () => { render() // Additional settings should be visible - expect(screen.getByTestId("auto-condense-percent-slider")).toBeInTheDocument() - expect(screen.getByRole("combobox")).toBeInTheDocument() + expect(screen.getByTestId("condense-threshold-slider")).toBeInTheDocument() + // Two comboboxes: one for profile selection, one for API config + expect(screen.getAllByRole("combobox")).toHaveLength(2) expect(screen.getByRole("textbox")).toBeInTheDocument() }) - it("hides additional settings when auto condense is disabled", () => { - const props = { ...autoCondenseProps, autoCondenseContext: false } - render() - - // Additional settings should not be visible - expect(screen.queryByTestId("auto-condense-percent-slider")).not.toBeInTheDocument() - expect(screen.queryByRole("combobox")).not.toBeInTheDocument() - expect(screen.queryByRole("textbox")).not.toBeInTheDocument() - }) - it("updates auto condense context percent", () => { const mockSetCachedStateField = vitest.fn() const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } render() - // Find the auto condense percent slider - const slider = screen.getByTestId("auto-condense-percent-slider") + // Find the condense threshold slider + const slider = screen.getByTestId("condense-threshold-slider") // Test slider interaction slider.focus() @@ -273,7 +256,9 @@ describe("ContextManagementSettings", () => { const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } render() - const apiSelect = screen.getByRole("combobox") + // Get the second combobox (API config select) + const selects = screen.getAllByRole("combobox") + const apiSelect = selects[1] fireEvent.click(apiSelect) const configOption = screen.getByText("Config 1") @@ -295,8 +280,9 @@ describe("ContextManagementSettings", () => { const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } render() - // Test selecting default config - const apiSelect = screen.getByRole("combobox") + // Test selecting default config - get the second combobox (API config) + const selects = screen.getAllByRole("combobox") + const apiSelect = selects[1] fireEvent.click(apiSelect) const defaultOption = screen.getByText( "settings:contextManagement.condensingApiConfiguration.useCurrentConfig", @@ -463,9 +449,14 @@ describe("ContextManagementSettings", () => { } render() - expect(screen.queryByText("settings:experimental.autoCondenseContextPercent.label")).not.toBeInTheDocument() - expect(screen.queryByText("settings:experimental.condensingApiConfiguration.label")).not.toBeInTheDocument() - expect(screen.queryByText("settings:experimental.customCondensingPrompt.label")).not.toBeInTheDocument() + // When auto condense is false, all condensing-related UI should not be visible + expect(screen.queryByTestId("condense-threshold-slider")).not.toBeInTheDocument() + expect( + screen.queryByText("settings:contextManagement.condensingApiConfiguration.label"), + ).not.toBeInTheDocument() + expect( + screen.queryByText("settings:contextManagement.customCondensingPrompt.label"), + ).not.toBeInTheDocument() }) it("renders max read file controls with default value when maxReadFileLine is undefined", () => { diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index e0f6e0d3ad..313c5170a0 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -44,6 +44,8 @@ export interface ExtensionStateContextType extends ExtensionState { setCustomCondensingPrompt: (value: string) => void marketplaceItems?: any[] marketplaceInstalledMetadata?: MarketplaceInstalledMetadata + profileThresholds: Record + setProfileThresholds: (value: Record) => void setApiConfiguration: (config: ProviderSettings) => void setCustomInstructions: (value?: string) => void setAlwaysAllowReadOnly: (value: boolean) => void @@ -201,6 +203,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode organizationAllowList: ORGANIZATION_ALLOW_ALL, autoCondenseContext: true, autoCondenseContextPercent: 100, + profileThresholds: {}, codebaseIndexConfig: { codebaseIndexEnabled: false, codebaseIndexQdrantUrl: "http://localhost:6333", @@ -345,6 +348,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode cloudIsAuthenticated: state.cloudIsAuthenticated ?? false, marketplaceItems, marketplaceInstalledMetadata, + profileThresholds: state.profileThresholds ?? {}, setExperimentEnabled: (id, enabled) => setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })), setApiConfiguration, @@ -429,6 +433,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setCondensingApiConfigId: (value) => setState((prevState) => ({ ...prevState, condensingApiConfigId: value })), setCustomCondensingPrompt: (value) => setState((prevState) => ({ ...prevState, customCondensingPrompt: value })), + setProfileThresholds: (value) => setState((prevState) => ({ ...prevState, profileThresholds: value })), } return {children} diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index d426e59252..56dd2ef426 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -207,6 +207,7 @@ describe("mergeExtensionState", () => { autoCondenseContextPercent: 100, cloudIsAuthenticated: false, sharingEnabled: false, + profileThresholds: {}, } const prevState: ExtensionState = { diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index c88005ea61..29a5731a4f 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -370,7 +370,8 @@ "hint": "Buit = utilitzar indicació per defecte" }, "autoCondenseContext": { - "name": "Activar automàticament la condensació intel·ligent de context" + "name": "Activar automàticament la condensació intel·ligent de context", + "description": "Quan està activat, Roo condensarà automàticament el context quan s'assoleixi el llindar. Quan està desactivat, encara pots activar manualment la condensació de context." }, "openTabs": { "label": "Límit de context de pestanyes obertes", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Límit de lectures simultànies", "description": "Nombre màxim de fitxers que l'eina 'read_file' pot processar simultàniament. Els valors més alts poden accelerar la lectura de múltiples fitxers petits però augmenten l'ús de memòria." + }, + "condensingThreshold": { + "label": "Llindar d'activació de condensació", + "selectProfile": "Configura el llindar per al perfil", + "defaultProfile": "Per defecte global (tots els perfils)", + "defaultDescription": "Quan el context arribi a aquest percentatge, es condensarà automàticament per a tots els perfils tret que tinguin configuracions personalitzades", + "profileDescription": "Llindar personalitzat només per a aquest perfil (substitueix el per defecte global)", + "inheritDescription": "Aquest perfil hereta el llindar per defecte global ({{threshold}}%)", + "usesGlobal": "(utilitza global {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 27a7486436..5e5ec292fe 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -370,7 +370,8 @@ "hint": "Leer = Standard-Prompt verwenden" }, "autoCondenseContext": { - "name": "Intelligente Kontextkomprimierung automatisch auslösen" + "name": "Intelligente Kontextkomprimierung automatisch auslösen", + "description": "Wenn aktiviert, wird Roo automatisch den Kontext komprimieren, wenn der Schwellenwert erreicht wird. Wenn deaktiviert, können Sie die Kontextkomprimierung weiterhin manuell auslösen." }, "openTabs": { "label": "Geöffnete Tabs Kontextlimit", @@ -393,6 +394,15 @@ "description": "Roo liest diese Anzahl von Zeilen, wenn das Modell keine Start-/Endwerte angibt. Wenn diese Zahl kleiner als die Gesamtzahl der Zeilen ist, erstellt Roo einen Zeilennummernindex der Codedefinitionen. Spezialfälle: -1 weist Roo an, die gesamte Datei zu lesen (ohne Indexierung), und 0 weist an, keine Zeilen zu lesen und nur Zeilenindizes für minimalen Kontext bereitzustellen. Niedrigere Werte minimieren die anfängliche Kontextnutzung und ermöglichen präzise nachfolgende Zeilenbereich-Lesungen. Explizite Start-/End-Anfragen sind von dieser Einstellung nicht begrenzt.", "lines": "Zeilen", "always_full_read": "Immer die gesamte Datei lesen" + }, + "condensingThreshold": { + "label": "Schwellenwert für Kontextkomprimierung", + "selectProfile": "Profil für Schwellenwert konfigurieren", + "defaultProfile": "Globaler Standard (alle Profile)", + "defaultDescription": "Wenn der Kontext diesen Prozentsatz erreicht, wird er automatisch für alle Profile komprimiert, es sei denn, sie haben benutzerdefinierte Einstellungen", + "profileDescription": "Benutzerdefinierter Schwellenwert nur für dieses Profil (überschreibt globalen Standard)", + "inheritDescription": "Dieses Profil erbt den globalen Standard-Schwellenwert ({{threshold}}%)", + "usesGlobal": "(verwendet global {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index b8e51afc50..dc14b06439 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -370,7 +370,8 @@ "hint": "Empty = use default prompt" }, "autoCondenseContext": { - "name": "Automatically trigger intelligent context condensing" + "name": "Automatically trigger intelligent context condensing", + "description": "When enabled, Roo will automatically condense the context when the threshold is reached. When disabled, you can still manually trigger context condensing." }, "openTabs": { "label": "Open tabs context limit", @@ -393,6 +394,15 @@ "description": "Roo reads this number of lines when the model omits start/end values. If this number is less than the file's total, Roo generates a line number index of code definitions. Special cases: -1 instructs Roo to read the entire file (without indexing), and 0 instructs it to read no lines and provides line indexes only for minimal context. Lower values minimize initial context usage, enabling precise subsequent line-range reads. Explicit start/end requests are not limited by this setting.", "lines": "lines", "always_full_read": "Always read entire file" + }, + "condensingThreshold": { + "label": "Condensing Trigger Threshold", + "selectProfile": "Configure threshold for profile", + "defaultProfile": "Global Default (all profiles)", + "defaultDescription": "When context reaches this percentage, it will be automatically condensed for all profiles unless they have custom settings", + "profileDescription": "Custom threshold for this profile only (overrides global default)", + "inheritDescription": "This profile inherits the global default threshold ({{threshold}}%)", + "usesGlobal": "(uses global {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index db8b4736eb..042addff47 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -370,7 +370,8 @@ "hint": "Vacío = usar prompt predeterminado" }, "autoCondenseContext": { - "name": "Activar automáticamente la condensación inteligente de contexto" + "name": "Activar automáticamente la condensación inteligente de contexto", + "description": "Cuando está habilitado, Roo condensará automáticamente el contexto cuando se alcance el umbral. Cuando está deshabilitado, aún puedes activar manualmente la condensación de contexto." }, "openTabs": { "label": "Límite de contexto de pestañas abiertas", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Límite de lecturas simultáneas", "description": "Número máximo de archivos que la herramienta 'read_file' puede procesar simultáneamente. Valores más altos pueden acelerar la lectura de múltiples archivos pequeños pero aumentan el uso de memoria." + }, + "condensingThreshold": { + "label": "Umbral de condensación de contexto", + "selectProfile": "Configurar umbral para perfil", + "defaultProfile": "Predeterminado global (todos los perfiles)", + "defaultDescription": "Cuando el contexto alcance este porcentaje, se condensará automáticamente para todos los perfiles a menos que tengan configuraciones personalizadas", + "profileDescription": "Umbral personalizado solo para este perfil (anula el predeterminado global)", + "inheritDescription": "Este perfil hereda el umbral predeterminado global ({{threshold}}%)", + "usesGlobal": "(usa global {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 0bf837accb..9c253b9053 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -370,7 +370,8 @@ "hint": "Vide = utiliser le prompt par défaut" }, "autoCondenseContext": { - "name": "Déclencher automatiquement la condensation intelligente du contexte" + "name": "Déclencher automatiquement la condensation intelligente du contexte", + "description": "Lorsque cette option est activée, Roo condensera automatiquement le contexte lorsque le seuil est atteint. Lorsqu'elle est désactivée, vous pouvez toujours déclencher manuellement la condensation du contexte." }, "openTabs": { "label": "Limite de contexte des onglets ouverts", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Limite de lectures simultanées", "description": "Nombre maximum de fichiers que l'outil 'read_file' peut traiter simultanément. Des valeurs plus élevées peuvent accélérer la lecture de plusieurs petits fichiers mais augmentent l'utilisation de la mémoire." + }, + "condensingThreshold": { + "label": "Seuil de condensation du contexte", + "selectProfile": "Configurer le seuil pour le profil", + "defaultProfile": "Par défaut global (tous les profils)", + "defaultDescription": "Lorsque le contexte atteint ce pourcentage, il sera automatiquement condensé pour tous les profils sauf s'ils ont des paramètres personnalisés", + "profileDescription": "Seuil personnalisé pour ce profil uniquement (remplace le défaut global)", + "inheritDescription": "Ce profil hérite du seuil par défaut global ({{threshold}}%)", + "usesGlobal": "(utilise global {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index fec1b27007..b0989cc1e0 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -370,7 +370,8 @@ "hint": "खाली = डिफ़ॉल्ट प्रॉम्प्ट का उपयोग करें" }, "autoCondenseContext": { - "name": "बुद्धिमान संदर्भ संघनन को स्वचालित रूप से ट्रिगर करें" + "name": "बुद्धिमान संदर्भ संघनन को स्वचालित रूप से ट्रिगर करें", + "description": "जब सक्षम हो, तो Roo स्वचालित रूप से संदर्भ को संघनित करेगा जब सीमा पहुंच जाएगी। जब अक्षम हो, तो आप अभी भी मैन्युअल रूप से संदर्भ संघनन को ट्रिगर कर सकते हैं।" }, "openTabs": { "label": "खुले टैब संदर्भ सीमा", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "एक साथ फ़ाइल पढ़ने की सीमा", "description": "'read_file' टूल द्वारा एक साथ प्रोसेस की जा सकने वाली अधिकतम फ़ाइलों की संख्या। उच्च मान कई छोटी फ़ाइलों को पढ़ने की गति बढ़ा सकते हैं लेकिन मेमोरी उपयोग बढ़ा देते हैं।" + }, + "condensingThreshold": { + "label": "संघनन ट्रिगर सीमा", + "selectProfile": "प्रोफ़ाइल के लिए सीमा कॉन्फ़िगर करें", + "defaultProfile": "वैश्विक डिफ़ॉल्ट (सभी प्रोफ़ाइल)", + "defaultDescription": "जब संदर्भ इस प्रतिशत तक पहुंचता है, तो यह सभी प्रोफ़ाइल के लिए स्वचालित रूप से संघनित हो जाएगा जब तक कि उनकी कस्टम सेटिंग्स न हों", + "profileDescription": "केवल इस प्रोफ़ाइल के लिए कस्टम सीमा (वैश्विक डिफ़ॉल्ट को ओवरराइड करता है)", + "inheritDescription": "यह प्रोफ़ाइल वैश्विक डिफ़ॉल्ट सीमा को इनहेरिट करता है ({{threshold}}%)", + "usesGlobal": "(वैश्विक {{threshold}}% का उपयोग करता है)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 6d6a8e93b1..a7a6eb71e6 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -374,7 +374,17 @@ "hint": "Kosong = gunakan prompt default" }, "autoCondenseContext": { - "name": "Secara otomatis memicu kondensasi konteks cerdas" + "name": "Secara otomatis memicu kondensasi konteks cerdas", + "description": "Ketika diaktifkan, Roo akan secara otomatis mengondensasi konteks ketika ambang batas tercapai. Ketika dinonaktifkan, kamu masih dapat memicu kondensasi konteks secara manual." + }, + "condensingThreshold": { + "label": "Ambang Batas Pemicu Kondensasi", + "selectProfile": "Konfigurasi ambang batas untuk profil", + "defaultProfile": "Default Global (semua profil)", + "defaultDescription": "Ketika konteks mencapai persentase ini, akan secara otomatis dikondensasi untuk semua profil kecuali mereka memiliki pengaturan kustom", + "profileDescription": "Ambang batas kustom untuk profil ini saja (menimpa default global)", + "inheritDescription": "Profil ini mewarisi ambang batas default global ({{threshold}}%)", + "usesGlobal": "(menggunakan global {{threshold}}%)" }, "openTabs": { "label": "Batas konteks tab terbuka", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index fcb389a4a7..3e8ec7ef66 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -370,7 +370,8 @@ "hint": "Vuoto = usa prompt predefinito" }, "autoCondenseContext": { - "name": "Attiva automaticamente la condensazione intelligente del contesto" + "name": "Attiva automaticamente la condensazione intelligente del contesto", + "description": "Quando abilitato, Roo condenserà automaticamente il contesto quando viene raggiunta la soglia. Quando disabilitato, puoi ancora attivare manualmente la condensazione del contesto." }, "openTabs": { "label": "Limite contesto schede aperte", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Limite letture simultanee", "description": "Numero massimo di file che lo strumento 'read_file' può elaborare contemporaneamente. Valori più alti possono velocizzare la lettura di più file piccoli ma aumentano l'utilizzo della memoria." + }, + "condensingThreshold": { + "label": "Soglia di attivazione condensazione", + "selectProfile": "Configura soglia per profilo", + "defaultProfile": "Predefinito globale (tutti i profili)", + "defaultDescription": "Quando il contesto raggiunge questa percentuale, verrà automaticamente condensato per tutti i profili a meno che non abbiano impostazioni personalizzate", + "profileDescription": "Soglia personalizzata solo per questo profilo (sovrascrive il predefinito globale)", + "inheritDescription": "Questo profilo eredita la soglia predefinita globale ({{threshold}}%)", + "usesGlobal": "(usa globale {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index eabd751308..13146f7071 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -370,7 +370,8 @@ "hint": "空 = デフォルトプロンプトを使用" }, "autoCondenseContext": { - "name": "インテリジェントなコンテキスト圧縮を自動的にトリガーする" + "name": "インテリジェントなコンテキスト圧縮を自動的にトリガーする", + "description": "有効にすると、Rooは閾値に達したときに自動的にコンテキストを圧縮します。無効にすると、手動でコンテキスト圧縮をトリガーできます。" }, "openTabs": { "label": "オープンタブコンテキスト制限", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "同時ファイル読み取り制限", "description": "read_file ツールが同時に処理できるファイルの最大数。値を高くすると複数の小さなファイルの読み取りが速くなる可能性がありますが、メモリ使用量が増加します。" + }, + "condensingThreshold": { + "label": "圧縮トリガーしきい値", + "selectProfile": "プロファイルのしきい値を設定", + "defaultProfile": "グローバルデフォルト(全プロファイル)", + "defaultDescription": "コンテキストがこの割合に達すると、カスタム設定がない限り、すべてのプロファイルで自動的に圧縮されます", + "profileDescription": "このプロファイルのみのカスタムしきい値(グローバルデフォルトを上書き)", + "inheritDescription": "このプロファイルはグローバルデフォルトしきい値を継承します({{threshold}}%)", + "usesGlobal": "(グローバル {{threshold}}% を使用)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 68ca2a963c..a5acab97cd 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -370,7 +370,8 @@ "hint": "비어있음 = 기본 프롬프트 사용" }, "autoCondenseContext": { - "name": "지능적 컨텍스트 압축 자동 트리거" + "name": "지능적 컨텍스트 압축 자동 트리거", + "description": "활성화되면 Roo는 임계값에 도달했을 때 자동으로 컨텍스트를 압축합니다. 비활성화되면 여전히 수동으로 컨텍스트 압축을 트리거할 수 있습니다." }, "openTabs": { "label": "열린 탭 컨텍스트 제한", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "동시 파일 읽기 제한", "description": "read_file 도구가 동시에 처리할 수 있는 최대 파일 수입니다. 높은 값은 여러 작은 파일을 읽는 속도를 높일 수 있지만 메모리 사용량이 증가합니다." + }, + "condensingThreshold": { + "label": "압축 트리거 임계값", + "selectProfile": "프로필 임계값 구성", + "defaultProfile": "글로벌 기본값 (모든 프로필)", + "defaultDescription": "컨텍스트가 이 비율에 도달하면 사용자 정의 설정이 없는 한 모든 프로필에 대해 자동으로 압축됩니다", + "profileDescription": "이 프로필만을 위한 사용자 정의 임계값 (글로벌 기본값 재정의)", + "inheritDescription": "이 프로필은 글로벌 기본 임계값을 상속합니다 ({{threshold}}%)", + "usesGlobal": "(글로벌 {{threshold}}% 사용)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 996c0c673c..56a95c8686 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -370,7 +370,8 @@ "hint": "Leeg = gebruik standaardprompt" }, "autoCondenseContext": { - "name": "Automatisch intelligente contextcompressie activeren" + "name": "Automatisch intelligente contextcompressie activeren", + "description": "Wanneer ingeschakeld, zal Roo automatisch de context comprimeren wanneer de drempel wordt bereikt. Wanneer uitgeschakeld, kun je nog steeds handmatig contextcompressie activeren." }, "openTabs": { "label": "Limiet geopende tabbladen in context", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Limiet gelijktijdige bestandslezingen", "description": "Maximum aantal bestanden dat de 'read_file' tool tegelijkertijd kan verwerken. Hogere waarden kunnen het lezen van meerdere kleine bestanden versnellen maar verhogen het geheugengebruik." + }, + "condensingThreshold": { + "label": "Compressie trigger drempelwaarde", + "selectProfile": "Drempelwaarde voor profiel configureren", + "defaultProfile": "Globale standaard (alle profielen)", + "defaultDescription": "Wanneer de context dit percentage bereikt, wordt het automatisch gecomprimeerd voor alle profielen tenzij ze aangepaste instellingen hebben", + "profileDescription": "Aangepaste drempelwaarde alleen voor dit profiel (overschrijft globale standaard)", + "inheritDescription": "Dit profiel erft de globale standaard drempelwaarde ({{threshold}}%)", + "usesGlobal": "(gebruikt globaal {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index cf4421e00e..57604862d2 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -370,7 +370,8 @@ "hint": "Puste = użyj domyślnego monitu" }, "autoCondenseContext": { - "name": "Automatycznie wyzwalaj inteligentną kondensację kontekstu" + "name": "Automatycznie wyzwalaj inteligentną kondensację kontekstu", + "description": "Gdy włączone, Roo automatycznie skondensuje kontekst po osiągnięciu progu. Gdy wyłączone, nadal możesz ręcznie wyzwolić kondensację kontekstu." }, "openTabs": { "label": "Limit kontekstu otwartych kart", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Limit jednoczesnych odczytów", "description": "Maksymalna liczba plików, które narzędzie 'read_file' może przetwarzać jednocześnie. Wyższe wartości mogą przyspieszyć odczyt wielu małych plików, ale zwiększają zużycie pamięci." + }, + "condensingThreshold": { + "label": "Próg wyzwalania kondensacji", + "selectProfile": "Skonfiguruj próg dla profilu", + "defaultProfile": "Globalny domyślny (wszystkie profile)", + "defaultDescription": "Gdy kontekst osiągnie ten procent, zostanie automatycznie skondensowany dla wszystkich profili, chyba że mają niestandardowe ustawienia", + "profileDescription": "Niestandardowy próg tylko dla tego profilu (zastępuje globalny domyślny)", + "inheritDescription": "Ten profil dziedziczy globalny domyślny próg ({{threshold}}%)", + "usesGlobal": "(używa globalnego {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 229419dd23..2d5c370dea 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -370,7 +370,8 @@ "hint": "Vazio = usar prompt padrão" }, "autoCondenseContext": { - "name": "Acionar automaticamente a condensação inteligente de contexto" + "name": "Acionar automaticamente a condensação inteligente de contexto", + "description": "Quando habilitado, o Roo condensará automaticamente o contexto quando o limite for atingido. Quando desabilitado, você ainda pode acionar manualmente a condensação de contexto." }, "openTabs": { "label": "Limite de contexto de abas abertas", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Limite de leituras simultâneas", "description": "Número máximo de arquivos que a ferramenta 'read_file' pode processar simultaneamente. Valores mais altos podem acelerar a leitura de vários arquivos pequenos, mas aumentam o uso de memória." + }, + "condensingThreshold": { + "label": "Limite de Ativação de Condensação", + "selectProfile": "Configurar limite para perfil", + "defaultProfile": "Padrão Global (todos os perfis)", + "defaultDescription": "Quando o contexto atingir essa porcentagem, será automaticamente condensado para todos os perfis, a menos que tenham configurações personalizadas", + "profileDescription": "Limite personalizado apenas para este perfil (substitui o padrão global)", + "inheritDescription": "Este perfil herda o limite padrão global ({{threshold}}%)", + "usesGlobal": "(usa global {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index dcce5e5b1a..97f282faf7 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -370,7 +370,8 @@ "hint": "Пусто = использовать промпт по умолчанию" }, "autoCondenseContext": { - "name": "Автоматически запускать интеллектуальное сжатие контекста" + "name": "Автоматически запускать интеллектуальное сжатие контекста", + "description": "Когда включено, Roo будет автоматически сжимать контекст при достижении порога. Когда отключено, вы все еще можете вручную запускать сжатие контекста." }, "openTabs": { "label": "Лимит контекста открытых вкладок", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Лимит одновременного чтения", "description": "Максимальное количество файлов, которые инструмент 'read_file' может обрабатывать одновременно. Более высокие значения могут ускорить чтение нескольких небольших файлов, но увеличивают использование памяти." + }, + "condensingThreshold": { + "label": "Порог запуска сжатия", + "selectProfile": "Настроить порог для профиля", + "defaultProfile": "Глобальный по умолчанию (все профили)", + "defaultDescription": "Когда контекст достигнет этого процента, он будет автоматически сжат для всех профилей, если у них нет пользовательских настроек", + "profileDescription": "Пользовательский порог только для этого профиля (переопределяет глобальный по умолчанию)", + "inheritDescription": "Этот профиль наследует глобальный порог по умолчанию ({{threshold}}%)", + "usesGlobal": "(использует глобальный {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index f8f53ae21c..95f38f6eb4 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -370,7 +370,8 @@ "hint": "Boş = varsayılan promptu kullan" }, "autoCondenseContext": { - "name": "Akıllı bağlam sıkıştırmayı otomatik olarak tetikle" + "name": "Akıllı bağlam sıkıştırmayı otomatik olarak tetikle", + "description": "Etkinleştirildiğinde, Roo eşiğe ulaşıldığında bağlamı otomatik olarak sıkıştırır. Devre dışı bırakıldığında, bağlam sıkıştırmayı hala manuel olarak tetikleyebilirsiniz." }, "openTabs": { "label": "Açık sekmeler bağlam sınırı", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Eşzamanlı dosya okuma sınırı", "description": "'read_file' aracının aynı anda işleyebileceği maksimum dosya sayısı. Daha yüksek değerler birden çok küçük dosyanın okunmasını hızlandırabilir ancak bellek kullanımını artırır." + }, + "condensingThreshold": { + "label": "Sıkıştırma Tetikleme Eşiği", + "selectProfile": "Profil için eşik yapılandır", + "defaultProfile": "Küresel Varsayılan (tüm profiller)", + "defaultDescription": "Bağlam bu yüzdeye ulaştığında, özel ayarları olmadıkça tüm profiller için otomatik olarak sıkıştırılacak", + "profileDescription": "Sadece bu profil için özel eşik (küresel varsayılanı geçersiz kılar)", + "inheritDescription": "Bu profil küresel varsayılan eşiği miras alır ({{threshold}}%)", + "usesGlobal": "(küresel {{threshold}}% kullanır)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index edb2b386b2..1170da626c 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -370,7 +370,8 @@ "hint": "Để trống = sử dụng prompt mặc định" }, "autoCondenseContext": { - "name": "Tự động kích hoạt nén ngữ cảnh thông minh" + "name": "Tự động kích hoạt nén ngữ cảnh thông minh", + "description": "Khi được bật, Roo sẽ tự động nén ngữ cảnh khi đạt đến ngưỡng. Khi bị tắt, bạn vẫn có thể kích hoạt nén ngữ cảnh thủ công." }, "openTabs": { "label": "Giới hạn ngữ cảnh tab đang mở", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "Giới hạn đọc file đồng thời", "description": "Số lượng file tối đa mà công cụ 'read_file' có thể xử lý cùng lúc. Giá trị cao hơn có thể tăng tốc độ đọc nhiều file nhỏ nhưng sẽ tăng mức sử dụng bộ nhớ." + }, + "condensingThreshold": { + "label": "Ngưỡng kích hoạt nén", + "selectProfile": "Cấu hình ngưỡng cho hồ sơ", + "defaultProfile": "Mặc định toàn cục (tất cả hồ sơ)", + "defaultDescription": "Khi ngữ cảnh đạt đến tỷ lệ phần trăm này, nó sẽ được tự động nén cho tất cả hồ sơ trừ khi chúng có cài đặt tùy chỉnh", + "profileDescription": "Ngưỡng tùy chỉnh chỉ cho hồ sơ này (ghi đè mặc định toàn cục)", + "inheritDescription": "Hồ sơ này kế thừa ngưỡng mặc định toàn cục ({{threshold}}%)", + "usesGlobal": "(sử dụng toàn cục {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 51ae2269e4..5297f5280c 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -370,7 +370,8 @@ "hint": "留空 = 使用默认提示词" }, "autoCondenseContext": { - "name": "自动触发智能上下文压缩" + "name": "自动触发智能上下文压缩", + "description": "启用时,Roo 将在达到阈值时自动压缩上下文。禁用时,您仍可以手动触发上下文压缩。" }, "openTabs": { "label": "标签页数量限制", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "并发文件读取限制", "description": "read_file 工具可以同时处理的最大文件数。较高的值可能会加快读取多个小文件的速度,但会增加内存使用量。" + }, + "condensingThreshold": { + "label": "压缩触发阈值", + "selectProfile": "配置配置文件阈值", + "defaultProfile": "全局默认(所有配置文件)", + "defaultDescription": "当上下文达到此百分比时,将自动为所有配置文件压缩,除非它们有自定义设置", + "profileDescription": "仅此配置文件的自定义阈值(覆盖全局默认)", + "inheritDescription": "此配置文件继承全局默认阈值({{threshold}}%)", + "usesGlobal": "(使用全局 {{threshold}}%)" } }, "terminal": { diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 07544879cd..0d5a4fe07e 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -370,7 +370,8 @@ "hint": "留空 = 使用預設提示" }, "autoCondenseContext": { - "name": "自動觸發智慧上下文壓縮" + "name": "自動觸發智慧上下文壓縮", + "description": "啟用時,Roo 會在達到閾值時自動壓縮上下文。停用時,您仍可手動觸發上下文壓縮。" }, "openTabs": { "label": "開啟分頁的上下文限制", @@ -393,6 +394,15 @@ "maxConcurrentFileReads": { "label": "並行檔案讀取限制", "description": "read_file 工具可以同時處理的最大檔案數。較高的值可能會加快讀取多個小檔案的速度,但會增加記憶體使用量。" + }, + "condensingThreshold": { + "label": "壓縮觸發閾值", + "selectProfile": "設定檔案閾值", + "defaultProfile": "全域預設(所有檔案)", + "defaultDescription": "當上下文達到此百分比時,將自動為所有檔案壓縮,除非它們有自訂設定", + "profileDescription": "僅此檔案的自訂閾值(覆蓋全域預設)", + "inheritDescription": "此檔案繼承全域預設閾值({{threshold}}%)", + "usesGlobal": "(使用全域 {{threshold}}%)" } }, "terminal": {