mirror of
https://github.com/cline/cline.git
synced 2026-08-29 03:52:41 +08:00
Remove dead "Use compact prompt" toggle from LM Studio provider settings (#12551)
* Remove dead 'Use compact prompt' toggle from LM Studio settings The compact system prompt option was never wired up in the SDK-based extension: the customPrompt value was stored in state and echoed back to the webview, but nothing in the session factory or SDK ever read it to alter the system prompt. Remove the checkbox (only shown for the LM Studio provider) and all the dead state/proto plumbing behind it. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Add changeset for compact prompt toggle removal Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Reserve removed custom_prompt field number/name in Settings proto Teach generate-state-proto.mjs to preserve reserved statements in the generated Secrets/Settings messages so removed fields keep their wire identity reserved across regenerations, and reserve field 150 and the custom_prompt name (plus the name in UpdateSettingsRequest). Addresses Greptile review feedback on #12551. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Never assign reserved proto field numbers to new Settings fields If the highest-numbered field was removed and reserved, the generator would hand that same number to the next new field, emitting both a reserved statement and a live field at the same number. Parse reserved numbers (including ranges) from the existing message, skip them when assigning new numbers, and fail fast if an active field collides with a reservation. Addresses Bugbot review feedback on #12551. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Format generate-state-proto.mjs Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Remove the non-functional "Use compact prompt" toggle from LM Studio provider settings
|
||||
@@ -112,6 +112,8 @@ message Secrets {
|
||||
// in src/shared/storage/state-keys.ts and use the scripts/generate-state-proto.mjs
|
||||
// script to regenerate this list.
|
||||
message Settings {
|
||||
reserved 150; // was custom_prompt (removed - compact prompt setting no longer supported)
|
||||
reserved "custom_prompt";
|
||||
optional string lite_llm_base_url = 1;
|
||||
optional bool lite_llm_use_prompt_cache = 2;
|
||||
optional string anthropic_base_url = 4;
|
||||
@@ -254,7 +256,6 @@ message Settings {
|
||||
optional string preferred_language = 145;
|
||||
optional PlanActMode mode = 147;
|
||||
optional FocusChainSettings focus_chain_settings = 149;
|
||||
optional string custom_prompt = 150;
|
||||
optional bool hooks_enabled = 152;
|
||||
optional bool subagents_enabled = 153;
|
||||
optional bool background_edit_enabled = 155;
|
||||
@@ -392,7 +393,8 @@ message UpdateSettingsRequest {
|
||||
reserved 38; // was skills_enabled (removed - now always enabled)
|
||||
reserved 43; // was lazy_teammate_mode_enabled (removed)
|
||||
reserved 12; // was terminal_output_line_limit (removed; SDK command output limits are character-based)
|
||||
reserved "native_tool_call_enabled", "cline_web_tools_enabled", "enable_parallel_tool_calling", "double_check_completion_enabled";
|
||||
reserved 19; // was custom_prompt (removed - compact prompt setting no longer supported)
|
||||
reserved "native_tool_call_enabled", "cline_web_tools_enabled", "enable_parallel_tool_calling", "double_check_completion_enabled", "custom_prompt";
|
||||
|
||||
Metadata metadata = 1;
|
||||
optional ModelsApiConfiguration api_configuration = 2;
|
||||
@@ -409,7 +411,6 @@ message UpdateSettingsRequest {
|
||||
reserved 16; // was strict_plan_mode_enabled (removed)
|
||||
optional FocusChainSettings focus_chain_settings = 17;
|
||||
optional bool use_auto_condense = 18;
|
||||
optional string custom_prompt = 19;
|
||||
optional BrowserSettingsUpdate browser_settings = 20;
|
||||
optional string default_terminal_profile = 21;
|
||||
optional bool yolo_mode_toggled = 22;
|
||||
|
||||
@@ -297,28 +297,86 @@ function parseProtoMessageFieldNumbers(protoContent, messageName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load field number mappings from existing proto file
|
||||
* Parse `reserved` statements from an existing proto message definition so they
|
||||
* survive regeneration (removed fields must keep their numbers/names reserved).
|
||||
*/
|
||||
function parseProtoMessageReservedStatements(protoContent, messageName) {
|
||||
const messageRegex = new RegExp(`message\\s+${messageName}\\s*\\{([^}]*(?:\\{[^}]*\\}[^}]*)*)\\}`, "s")
|
||||
const match = protoContent.match(messageRegex)
|
||||
|
||||
if (!match) {
|
||||
return []
|
||||
}
|
||||
|
||||
return [...match[1].matchAll(/^[ \t]*(reserved\b[^\n]*)$/gm)].map((m) => m[1].trim())
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the set of field numbers covered by `reserved` statements
|
||||
* (single numbers, comma lists, and `N to M` ranges; name reservations are ignored).
|
||||
*/
|
||||
function parseReservedFieldNumbers(reservedStatements) {
|
||||
const reservedNumbers = new Set()
|
||||
|
||||
for (const statement of reservedStatements) {
|
||||
// Strip the keyword, trailing semicolon, and any comment
|
||||
const body = statement
|
||||
.replace(/^reserved\b/, "")
|
||||
.replace(/;.*$/, "")
|
||||
.trim()
|
||||
|
||||
// Name reservations (quoted) carry no numbers
|
||||
if (body.includes('"') || body.includes("'")) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const part of body.split(",")) {
|
||||
const rangeMatch = part.trim().match(/^(\d+)\s+to\s+(\d+|max)$/)
|
||||
if (rangeMatch) {
|
||||
const start = Number.parseInt(rangeMatch[1], 10)
|
||||
// "to max" reserves everything upward; represent with Infinity
|
||||
const end = rangeMatch[2] === "max" ? Number.POSITIVE_INFINITY : Number.parseInt(rangeMatch[2], 10)
|
||||
if (end === Number.POSITIVE_INFINITY) {
|
||||
throw new Error(`"reserved ${part.trim()}" leaves no field numbers available for new fields`)
|
||||
}
|
||||
for (let n = start; n <= end; n++) {
|
||||
reservedNumbers.add(n)
|
||||
}
|
||||
} else if (/^\d+$/.test(part.trim())) {
|
||||
reservedNumbers.add(Number.parseInt(part.trim(), 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reservedNumbers
|
||||
}
|
||||
|
||||
/**
|
||||
* Load field number mappings and reserved statements from existing proto file
|
||||
*/
|
||||
async function loadFieldNumbersFromProto() {
|
||||
try {
|
||||
const protoContent = await fs.readFile(STATE_PROTO_PATH, "utf-8")
|
||||
const secrets = parseProtoMessageFieldNumbers(protoContent, "Secrets")
|
||||
const settings = parseProtoMessageFieldNumbers(protoContent, "Settings")
|
||||
const secretsReserved = parseProtoMessageReservedStatements(protoContent, "Secrets")
|
||||
const settingsReserved = parseProtoMessageReservedStatements(protoContent, "Settings")
|
||||
|
||||
console.log(` Found ${Object.keys(secrets).length} existing Secrets fields`)
|
||||
console.log(` Found ${Object.keys(settings).length} existing Settings fields`)
|
||||
|
||||
return { Secrets: secrets, Settings: settings }
|
||||
return { Secrets: secrets, Settings: settings, SecretsReserved: secretsReserved, SettingsReserved: settingsReserved }
|
||||
} catch {
|
||||
// Proto file doesn't exist, start fresh
|
||||
return { Secrets: {}, Settings: {} }
|
||||
return { Secrets: {}, Settings: {}, SecretsReserved: [], SettingsReserved: [] }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign field numbers, preserving existing assignments and adding new ones
|
||||
* Assign field numbers, preserving existing assignments and adding new ones.
|
||||
* Reserved numbers are never handed out to new fields.
|
||||
*/
|
||||
function assignFieldNumbers(fields, existingNumbers, startNumber = 1) {
|
||||
function assignFieldNumbers(fields, existingNumbers, startNumber = 1, reservedNumbers = new Set()) {
|
||||
const result = {}
|
||||
let nextNumber = startNumber
|
||||
|
||||
@@ -347,13 +405,21 @@ function assignFieldNumbers(fields, existingNumbers, startNumber = 1) {
|
||||
|
||||
const existingFieldNumber = normalizedFieldNumber ?? rawFieldNumber
|
||||
if (existingFieldNumber !== undefined) {
|
||||
if (reservedNumbers.has(existingFieldNumber)) {
|
||||
throw new Error(
|
||||
`Field "${field.name}" uses number ${existingFieldNumber}, which is also covered by a reserved statement. Remove the field or the reservation.`,
|
||||
)
|
||||
}
|
||||
result[field.name] = existingFieldNumber
|
||||
}
|
||||
}
|
||||
|
||||
// Assign new numbers for new fields
|
||||
// Assign new numbers for new fields, skipping reserved numbers
|
||||
for (const field of fields) {
|
||||
if (result[field.name] === undefined) {
|
||||
while (reservedNumbers.has(nextNumber)) {
|
||||
nextNumber++
|
||||
}
|
||||
result[field.name] = nextNumber++
|
||||
}
|
||||
}
|
||||
@@ -364,9 +430,14 @@ function assignFieldNumbers(fields, existingNumbers, startNumber = 1) {
|
||||
/**
|
||||
* Generate proto message definition
|
||||
*/
|
||||
function generateProtoMessage(messageName, fields, fieldNumbers) {
|
||||
function generateProtoMessage(messageName, fields, fieldNumbers, reservedStatements = []) {
|
||||
const lines = [`message ${messageName} {`]
|
||||
|
||||
// Re-emit reserved statements so removed field numbers/names stay reserved
|
||||
for (const statement of reservedStatements) {
|
||||
lines.push(` ${statement}`)
|
||||
}
|
||||
|
||||
// Sort fields by field number for consistent output
|
||||
const sortedFields = [...fields].sort((a, b) => fieldNumbers[a.name] - fieldNumbers[b.name])
|
||||
|
||||
@@ -385,13 +456,13 @@ function generateProtoMessage(messageName, fields, fieldNumbers) {
|
||||
/**
|
||||
* Generate Secrets message from SECRETS_KEYS
|
||||
*/
|
||||
function generateSecretsMessage(secretsKeys, fieldNumbers) {
|
||||
function generateSecretsMessage(secretsKeys, fieldNumbers, reservedStatements) {
|
||||
const fields = secretsKeys.map((key) => ({
|
||||
name: key,
|
||||
protoType: "string",
|
||||
}))
|
||||
|
||||
return generateProtoMessage("Secrets", fields, fieldNumbers)
|
||||
return generateProtoMessage("Secrets", fields, fieldNumbers, reservedStatements)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,17 +501,28 @@ async function main() {
|
||||
// Load existing field numbers from proto file
|
||||
const existingFieldNumbers = await loadFieldNumbersFromProto()
|
||||
|
||||
// Assign field numbers (preserving existing, adding new ones)
|
||||
// Assign field numbers (preserving existing, adding new ones, never reusing reserved ones)
|
||||
const secretsFieldNumbers = assignFieldNumbers(
|
||||
secretsKeys.map((k) => ({ name: k })),
|
||||
existingFieldNumbers.Secrets,
|
||||
1,
|
||||
parseReservedFieldNumbers(existingFieldNumbers.SecretsReserved),
|
||||
)
|
||||
const settingsFieldNumbers = assignFieldNumbers(
|
||||
settingsFields,
|
||||
existingFieldNumbers.Settings,
|
||||
1,
|
||||
parseReservedFieldNumbers(existingFieldNumbers.SettingsReserved),
|
||||
)
|
||||
const settingsFieldNumbers = assignFieldNumbers(settingsFields, existingFieldNumbers.Settings, 1)
|
||||
|
||||
// Generate messages
|
||||
const secretsMessage = generateSecretsMessage(secretsKeys, secretsFieldNumbers)
|
||||
const settingsMessage = generateProtoMessage("Settings", settingsFields, settingsFieldNumbers)
|
||||
const secretsMessage = generateSecretsMessage(secretsKeys, secretsFieldNumbers, existingFieldNumbers.SecretsReserved)
|
||||
const settingsMessage = generateProtoMessage(
|
||||
"Settings",
|
||||
settingsFields,
|
||||
settingsFieldNumbers,
|
||||
existingFieldNumbers.SettingsReserved,
|
||||
)
|
||||
|
||||
// Read existing proto file
|
||||
let protoContent = await fs.readFile(STATE_PROTO_PATH, "utf-8")
|
||||
|
||||
@@ -19,5 +19,4 @@ export interface ApiProviderInfo {
|
||||
providerId: string
|
||||
model: ApiHandlerModel
|
||||
mode: Mode
|
||||
customPrompt?: string // "compact"
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ export async function getStateToPostToWebview(controller: {
|
||||
const isNewUser = stateManager.getGlobalStateKey("isNewUser")
|
||||
const welcomeViewCompleted = !!stateManager.getGlobalStateKey("welcomeViewCompleted")
|
||||
|
||||
const customPrompt = stateManager.getGlobalSettingsKey("customPrompt")
|
||||
const mcpResponsesCollapsed = stateManager.getGlobalStateKey("mcpResponsesCollapsed")
|
||||
const maxConsecutiveMistakes = stateManager.getGlobalSettingsKey("maxConsecutiveMistakes")
|
||||
const favoritedModelIds = stateManager.getGlobalStateKey("favoritedModelIds")
|
||||
@@ -152,7 +151,6 @@ export async function getStateToPostToWebview(controller: {
|
||||
onboardingModels,
|
||||
mcpResponsesCollapsed,
|
||||
maxConsecutiveMistakes,
|
||||
customPrompt,
|
||||
taskHistory: processedTaskHistory,
|
||||
shouldShowAnnouncement,
|
||||
favoritedModelIds,
|
||||
|
||||
@@ -188,12 +188,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett
|
||||
setCompactionStrategyGlobally(strategy)
|
||||
}
|
||||
|
||||
// Update custom prompt choice
|
||||
if (request.customPrompt !== undefined) {
|
||||
const value = request.customPrompt === "compact" ? "compact" : undefined
|
||||
controller.stateManager.setGlobalState("customPrompt", value)
|
||||
}
|
||||
|
||||
// Update browser settings
|
||||
if (request.browserSettings !== undefined) {
|
||||
// Get current browser settings to preserve fields not in the request
|
||||
|
||||
@@ -37,7 +37,6 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
|
||||
planModeReasoningEffort,
|
||||
actModeReasoningEffort,
|
||||
mode,
|
||||
customPrompt,
|
||||
planModeApiProvider,
|
||||
actModeApiProvider,
|
||||
// Fields requiring special logic (telemetry, merging, etc.)
|
||||
@@ -96,10 +95,6 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
|
||||
controller.stateManager.setGlobalState("mode", converted)
|
||||
}
|
||||
|
||||
if (customPrompt === "compact") {
|
||||
controller.stateManager.setGlobalState("customPrompt", "compact")
|
||||
}
|
||||
|
||||
if (planModeApiProvider !== undefined) {
|
||||
const converted = convertProtoToApiProvider(planModeApiProvider)
|
||||
controller.stateManager.setGlobalState("planModeApiProvider", converted)
|
||||
|
||||
@@ -37,7 +37,6 @@ export async function updateTaskSettings(controller: Controller, request: Update
|
||||
planModeReasoningEffort,
|
||||
actModeReasoningEffort,
|
||||
mode,
|
||||
customPrompt,
|
||||
planModeApiProvider,
|
||||
actModeApiProvider,
|
||||
// Fields requiring special logic
|
||||
@@ -87,10 +86,6 @@ export async function updateTaskSettings(controller: Controller, request: Update
|
||||
controller.stateManager.setTaskSettings(taskId, "mode", converted)
|
||||
}
|
||||
|
||||
if (customPrompt === "compact") {
|
||||
controller.stateManager.setTaskSettings(taskId, "customPrompt", "compact")
|
||||
}
|
||||
|
||||
if (planModeApiProvider !== undefined) {
|
||||
const converted = convertProtoToApiProvider(planModeApiProvider)
|
||||
controller.stateManager.setTaskSettings(taskId, "planModeApiProvider", converted)
|
||||
|
||||
@@ -60,9 +60,6 @@ export async function newTask(controller: Controller, request: NewTaskRequest):
|
||||
...(request.taskSettings?.mode !== undefined && {
|
||||
mode: convertPlanActMode(request.taskSettings.mode),
|
||||
}),
|
||||
...(request.taskSettings?.customPrompt === "compact" && {
|
||||
customPrompt: "compact",
|
||||
}),
|
||||
...(request.taskSettings?.planModeApiProvider !== undefined && {
|
||||
planModeApiProvider: convertProtoToApiProvider(request.taskSettings.planModeApiProvider),
|
||||
}),
|
||||
|
||||
@@ -117,7 +117,6 @@ export interface ExtensionState {
|
||||
compactionStrategy?: string
|
||||
subagentsEnabled?: boolean
|
||||
worktreesEnabled?: ClineFeatureSetting
|
||||
customPrompt?: string
|
||||
favoritedModelIds: string[]
|
||||
// NEW: Add workspace information
|
||||
workspaceRoots: WorkspaceRoot[]
|
||||
|
||||
@@ -282,7 +282,6 @@ const USER_SETTINGS_FIELDS = {
|
||||
preferredLanguage: { default: "English" as string },
|
||||
mode: { default: "act" as Mode },
|
||||
focusChainSettings: { default: DEFAULT_FOCUS_CHAIN_SETTINGS as FocusChainSettings },
|
||||
customPrompt: { default: undefined as "compact" | undefined },
|
||||
backgroundEditEnabled: { default: false as boolean },
|
||||
optOutOfRemoteConfig: { default: false as boolean },
|
||||
showFeatureTips: { default: true as boolean },
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { useCallback, useState } from "react"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { updateSetting } from "./utils/settingsHandlers"
|
||||
|
||||
interface CustomPromptCheckboxProps {
|
||||
providerId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkbox to enable or disable the use of a compact prompt for local models providers.
|
||||
*/
|
||||
const UseCustomPromptCheckbox: React.FC<CustomPromptCheckboxProps> = ({ providerId }) => {
|
||||
const { customPrompt } = useExtensionState()
|
||||
const [isCompactPromptEnabled, setIsCompactPromptEnabled] = useState<boolean>(customPrompt === "compact")
|
||||
|
||||
const toggleCompactPrompt = useCallback((isChecked: boolean) => {
|
||||
setIsCompactPromptEnabled(isChecked)
|
||||
updateSetting("customPrompt", isChecked ? "compact" : "")
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div id={providerId}>
|
||||
<VSCodeCheckbox checked={isCompactPromptEnabled} onChange={() => toggleCompactPrompt(!isCompactPromptEnabled)}>
|
||||
Use compact prompt
|
||||
</VSCodeCheckbox>
|
||||
<div className="text-xs text-description">
|
||||
A system prompt optimized for smaller context window (e.g. 8k or less).
|
||||
<div className="text-error flex align-middle">
|
||||
<i className="codicon codicon-x" />
|
||||
Does not support Mcp and Focus Chain
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UseCustomPromptCheckbox
|
||||
@@ -3,7 +3,6 @@ import type { Mode } from "@shared/storage/types"
|
||||
import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useInterval } from "react-use"
|
||||
import UseCustomPromptCheckbox from "@/components/settings/UseCustomPromptCheckbox"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { useProviderConfig } from "@/hooks/useProviderConfig"
|
||||
import { useProviderModelSelection } from "@/hooks/useProviderModelSelection"
|
||||
@@ -194,8 +193,6 @@ export const LMStudioProvider = ({ currentMode }: LMStudioProviderProps) => {
|
||||
value={String(currentLoadedContext ?? lmStudioMaxTokens ?? "0")}
|
||||
/>
|
||||
|
||||
<UseCustomPromptCheckbox providerId="lmstudio" />
|
||||
|
||||
<div className="text-xs text-description">
|
||||
LM Studio allows you to run models locally on your computer. For instructions on how to get started, see their
|
||||
<VSCodeLink href="https://lmstudio.ai/docs" style={{ display: "inline", fontSize: "inherit" }}>
|
||||
|
||||
@@ -299,7 +299,6 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
onboardingModels: undefined,
|
||||
mcpResponsesCollapsed: false, // Default value (expanded), will be overwritten by extension state
|
||||
yoloModeToggled: false,
|
||||
customPrompt: undefined,
|
||||
useAutoCondense: false,
|
||||
compactionStrategy: "basic",
|
||||
subagentsEnabled: false,
|
||||
|
||||
Reference in New Issue
Block a user