diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 45d4a7e780..29769ff696 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -103,6 +103,7 @@ const baseProviderSettingsSchema = z.object({ modelTemperature: z.number().nullish(), rateLimitSeconds: z.number().optional(), consecutiveMistakeLimit: z.number().min(0).optional(), + compactPromptMode: z.boolean().optional(), // Model reasoning. enableReasoningEffort: z.boolean().optional(), diff --git a/src/core/prompts/__tests__/system-prompt.spec.ts b/src/core/prompts/__tests__/system-prompt.spec.ts index f2d5469da4..ee877a03fb 100644 --- a/src/core/prompts/__tests__/system-prompt.spec.ts +++ b/src/core/prompts/__tests__/system-prompt.spec.ts @@ -672,6 +672,194 @@ describe("SYSTEM_PROMPT", () => { expect(prompt).toContain("## update_todo_list") }) + describe("Compact Prompt Mode", () => { + it("should generate a compact prompt when compactPromptMode is true", async () => { + const prompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + undefined, // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes + undefined, // globalCustomInstructions + undefined, // diffEnabled + experiments, + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled + undefined, // settings + undefined, // todoList + undefined, // modelId + true, // compactPromptMode + ) + + // Compact prompt should be significantly shorter + const normalPrompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + undefined, // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes + undefined, // globalCustomInstructions + undefined, // diffEnabled + experiments, + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled + undefined, // settings + undefined, // todoList + undefined, // modelId + false, // compactPromptMode + ) + + // Compact prompt should be shorter + expect(prompt.length).toBeLessThan(normalPrompt.length) + + // Should still contain essential sections + expect(prompt).toContain("You are Roo") + expect(prompt).toContain("## read_file") + expect(prompt).toContain("## write_to_file") + expect(prompt).toContain("## list_files") + expect(prompt).toContain("## search_files") + + // Should NOT contain non-essential sections + expect(prompt).not.toContain("MCP") + expect(prompt).not.toContain("browser") + expect(prompt).not.toContain("CAPABILITIES") + expect(prompt).not.toContain("MODES") + expect(prompt).not.toContain("execute_command") // Execute command is not included in compact mode for architect mode + }) + + it("should generate a normal prompt when compactPromptMode is false", async () => { + const prompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + undefined, // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes + undefined, // globalCustomInstructions + undefined, // diffEnabled + experiments, + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled + undefined, // settings + undefined, // todoList + undefined, // modelId + false, // compactPromptMode + ) + + // Normal prompt should contain all sections + expect(prompt).toContain("CAPABILITIES") + expect(prompt).toContain("MODES") + expect(prompt).toContain("RULES") + expect(prompt).toContain("SYSTEM INFORMATION") + expect(prompt).toContain("OBJECTIVE") + }) + + it("should generate a normal prompt when compactPromptMode is undefined", async () => { + const prompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + undefined, // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes + undefined, // globalCustomInstructions + undefined, // diffEnabled + experiments, + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled + undefined, // settings + undefined, // todoList + undefined, // modelId + undefined, // compactPromptMode + ) + + // Should generate normal prompt by default + expect(prompt).toContain("CAPABILITIES") + expect(prompt).toContain("MODES") + expect(prompt).toContain("RULES") + expect(prompt).toContain("SYSTEM INFORMATION") + expect(prompt).toContain("OBJECTIVE") + }) + + it("should not include diff tool in compact mode even when diffEnabled is true", async () => { + const prompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + new MultiSearchReplaceDiffStrategy(), // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes + undefined, // globalCustomInstructions + true, // diffEnabled + experiments, + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled + undefined, // settings + undefined, // todoList + undefined, // modelId + true, // compactPromptMode + ) + + // Compact mode doesn't include diff tool to keep prompt minimal + expect(prompt).not.toContain("apply_diff") + }) + + it("should maintain custom instructions in compact mode", async () => { + const prompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + undefined, // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes + "Test global instructions", // globalCustomInstructions + undefined, // diffEnabled + experiments, + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled + undefined, // settings + undefined, // todoList + undefined, // modelId + true, // compactPromptMode + ) + + // Should still include custom instructions + expect(prompt).toContain("Test global instructions") + }) + }) + afterAll(() => { vi.restoreAllMocks() }) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 3cc327c815..520b6d9270 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -153,6 +153,7 @@ export const SYSTEM_PROMPT = async ( settings?: SystemPromptSettings, todoList?: TodoItem[], modelId?: string, + compactPromptMode?: boolean, ): Promise => { if (!context) { throw new Error("Extension context is required for generating system prompt") @@ -202,6 +203,62 @@ ${fileCustomSystemPrompt} ${customInstructions}` } + // If compact prompt mode is enabled, generate a minimal prompt + if (compactPromptMode) { + const { roleDefinition, baseInstructions } = getModeSelection(mode, promptComponent, customModes) + const codeIndexManager = CodeIndexManager.getInstance(context, cwd) + + // Generate a compact prompt with only essential sections + const compactPrompt = `${roleDefinition} + +==== + +TOOL USE + +You have access to tools that are executed upon user approval. Use one tool per message. + +# Tools + +${getToolDescriptionsForMode( + mode, + cwd, + false, // Disable computer use for compact mode + codeIndexManager, + undefined, // No diff strategy in compact mode + undefined, // No browser viewport + undefined, // No MCP in compact mode + customModes, + experiments, + partialReadsEnabled, + settings, + false, // No MCP server creation + modelId, +)} + +==== + +RULES + +- Project directory: ${cwd.toPosix()} +- Use tools efficiently to accomplish tasks +- Wait for user response after each tool use +- Be concise and direct in responses + +==== + +OBJECTIVE + +Complete the user's task efficiently using available tools. + +${await addCustomInstructions(baseInstructions, globalCustomInstructions || "", cwd, mode, { + language: language ?? formatLanguage(vscode.env.language), + rooIgnoreInstructions, + settings, +})}` + + return compactPrompt + } + // If diff is disabled, don't pass the diffStrategy const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 655983db20..1606cecce3 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2242,6 +2242,11 @@ export class Task extends EventEmitter implements TaskLike { apiConfiguration, } = state ?? {} + // Check if we should use compact prompt mode for local LLM providers + const isLocalLLMProvider = + this.apiConfiguration.apiProvider === "lmstudio" || this.apiConfiguration.apiProvider === "ollama" + const shouldUseCompactPrompt = isLocalLLMProvider && this.apiConfiguration.compactPromptMode + return await (async () => { const provider = this.providerRef.deref() @@ -2276,6 +2281,7 @@ export class Task extends EventEmitter implements TaskLike { }, undefined, // todoList this.api.getModel().id, + shouldUseCompactPrompt, ) })() } diff --git a/webview-ui/src/components/settings/CompactPromptControl.tsx b/webview-ui/src/components/settings/CompactPromptControl.tsx new file mode 100644 index 0000000000..94801bc10e --- /dev/null +++ b/webview-ui/src/components/settings/CompactPromptControl.tsx @@ -0,0 +1,44 @@ +import React from "react" +import { useAppTranslation } from "@src/i18n/TranslationContext" + +interface CompactPromptControlProps { + compactPromptMode?: boolean + onChange: (value: boolean) => void + providerName?: string +} + +export const CompactPromptControl: React.FC = ({ + compactPromptMode = false, + onChange, + providerName, +}) => { + const { t } = useAppTranslation() + + // Determine the correct translation key prefix based on provider + const translationPrefix = providerName === "LM Studio" ? "providers.lmStudio" : "providers.ollama" + + return ( +
+
+ + onChange(e.target.checked)} + className="cursor-pointer" + /> +
+

+ {t(`settings:${translationPrefix}.compactPrompt.description`)} +

+ {providerName && ( +

+ {t(`settings:${translationPrefix}.compactPrompt.providerNote`, { provider: providerName })} +

+ )} +
+ ) +} diff --git a/webview-ui/src/components/settings/providers/LMStudio.tsx b/webview-ui/src/components/settings/providers/LMStudio.tsx index e3401aa62c..6b29186453 100644 --- a/webview-ui/src/components/settings/providers/LMStudio.tsx +++ b/webview-ui/src/components/settings/providers/LMStudio.tsx @@ -13,6 +13,7 @@ import { vscode } from "@src/utils/vscode" import { inputEventTransform } from "../transforms" import { ModelRecord } from "@roo/api" +import { CompactPromptControl } from "../CompactPromptControl" type LMStudioProps = { apiConfiguration: ProviderSettings @@ -207,6 +208,11 @@ export const LMStudio = ({ apiConfiguration, setApiConfigurationField }: LMStudi )} )} + setApiConfigurationField("compactPromptMode", value)} + providerName="LM Studio" + />
)} + setApiConfigurationField("compactPromptMode", value)} + providerName="Ollama" + />
{t("settings:providers.ollama.description")} {t("settings:providers.ollama.warning")} diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 33fba24b8e..924a0821ab 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -368,7 +368,12 @@ "draftModelDesc": "Draft model must be from the same model family for speculative decoding to work correctly.", "selectDraftModel": "Select Draft Model", "noModelsFound": "No draft models found. Please ensure LM Studio is running with Server Mode enabled.", - "description": "LM Studio allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide. You will also need to start LM Studio's local server feature to use it with this extension. Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected." + "description": "LM Studio allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide. You will also need to start LM Studio's local server feature to use it with this extension. Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected.", + "compactPrompt": { + "title": "Compact Prompt Mode", + "description": "Reduces the system prompt size for faster response times with local LLMs. This removes non-essential sections while keeping core functionality.", + "providerNote": "Recommended for {{provider}} to prevent timeouts with slower local models" + } }, "ollama": { "baseUrl": "Base URL (optional)", @@ -376,6 +381,11 @@ "description": "Ollama allows you to run models locally on your computer. For instructions on how to get started, see their quickstart guide.", "warning": "Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected." }, + "compactPrompt": { + "title": "Compact Prompt Mode", + "description": "Reduces the system prompt size for faster response times with local LLMs. This removes non-essential sections while keeping core functionality.", + "providerNote": "Recommended for {{provider}} to prevent timeouts with slower local models" + }, "unboundApiKey": "Unbound API Key", "getUnboundApiKey": "Get Unbound API Key", "unboundRefreshModelsSuccess": "Models list updated! You can now select from the latest models.",