mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
Persist provider/model between plan/act mode (#1525)
Fix truncation algorithm Fix Fix
This commit is contained in:
parent
fc5d0bdb5a
commit
ac53dbb122
4 changed files with 128 additions and 26 deletions
|
|
@ -1272,10 +1272,16 @@ export class Cline {
|
|||
|
||||
// This is the most reliable way to know when we're close to hitting the context window.
|
||||
if (totalTokens >= maxAllowedSize) {
|
||||
// Since the user may switch between models with different context windows, truncating half may not be enough (ie if switching from claude 200k to deepseek 64k, half truncation will only remove 100k tokens, but we need to remove much more)
|
||||
// So if totalTokens/2 is greater than maxAllowedSize, we truncate 3/4 instead of 1/2
|
||||
// FIXME: truncating the conversation in a way that is optimal for prompt caching AND takes into account multi-context window complexity is something we need to improve
|
||||
const keep = totalTokens / 2 > maxAllowedSize ? "quarter" : "half"
|
||||
|
||||
// NOTE: it's okay that we overwriteConversationHistory in resume task since we're only ever removing the last user message and not anything in the middle which would affect this range
|
||||
this.conversationHistoryDeletedRange = getNextTruncationRange(
|
||||
this.apiConversationHistory,
|
||||
this.conversationHistoryDeletedRange,
|
||||
keep,
|
||||
)
|
||||
await this.saveClineMessages() // saves task history item which we use to keep track of conversation history deleted range
|
||||
// await this.overwriteApiConversationHistory(truncatedMessages)
|
||||
|
|
|
|||
|
|
@ -55,13 +55,21 @@ truncated = getTruncatedMessages(messages, deletedRange);
|
|||
export function getNextTruncationRange(
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
currentDeletedRange: [number, number] | undefined = undefined,
|
||||
keep: "half" | "quarter" = "half",
|
||||
): [number, number] {
|
||||
// Since we always keep the first message, currentDeletedRange[0] will always be 1 (for now until we have a smarter truncation algorithm)
|
||||
const rangeStartIndex = 1
|
||||
const startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1
|
||||
|
||||
// Remove half of user-assistant pairs
|
||||
const messagesToRemove = Math.floor((messages.length - startOfRest) / 4) * 2 // Keep even number
|
||||
let messagesToRemove: number
|
||||
if (keep === "half") {
|
||||
// Remove half of user-assistant pairs
|
||||
messagesToRemove = Math.floor((messages.length - startOfRest) / 4) * 2 // Keep even number
|
||||
} else {
|
||||
// Remove 3/4 of user-assistant pairs
|
||||
messagesToRemove = Math.floor((messages.length - startOfRest) / 8) * 3 * 2
|
||||
}
|
||||
|
||||
let rangeEndIndex = startOfRest + messagesToRemove - 1
|
||||
|
||||
// Make sure the last message being removed is a user message, so that the next message after the initial task message is an assistant message. This preservers the user-assistant-user-assistant structure.
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@ type GlobalStateKey =
|
|||
| "vsCodeLmModelSelector"
|
||||
| "localeLanguage"
|
||||
| "userInfo"
|
||||
| "previousModeApiProvider"
|
||||
| "previousModeModelId"
|
||||
| "previousModeModelInfo"
|
||||
|
||||
export const GlobalFileNames = {
|
||||
apiConversationHistory: "api_conversation_history.json",
|
||||
|
|
@ -501,6 +504,71 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
case "chatSettings":
|
||||
if (message.chatSettings) {
|
||||
const didSwitchToActMode = message.chatSettings.mode === "act"
|
||||
|
||||
// Get previous model info that we will revert to after saving current mode api info
|
||||
const {
|
||||
apiConfiguration,
|
||||
previousModeApiProvider: newApiProvider,
|
||||
previousModeModelId: newModelId,
|
||||
previousModeModelInfo: newModelInfo,
|
||||
} = await this.getState()
|
||||
|
||||
// Save the last model used in this mode
|
||||
await this.updateGlobalState("previousModeApiProvider", apiConfiguration.apiProvider)
|
||||
switch (apiConfiguration.apiProvider) {
|
||||
case "anthropic":
|
||||
case "bedrock":
|
||||
case "vertex":
|
||||
case "gemini":
|
||||
await this.updateGlobalState("previousModeModelId", apiConfiguration.apiModelId)
|
||||
break
|
||||
case "openrouter":
|
||||
await this.updateGlobalState("previousModeModelId", apiConfiguration.openRouterModelId)
|
||||
await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openRouterModelInfo)
|
||||
break
|
||||
case "vscode-lm":
|
||||
await this.updateGlobalState("previousModeModelId", apiConfiguration.vsCodeLmModelSelector)
|
||||
break
|
||||
case "openai":
|
||||
await this.updateGlobalState("previousModeModelId", apiConfiguration.openAiModelId)
|
||||
break
|
||||
case "ollama":
|
||||
await this.updateGlobalState("previousModeModelId", apiConfiguration.ollamaModelId)
|
||||
break
|
||||
case "lmstudio":
|
||||
await this.updateGlobalState("previousModeModelId", apiConfiguration.lmStudioModelId)
|
||||
break
|
||||
}
|
||||
|
||||
// Restore the model used in previous mode
|
||||
if (newApiProvider && newModelId) {
|
||||
await this.updateGlobalState("apiProvider", newApiProvider)
|
||||
switch (newApiProvider) {
|
||||
case "anthropic":
|
||||
case "bedrock":
|
||||
case "vertex":
|
||||
case "gemini":
|
||||
await this.updateGlobalState("apiModelId", newModelId)
|
||||
break
|
||||
case "openrouter":
|
||||
await this.updateGlobalState("openRouterModelId", newModelId)
|
||||
await this.updateGlobalState("openRouterModelInfo", newModelInfo)
|
||||
break
|
||||
case "vscode-lm":
|
||||
await this.updateGlobalState("vsCodeLmModelSelector", newModelId)
|
||||
break
|
||||
case "openai":
|
||||
await this.updateGlobalState("openAiModelId", newModelId)
|
||||
break
|
||||
case "ollama":
|
||||
await this.updateGlobalState("ollamaModelId", newModelId)
|
||||
break
|
||||
case "lmstudio":
|
||||
await this.updateGlobalState("lmStudioModelId", newModelId)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
await this.updateGlobalState("chatSettings", message.chatSettings)
|
||||
await this.postStateToWebview()
|
||||
if (this.cline) {
|
||||
|
|
@ -1198,10 +1266,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
browserSettings,
|
||||
chatSettings,
|
||||
userInfo,
|
||||
authToken,
|
||||
localeLanguage,
|
||||
} = await this.getState()
|
||||
|
||||
const authToken = await this.getSecret("authToken")
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
apiConfiguration,
|
||||
|
|
@ -1310,6 +1378,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
vsCodeLmModelSelector,
|
||||
localeLanguage,
|
||||
userInfo,
|
||||
authToken,
|
||||
previousModeApiProvider,
|
||||
previousModeModelId,
|
||||
previousModeModelInfo,
|
||||
] = await Promise.all([
|
||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||
|
|
@ -1346,6 +1418,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
this.getGlobalState("vsCodeLmModelSelector") as Promise<vscode.LanguageModelChatSelector | undefined>,
|
||||
this.getGlobalState("localeLanguage") as Promise<string | undefined>,
|
||||
this.getGlobalState("userInfo") as Promise<UserInfo | undefined>,
|
||||
this.getSecret("authToken") as Promise<string | undefined>,
|
||||
this.getGlobalState("previousModeApiProvider") as Promise<ApiProvider | undefined>,
|
||||
this.getGlobalState("previousModeModelId") as Promise<string | undefined>,
|
||||
this.getGlobalState("previousModeModelInfo") as Promise<ModelInfo | undefined>,
|
||||
])
|
||||
|
||||
let apiProvider: ApiProvider
|
||||
|
|
@ -1400,6 +1476,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
chatSettings: chatSettings || DEFAULT_CHAT_SETTINGS,
|
||||
localeLanguage,
|
||||
userInfo,
|
||||
authToken,
|
||||
previousModeApiProvider,
|
||||
previousModeModelId,
|
||||
previousModeModelInfo,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -586,20 +586,40 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
[updateCursorPosition],
|
||||
)
|
||||
|
||||
// Separate the API config submission logic
|
||||
const submitApiConfig = useCallback(() => {
|
||||
const apiValidationResult = validateApiConfiguration(apiConfiguration)
|
||||
const modelIdValidationResult = validateModelId(apiConfiguration, openRouterModels)
|
||||
|
||||
if (!apiValidationResult && !modelIdValidationResult) {
|
||||
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
|
||||
} else {
|
||||
vscode.postMessage({ type: "getLatestState" })
|
||||
}
|
||||
}, [apiConfiguration, openRouterModels])
|
||||
|
||||
const onModeToggle = useCallback(() => {
|
||||
if (textAreaDisabled) return
|
||||
const newMode = chatSettings.mode === "plan" ? "act" : "plan"
|
||||
vscode.postMessage({
|
||||
type: "chatSettings",
|
||||
chatSettings: {
|
||||
mode: newMode,
|
||||
},
|
||||
})
|
||||
// Focus the textarea after mode toggle with slight delay
|
||||
let changeModeDelay = 0
|
||||
if (showModelSelector) {
|
||||
// user has model selector open, so we should save it before switching modes
|
||||
submitApiConfig()
|
||||
changeModeDelay = 250 // necessary to let the api config update (we send message and wait for it to be saved) FIXME: this is a hack and we ideally should check for api config changes, then wait for it to be saved, before switching modes
|
||||
}
|
||||
setTimeout(() => {
|
||||
textAreaRef.current?.focus()
|
||||
}, 100)
|
||||
}, [chatSettings.mode, textAreaDisabled])
|
||||
const newMode = chatSettings.mode === "plan" ? "act" : "plan"
|
||||
vscode.postMessage({
|
||||
type: "chatSettings",
|
||||
chatSettings: {
|
||||
mode: newMode,
|
||||
},
|
||||
})
|
||||
// Focus the textarea after mode toggle with slight delay
|
||||
setTimeout(() => {
|
||||
textAreaRef.current?.focus()
|
||||
}, 100)
|
||||
}, changeModeDelay)
|
||||
}, [chatSettings.mode, textAreaDisabled, showModelSelector, submitApiConfig])
|
||||
|
||||
const handleContextButtonClick = useCallback(() => {
|
||||
if (textAreaDisabled) return
|
||||
|
|
@ -644,18 +664,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
updateHighlights()
|
||||
}, [inputValue, textAreaDisabled, handleInputChange, updateHighlights])
|
||||
|
||||
// Separate the API config submission logic
|
||||
const submitApiConfig = useCallback(() => {
|
||||
const apiValidationResult = validateApiConfiguration(apiConfiguration)
|
||||
const modelIdValidationResult = validateModelId(apiConfiguration, openRouterModels)
|
||||
|
||||
if (!apiValidationResult && !modelIdValidationResult) {
|
||||
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
|
||||
} else {
|
||||
vscode.postMessage({ type: "getLatestState" })
|
||||
}
|
||||
}, [apiConfiguration, openRouterModels])
|
||||
|
||||
// Use an effect to detect menu close
|
||||
useEffect(() => {
|
||||
if (prevShowModelSelector.current && !showModelSelector) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue