Skip to content

Commit 576864b

Browse files
committed
feat: add compact prompt mode for local LLMs
- Add compactPromptMode boolean setting to ProviderSettings type - Implement compact prompt generation in SYSTEM_PROMPT function - Add UI toggle in LM Studio and Ollama provider settings - Create reusable CompactPromptControl component - Add translation strings for the new feature - Include comprehensive tests for compact prompt functionality This feature addresses issue #7550 by providing a minimal prompt option that reduces context size and improves response times for local LLMs that have slower token generation speeds.
1 parent c7d7ad8 commit 576864b

File tree

8 files changed

+319
-1
lines changed

8 files changed

+319
-1
lines changed

packages/types/src/provider-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const baseProviderSettingsSchema = z.object({
103103
modelTemperature: z.number().nullish(),
104104
rateLimitSeconds: z.number().optional(),
105105
consecutiveMistakeLimit: z.number().min(0).optional(),
106+
compactPromptMode: z.boolean().optional(),
106107

107108
// Model reasoning.
108109
enableReasoningEffort: z.boolean().optional(),

src/core/prompts/__tests__/system-prompt.spec.ts

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,194 @@ describe("SYSTEM_PROMPT", () => {
672672
expect(prompt).toContain("## update_todo_list")
673673
})
674674

675+
describe("Compact Prompt Mode", () => {
676+
it("should generate a compact prompt when compactPromptMode is true", async () => {
677+
const prompt = await SYSTEM_PROMPT(
678+
mockContext,
679+
"/test/path",
680+
false, // supportsComputerUse
681+
undefined, // mcpHub
682+
undefined, // diffStrategy
683+
undefined, // browserViewportSize
684+
defaultModeSlug, // mode
685+
undefined, // customModePrompts
686+
undefined, // customModes
687+
undefined, // globalCustomInstructions
688+
undefined, // diffEnabled
689+
experiments,
690+
true, // enableMcpServerCreation
691+
undefined, // language
692+
undefined, // rooIgnoreInstructions
693+
undefined, // partialReadsEnabled
694+
undefined, // settings
695+
undefined, // todoList
696+
undefined, // modelId
697+
true, // compactPromptMode
698+
)
699+
700+
// Compact prompt should be significantly shorter
701+
const normalPrompt = await SYSTEM_PROMPT(
702+
mockContext,
703+
"/test/path",
704+
false, // supportsComputerUse
705+
undefined, // mcpHub
706+
undefined, // diffStrategy
707+
undefined, // browserViewportSize
708+
defaultModeSlug, // mode
709+
undefined, // customModePrompts
710+
undefined, // customModes
711+
undefined, // globalCustomInstructions
712+
undefined, // diffEnabled
713+
experiments,
714+
true, // enableMcpServerCreation
715+
undefined, // language
716+
undefined, // rooIgnoreInstructions
717+
undefined, // partialReadsEnabled
718+
undefined, // settings
719+
undefined, // todoList
720+
undefined, // modelId
721+
false, // compactPromptMode
722+
)
723+
724+
// Compact prompt should be shorter
725+
expect(prompt.length).toBeLessThan(normalPrompt.length)
726+
727+
// Should still contain essential sections
728+
expect(prompt).toContain("You are Roo")
729+
expect(prompt).toContain("## read_file")
730+
expect(prompt).toContain("## write_to_file")
731+
expect(prompt).toContain("## list_files")
732+
expect(prompt).toContain("## search_files")
733+
734+
// Should NOT contain non-essential sections
735+
expect(prompt).not.toContain("MCP")
736+
expect(prompt).not.toContain("browser")
737+
expect(prompt).not.toContain("CAPABILITIES")
738+
expect(prompt).not.toContain("MODES")
739+
expect(prompt).not.toContain("execute_command") // Execute command is not included in compact mode for architect mode
740+
})
741+
742+
it("should generate a normal prompt when compactPromptMode is false", async () => {
743+
const prompt = await SYSTEM_PROMPT(
744+
mockContext,
745+
"/test/path",
746+
false, // supportsComputerUse
747+
undefined, // mcpHub
748+
undefined, // diffStrategy
749+
undefined, // browserViewportSize
750+
defaultModeSlug, // mode
751+
undefined, // customModePrompts
752+
undefined, // customModes
753+
undefined, // globalCustomInstructions
754+
undefined, // diffEnabled
755+
experiments,
756+
true, // enableMcpServerCreation
757+
undefined, // language
758+
undefined, // rooIgnoreInstructions
759+
undefined, // partialReadsEnabled
760+
undefined, // settings
761+
undefined, // todoList
762+
undefined, // modelId
763+
false, // compactPromptMode
764+
)
765+
766+
// Normal prompt should contain all sections
767+
expect(prompt).toContain("CAPABILITIES")
768+
expect(prompt).toContain("MODES")
769+
expect(prompt).toContain("RULES")
770+
expect(prompt).toContain("SYSTEM INFORMATION")
771+
expect(prompt).toContain("OBJECTIVE")
772+
})
773+
774+
it("should generate a normal prompt when compactPromptMode is undefined", async () => {
775+
const prompt = await SYSTEM_PROMPT(
776+
mockContext,
777+
"/test/path",
778+
false, // supportsComputerUse
779+
undefined, // mcpHub
780+
undefined, // diffStrategy
781+
undefined, // browserViewportSize
782+
defaultModeSlug, // mode
783+
undefined, // customModePrompts
784+
undefined, // customModes
785+
undefined, // globalCustomInstructions
786+
undefined, // diffEnabled
787+
experiments,
788+
true, // enableMcpServerCreation
789+
undefined, // language
790+
undefined, // rooIgnoreInstructions
791+
undefined, // partialReadsEnabled
792+
undefined, // settings
793+
undefined, // todoList
794+
undefined, // modelId
795+
undefined, // compactPromptMode
796+
)
797+
798+
// Should generate normal prompt by default
799+
expect(prompt).toContain("CAPABILITIES")
800+
expect(prompt).toContain("MODES")
801+
expect(prompt).toContain("RULES")
802+
expect(prompt).toContain("SYSTEM INFORMATION")
803+
expect(prompt).toContain("OBJECTIVE")
804+
})
805+
806+
it("should not include diff tool in compact mode even when diffEnabled is true", async () => {
807+
const prompt = await SYSTEM_PROMPT(
808+
mockContext,
809+
"/test/path",
810+
false, // supportsComputerUse
811+
undefined, // mcpHub
812+
new MultiSearchReplaceDiffStrategy(), // diffStrategy
813+
undefined, // browserViewportSize
814+
defaultModeSlug, // mode
815+
undefined, // customModePrompts
816+
undefined, // customModes
817+
undefined, // globalCustomInstructions
818+
true, // diffEnabled
819+
experiments,
820+
true, // enableMcpServerCreation
821+
undefined, // language
822+
undefined, // rooIgnoreInstructions
823+
undefined, // partialReadsEnabled
824+
undefined, // settings
825+
undefined, // todoList
826+
undefined, // modelId
827+
true, // compactPromptMode
828+
)
829+
830+
// Compact mode doesn't include diff tool to keep prompt minimal
831+
expect(prompt).not.toContain("apply_diff")
832+
})
833+
834+
it("should maintain custom instructions in compact mode", async () => {
835+
const prompt = await SYSTEM_PROMPT(
836+
mockContext,
837+
"/test/path",
838+
false, // supportsComputerUse
839+
undefined, // mcpHub
840+
undefined, // diffStrategy
841+
undefined, // browserViewportSize
842+
defaultModeSlug, // mode
843+
undefined, // customModePrompts
844+
undefined, // customModes
845+
"Test global instructions", // globalCustomInstructions
846+
undefined, // diffEnabled
847+
experiments,
848+
true, // enableMcpServerCreation
849+
undefined, // language
850+
undefined, // rooIgnoreInstructions
851+
undefined, // partialReadsEnabled
852+
undefined, // settings
853+
undefined, // todoList
854+
undefined, // modelId
855+
true, // compactPromptMode
856+
)
857+
858+
// Should still include custom instructions
859+
expect(prompt).toContain("Test global instructions")
860+
})
861+
})
862+
675863
afterAll(() => {
676864
vi.restoreAllMocks()
677865
})

src/core/prompts/system.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export const SYSTEM_PROMPT = async (
153153
settings?: SystemPromptSettings,
154154
todoList?: TodoItem[],
155155
modelId?: string,
156+
compactPromptMode?: boolean,
156157
): Promise<string> => {
157158
if (!context) {
158159
throw new Error("Extension context is required for generating system prompt")
@@ -202,6 +203,62 @@ ${fileCustomSystemPrompt}
202203
${customInstructions}`
203204
}
204205

206+
// If compact prompt mode is enabled, generate a minimal prompt
207+
if (compactPromptMode) {
208+
const { roleDefinition, baseInstructions } = getModeSelection(mode, promptComponent, customModes)
209+
const codeIndexManager = CodeIndexManager.getInstance(context, cwd)
210+
211+
// Generate a compact prompt with only essential sections
212+
const compactPrompt = `${roleDefinition}
213+
214+
====
215+
216+
TOOL USE
217+
218+
You have access to tools that are executed upon user approval. Use one tool per message.
219+
220+
# Tools
221+
222+
${getToolDescriptionsForMode(
223+
mode,
224+
cwd,
225+
false, // Disable computer use for compact mode
226+
codeIndexManager,
227+
undefined, // No diff strategy in compact mode
228+
undefined, // No browser viewport
229+
undefined, // No MCP in compact mode
230+
customModes,
231+
experiments,
232+
partialReadsEnabled,
233+
settings,
234+
false, // No MCP server creation
235+
modelId,
236+
)}
237+
238+
====
239+
240+
RULES
241+
242+
- Project directory: ${cwd.toPosix()}
243+
- Use tools efficiently to accomplish tasks
244+
- Wait for user response after each tool use
245+
- Be concise and direct in responses
246+
247+
====
248+
249+
OBJECTIVE
250+
251+
Complete the user's task efficiently using available tools.
252+
253+
${await addCustomInstructions(baseInstructions, globalCustomInstructions || "", cwd, mode, {
254+
language: language ?? formatLanguage(vscode.env.language),
255+
rooIgnoreInstructions,
256+
settings,
257+
})}`
258+
259+
return compactPrompt
260+
}
261+
205262
// If diff is disabled, don't pass the diffStrategy
206263
const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined
207264

src/core/task/Task.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
22422242
apiConfiguration,
22432243
} = state ?? {}
22442244

2245+
// Check if we should use compact prompt mode for local LLM providers
2246+
const isLocalLLMProvider =
2247+
this.apiConfiguration.apiProvider === "lmstudio" || this.apiConfiguration.apiProvider === "ollama"
2248+
const shouldUseCompactPrompt = isLocalLLMProvider && this.apiConfiguration.compactPromptMode
2249+
22452250
return await (async () => {
22462251
const provider = this.providerRef.deref()
22472252

@@ -2276,6 +2281,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
22762281
},
22772282
undefined, // todoList
22782283
this.api.getModel().id,
2284+
shouldUseCompactPrompt,
22792285
)
22802286
})()
22812287
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from "react"
2+
import { useAppTranslation } from "@src/i18n/TranslationContext"
3+
4+
interface CompactPromptControlProps {
5+
compactPromptMode?: boolean
6+
onChange: (value: boolean) => void
7+
providerName?: string
8+
}
9+
10+
export const CompactPromptControl: React.FC<CompactPromptControlProps> = ({
11+
compactPromptMode = false,
12+
onChange,
13+
providerName,
14+
}) => {
15+
const { t } = useAppTranslation()
16+
17+
// Determine the correct translation key prefix based on provider
18+
const translationPrefix = providerName === "LM Studio" ? "providers.lmStudio" : "providers.ollama"
19+
20+
return (
21+
<div className="flex flex-col gap-2">
22+
<div className="flex items-center justify-between">
23+
<label htmlFor="compact-prompt-mode" className="font-medium">
24+
{t(`settings:${translationPrefix}.compactPrompt.title`)}
25+
</label>
26+
<input
27+
id="compact-prompt-mode"
28+
type="checkbox"
29+
checked={compactPromptMode}
30+
onChange={(e) => onChange(e.target.checked)}
31+
className="cursor-pointer"
32+
/>
33+
</div>
34+
<p className="text-sm text-vscode-descriptionForeground">
35+
{t(`settings:${translationPrefix}.compactPrompt.description`)}
36+
</p>
37+
{providerName && (
38+
<p className="text-xs text-vscode-descriptionForeground italic">
39+
{t(`settings:${translationPrefix}.compactPrompt.providerNote`, { provider: providerName })}
40+
</p>
41+
)}
42+
</div>
43+
)
44+
}

webview-ui/src/components/settings/providers/LMStudio.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { vscode } from "@src/utils/vscode"
1313

1414
import { inputEventTransform } from "../transforms"
1515
import { ModelRecord } from "@roo/api"
16+
import { CompactPromptControl } from "../CompactPromptControl"
1617

1718
type LMStudioProps = {
1819
apiConfiguration: ProviderSettings
@@ -207,6 +208,11 @@ export const LMStudio = ({ apiConfiguration, setApiConfigurationField }: LMStudi
207208
)}
208209
</>
209210
)}
211+
<CompactPromptControl
212+
compactPromptMode={apiConfiguration?.compactPromptMode}
213+
onChange={(value) => setApiConfigurationField("compactPromptMode", value)}
214+
providerName="LM Studio"
215+
/>
210216
<div className="text-sm text-vscode-descriptionForeground">
211217
<Trans
212218
i18nKey="settings:providers.lmStudio.description"

webview-ui/src/components/settings/providers/Ollama.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useRouterModels } from "@src/components/ui/hooks/useRouterModels"
1111
import { vscode } from "@src/utils/vscode"
1212

1313
import { inputEventTransform } from "../transforms"
14+
import { CompactPromptControl } from "../CompactPromptControl"
1415

1516
type OllamaProps = {
1617
apiConfiguration: ProviderSettings
@@ -118,6 +119,11 @@ export const Ollama = ({ apiConfiguration, setApiConfigurationField }: OllamaPro
118119
))}
119120
</VSCodeRadioGroup>
120121
)}
122+
<CompactPromptControl
123+
compactPromptMode={apiConfiguration?.compactPromptMode}
124+
onChange={(value) => setApiConfigurationField("compactPromptMode", value)}
125+
providerName="Ollama"
126+
/>
121127
<div className="text-sm text-vscode-descriptionForeground">
122128
{t("settings:providers.ollama.description")}
123129
<span className="text-vscode-errorForeground ml-1">{t("settings:providers.ollama.warning")}</span>

0 commit comments

Comments
 (0)