diff --git a/CHANGELOG.md b/CHANGELOG.md index 850007a49d..85989ce8e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## [3.2.0] -- Add 'Consult Advisor' tool to let Cline ask a powerful model like o1 for help when he hits a roadblock (available with OpenRouter and Anthropic) - Add Plan/Act mode toggle to let you plan tasks with Cline before letting him get to work - Easily switch between API providers and models using a new popup menu under the chat field - Add VS Code LM API provider to run models provided by other VS Code extensions (e.g. GitHub Copilot). Shoutout to @julesmons, @RaySinner, and @MrUbens for putting this together! diff --git a/src/api/index.ts b/src/api/index.ts index f200a91b21..2ef82f8659 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,5 +1,5 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ApiConfiguration, ModelInfo, ModelType } from "../shared/api" +import { ApiConfiguration, ModelInfo } from "../shared/api" import { AnthropicHandler } from "./providers/anthropic" import { AwsBedrockHandler } from "./providers/bedrock" import { OpenRouterHandler } from "./providers/openrouter" @@ -15,9 +15,8 @@ import { MistralHandler } from "./providers/mistral" import { VsCodeLmHandler } from "./providers/vscode-lm" export interface ApiHandler { - createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], modelType?: ModelType): ApiStream + createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream getModel(): { id: string; info: ModelInfo } - getAdvisorModel?(): { id: string; info: ModelInfo } } export interface SingleCompletionHandler { diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index c84cbf0921..8c3fd1b87d 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -1,14 +1,6 @@ import { Anthropic } from "@anthropic-ai/sdk" import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming" -import { - anthropicDefaultAdvisorModelId, - anthropicDefaultModelId, - AnthropicModelId, - anthropicModels, - ApiHandlerOptions, - ModelInfo, - ModelType, -} from "../../shared/api" +import { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions, ModelInfo } from "../../shared/api" import { ApiHandler } from "../index" import { ApiStream } from "../transform/stream" @@ -24,8 +16,8 @@ export class AnthropicHandler implements ApiHandler { }) } - async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], modelType: ModelType): ApiStream { - const model = modelType === "advisor" ? this.getAdvisorModel() : this.getModel() + async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + const model = this.getModel() let stream: AnthropicStream const modelId = model.id switch (modelId) { @@ -34,20 +26,6 @@ export class AnthropicHandler implements ApiHandler { case "claude-3-5-haiku-20241022": case "claude-3-opus-20240229": case "claude-3-haiku-20240307": { - // don't use prompt caching for advisor model requests - if (modelType === "advisor") { - stream = (await this.client.messages.create({ - model: modelId, - max_tokens: model.info.maxTokens || 8192, - temperature: 0, - system: [{ text: systemPrompt, type: "text" }], - messages, - // tools, - // tool_choice: { type: "auto" }, - stream: true, - })) as any - break - } /* The latest message will be the new user message, one before will be the assistant message from a previous request, and the user message before that will be a previously cached user message. So we need to mark the latest user message as ephemeral to cache it for the next request, and mark the second to last user message as ephemeral to let the server know the last message to retrieve from the cache for the current request.. */ @@ -208,16 +186,4 @@ export class AnthropicHandler implements ApiHandler { info: anthropicModels[anthropicDefaultModelId], } } - - getAdvisorModel(): { id: string; info: ModelInfo } { - const modelId = this.options.anthropicAdvisorModelId - if (modelId && modelId in anthropicModels) { - const id = modelId as AnthropicModelId - return { id, info: anthropicModels[id] } - } - return { - id: anthropicDefaultAdvisorModelId, - info: anthropicModels[anthropicDefaultAdvisorModelId], - } - } } diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index d044caad19..e0bec2cf1c 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -2,15 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import axios from "axios" import OpenAI from "openai" import { ApiHandler } from "../" -import { - ApiHandlerOptions, - ModelInfo, - ModelType, - openRouterDefaultAdvisorModelId, - openRouterDefaultAdvisorModelInfo, - openRouterDefaultModelId, - openRouterDefaultModelInfo, -} from "../../shared/api" +import { ApiHandlerOptions, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "../../shared/api" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" import delay from "delay" @@ -31,8 +23,8 @@ export class OpenRouterHandler implements ApiHandler { }) } - async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], modelType?: ModelType): ApiStream { - const model = modelType === "advisor" ? this.getAdvisorModel() : this.getModel() + async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + const model = this.getModel() // Convert Anthropic messages to OpenAI format const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ @@ -55,11 +47,6 @@ export class OpenRouterHandler implements ApiHandler { case "anthropic/claude-3-haiku:beta": case "anthropic/claude-3-opus": case "anthropic/claude-3-opus:beta": - // don't use prompt caching for advisor model requests - if (modelType === "advisor") { - break - } - openAiMessages[0] = { role: "system", content: [ @@ -196,16 +183,4 @@ export class OpenRouterHandler implements ApiHandler { info: openRouterDefaultModelInfo, } } - - getAdvisorModel(): { id: string; info: ModelInfo } { - const modelId = this.options.openRouterAdvisorModelId - const modelInfo = this.options.openRouterAdvisorModelInfo - if (modelId && modelInfo) { - return { id: modelId, info: modelInfo } - } - return { - id: openRouterDefaultAdvisorModelId, - info: openRouterDefaultAdvisorModelInfo, - } - } } diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 42107492fa..ef6d613c00 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2,25 +2,29 @@ import { Anthropic } from "@anthropic-ai/sdk" import cloneDeep from "clone-deep" import delay from "delay" import fs from "fs/promises" +import getFolderSize from "get-folder-size" import os from "os" import pWaitFor from "p-wait-for" import * as path from "path" import { serializeError } from "serialize-error" import * as vscode from "vscode" import { ApiHandler, buildApiHandler } from "../api" -import { ApiStream } from "../api/transform/stream" +import CheckpointTracker from "../integrations/checkpoints/CheckpointTracker" import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider" import { findToolName, formatContentBlockToMarkdown } from "../integrations/misc/export-markdown" import { extractTextFromFile } from "../integrations/misc/extract-text" +import { showSystemNotification } from "../integrations/notifications" import { TerminalManager } from "../integrations/terminal/TerminalManager" import { BrowserSession } from "../services/browser/BrowserSession" import { UrlContentFetcher } from "../services/browser/UrlContentFetcher" import { listFiles } from "../services/glob/list-files" import { regexSearchFiles } from "../services/ripgrep" import { parseSourceCodeForDefinitionsTopLevel } from "../services/tree-sitter" -import { ApiConfiguration, ModelInfo } from "../shared/api" +import { ApiConfiguration } from "../shared/api" import { findLast, findLastIndex } from "../shared/array" import { AutoApprovalSettings } from "../shared/AutoApprovalSettings" +import { BrowserSettings } from "../shared/BrowserSettings" +import { ChatSettings } from "../shared/ChatSettings" import { combineApiRequests } from "../shared/combineApiRequests" import { combineCommandSequences, COMMAND_REQ_APP_STRING } from "../shared/combineCommandSequences" import { @@ -31,7 +35,6 @@ import { ClineApiReqInfo, ClineAsk, ClineAskUseMcpServer, - ClineConsultAdvisor, ClineMessage, ClineSay, ClineSayBrowserAction, @@ -44,23 +47,18 @@ import { ClineAskResponse, ClineCheckpointRestore } from "../shared/WebviewMessa import { calculateApiCost } from "../utils/cost" import { fileExistsAtPath } from "../utils/fs" import { arePathsEqual, getReadablePath } from "../utils/path" +import { fixModelHtmlEscaping, removeInvalidChars } from "../utils/string" import { AssistantMessageContent, parseAssistantMessage, ToolParamName, ToolUseName } from "./assistant-message" import { constructNewFileContent } from "./assistant-message/diff" import { parseMentions } from "./mentions" import { formatResponse } from "./prompts/responses" -import { addUserInstructions, SYSTEM_PROMPT } from "./prompts/system" -import { getNextTruncationRange, getTruncatedMessages } from "./sliding-window" import { ClineProvider, GlobalFileNames } from "./webview/ClineProvider" -import { showSystemNotification } from "../integrations/notifications" -import { removeInvalidChars } from "../utils/string" -import { fixModelHtmlEscaping } from "../utils/string" -import { OpenAiHandler } from "../api/providers/openai" -import CheckpointTracker from "../integrations/checkpoints/CheckpointTracker" -import getFolderSize from "get-folder-size" -import { BrowserSettings } from "../shared/BrowserSettings" -import { ADVISOR_SYSTEM_PROMPT } from "./prompts/advisor" -import { ChatSettings } from "../shared/ChatSettings" import { OpenRouterHandler } from "../api/providers/openrouter" +import { getNextTruncationRange, getTruncatedMessages } from "./sliding-window" +import { SYSTEM_PROMPT } from "./prompts/system" +import { addUserInstructions } from "./prompts/system" +import { OpenAiHandler } from "../api/providers/openai" +import { ApiStream } from "../api/transform/stream" const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution @@ -97,7 +95,6 @@ export class Cline { checkpointTrackerErrorMessage?: string conversationHistoryDeletedRange?: [number, number] isInitialized = false - private advisorProblem?: string isAwaitingPlanResponse = false didRespondToPlanAskBySwitchingMode = false @@ -1082,8 +1079,6 @@ export class Cline { message.ask === "followup" || message.say === "use_mcp_server" || message.ask === "use_mcp_server" || - message.say === "consult_advisor" || - message.ask === "consult_advisor" || message.say === "browser_action" || message.say === "browser_action_launch" || message.ask === "browser_action_launch" @@ -1194,78 +1189,11 @@ export class Cline { case "access_mcp_resource": case "use_mcp_tool": return this.autoApprovalSettings.actions.useMcp - case "consult_advisor": - return this.autoApprovalSettings.actions.consultAdvisor ?? false } } return false } - estimateAdvisorModelCost(problem: string) { - const truncatedConversationHistory = getTruncatedMessages( - this.apiConversationHistory, - this.conversationHistoryDeletedRange, - ) - const advisorModel = this.api.getAdvisorModel?.() - if (!advisorModel) { - return 0 - } - const advisorMessage = this.createAdvisorMessage(truncatedConversationHistory, advisorModel, problem) - const prompt = ADVISOR_SYSTEM_PROMPT() + advisorMessage - // Estimate ~3 chars per token as a rough approximation - const estimatedInputTokens = Math.ceil(prompt.length / 3) - const estimatedOutputTokens = 300 // typical response size - // Note: we don't prompt cache since we only send up one request at a time - const inputCost = (estimatedInputTokens * (advisorModel.info.inputPrice ?? 0)) / 1_000_000 // Convert from per million tokens - const outputCost = (estimatedOutputTokens * (advisorModel.info.outputPrice ?? 0)) / 1_000_000 - return inputCost + outputCost - } - - createAdvisorMessage( - truncatedConversationHistory: Anthropic.Messages.MessageParam[], - advisorModel: { - id: string - info: ModelInfo - }, - advisorProblem: string, - ) { - // Generate markdown - const markdownContent = truncatedConversationHistory - .map((message) => { - const role = message.role === "user" ? "**User:**" : "**Coding Agent:**" - const content = Array.isArray(message.content) - ? message.content.map((block) => formatContentBlockToMarkdown(block)).join("\n") - : message.content - return `${role}\n\n${content}\n\n` - }) - .join("---\n\n") - - // Don't want to send the entire conv history, just the most recent context - // Get approximate char count from token limit - const advisorContextWindow = advisorModel.info.contextWindow || 128_000 - const tokensToKeep = Math.floor(advisorContextWindow / 2) - // Estimate ~3 chars per token as a rough approximation - const charsToKeep = tokensToKeep * 3 - // Get last n chars of markdown content - const isTruncated = markdownContent.length > charsToKeep - const firstMessage = truncatedConversationHistory.at(0) - const firstMessageContent = firstMessage - ? Array.isArray(firstMessage.content) - ? firstMessage.content.map((block) => (block.type === "text" ? block.text : "")).join("\n") - : firstMessage.content - : "" - const recentContext = - (isTruncated ? `**User:**:\n\n${firstMessageContent}\n\n... (older messages removed for brevity) ...\n\n` : "") + - markdownContent.slice(-charsToKeep) - const advisorMessage = - "\n\n# The conversation history leading up to this point:\n\n" + - recentContext + - "\n\n# The problem the coding agent needs advice on:\n\n" + - advisorProblem - - return advisorMessage - } - async *attemptApiRequest(previousApiReqIndex: number): ApiStream { // Wait for MCP servers to be connected before generating system prompt await pWaitFor(() => this.providerRef.deref()?.mcpHub?.isConnecting !== true, { timeout: 10_000 }).catch(() => { @@ -1277,15 +1205,11 @@ export class Cline { throw new Error("MCP hub not available") } - const advisorModel = this.api.getAdvisorModel?.() - const supportsConsultAdvisor = advisorModel !== undefined - let systemPrompt = await SYSTEM_PROMPT( cwd, this.api.getModel().info.supportsComputerUse ?? false, mcpHub, this.browserSettings, - supportsConsultAdvisor, ) let settingsCustomInstructions = this.customInstructions?.trim() @@ -1354,26 +1278,6 @@ export class Cline { let stream = this.api.createMessage(systemPrompt, truncatedConversationHistory) - // If we're consulting the advisor, override the request - if (this.advisorProblem && advisorModel) { - const advisorMessage = this.createAdvisorMessage(truncatedConversationHistory, advisorModel, this.advisorProblem) - stream = this.api.createMessage( - ADVISOR_SYSTEM_PROMPT(), - [ - { - role: "user", - content: [ - { - type: "text", - text: advisorMessage, - }, - ], - }, - ], - "advisor", - ) - } - const iterator = stream[Symbol.asyncIterator]() try { @@ -1438,11 +1342,6 @@ export class Cline { const block = cloneDeep(this.assistantMessageContent[this.currentStreamingContentIndex]) // need to create copy bc while stream is updating the array, it could be updating the reference block properties too switch (block.type) { case "text": { - if (this.advisorProblem) { - await this.say("advisor_response", block.content, undefined, block.partial) - break - } - if (this.didRejectTool || this.didAlreadyUseTool) { break } @@ -1523,8 +1422,6 @@ export class Cline { return `[${block.name} for '${block.params.server_name}']` case "access_mcp_resource": return `[${block.name} for '${block.params.server_name}']` - case "consult_advisor": - return `[${block.name} for '${block.params.problem}']` case "ask_followup_question": return `[${block.name} for '${block.params.question}']` case "plan_mode_response": @@ -2618,85 +2515,6 @@ export class Cline { break } } - case "consult_advisor": { - const problem: string | undefined = block.params.problem - try { - if (block.partial) { - const partialMessage = JSON.stringify({ - problem: removeClosingTag("problem", problem), - advisorModelId: this.api.getAdvisorModel?.().id, - } satisfies ClineConsultAdvisor) - - if (this.shouldAutoApproveTool(block.name)) { - this.removeLastPartialMessageIfExistsWithType("ask", "consult_advisor") - await this.say("consult_advisor", partialMessage, undefined, block.partial) - } else { - this.removeLastPartialMessageIfExistsWithType("say", "consult_advisor") - await this.ask("consult_advisor", partialMessage, block.partial).catch(() => {}) - } - - break - } else { - if (!problem) { - this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("consult_advisor", "problem")) - await this.saveCheckpoint() - break - } - - this.consecutiveMistakeCount = 0 - - const estimatedCost = undefined //this.estimateAdvisorModelCost(problem) - const completeMessage = JSON.stringify({ - problem: removeClosingTag("problem", problem), - advisorModelId: this.api.getAdvisorModel?.().id, - estimatedCost, - } satisfies ClineConsultAdvisor) - - if (this.shouldAutoApproveTool(block.name)) { - this.removeLastPartialMessageIfExistsWithType("ask", "consult_advisor") - await this.say("consult_advisor", completeMessage, undefined, false) - this.consecutiveAutoApprovedRequestsCount++ - } else { - showNotificationForApprovalIfAutoApprovalEnabled( - `Cline wants to consult the Advisor model about: ${problem}`, - ) - this.removeLastPartialMessageIfExistsWithType("say", "consult_advisor") - const didApprove = await askApproval("consult_advisor", completeMessage) - if (!didApprove) { - await this.saveCheckpoint() - break - } - } - - // Update the last consult_advisor message in case the advisor model changed - const lastMessage = findLast( - this.clineMessages, - (m) => m.ask === "consult_advisor" || m.say === "consult_advisor", - ) - if (lastMessage) { - lastMessage.text = JSON.stringify({ - problem: removeClosingTag("problem", problem), - advisorModelId: this.api.getAdvisorModel?.().id, - estimatedCost, - } satisfies ClineConsultAdvisor) - } - - // now execute the tool - this.advisorProblem = problem - // await this.say("consult_advisor_request_started") - // const resourceResult = "Just try again bro." //await this.providerRef.deref()?.mcpHub?.readResource(server_name, uri) - // await this.say("consult_advisor_response", resourceResult) - pushToolResult(formatResponse.toolResult("Awaiting response from the Advisor model...")) - await this.saveCheckpoint() - break - } - } catch (error) { - await handleError("consulting advisor", error) - await this.saveCheckpoint() - break - } - } case "ask_followup_question": { const question: string | undefined = block.params.question try { @@ -3036,13 +2854,10 @@ export class Cline { // getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds // for the best UX we show a placeholder api_req_started message with a loading spinner as this happens - const advisorRequest = this.advisorProblem ? `(...conversation history)\n\n${this.advisorProblem}` : undefined await this.say( "api_req_started", JSON.stringify({ - request: - advisorRequest || - userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...", + request: userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...", }), ) @@ -3073,7 +2888,7 @@ export class Cline { // since we sent off a placeholder api_req_started message to update the webview while waiting to actually start the API request (to load potential details for example), we need to update the text of that message const lastApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === "api_req_started") this.clineMessages[lastApiReqIndex].text = JSON.stringify({ - request: advisorRequest || userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n"), + request: userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n"), } satisfies ClineApiReqInfo) await this.saveClineMessages() await this.providerRef.deref()?.postStateToWebview() @@ -3156,8 +2971,6 @@ export class Cline { this.didAutomaticallyRetryFailedApiRequest = false await this.diffViewProvider.reset() - const isCallingAdvisor = this.advisorProblem !== undefined - const stream = this.attemptApiRequest(previousApiReqIndex) // yields only if the first chunk is successful, otherwise will allow the user to retry the request (most likely due to rate limit error, which gets thrown on the first chunk) let assistantMessage = "" this.isStreaming = true @@ -3245,11 +3058,6 @@ export class Cline { await this.saveClineMessages() await this.providerRef.deref()?.postStateToWebview() - // If this last request was to the advisor model, then reset advisor problem to give control back to base model - if (isCallingAdvisor) { - this.advisorProblem = undefined - } - // now add to apiconversationhistory // need to save assistant responses to file before proceeding to tool use since user can exit at any moment and we wouldn't be able to save the assistant's response let didEndLoop = false @@ -3273,20 +3081,12 @@ export class Cline { const didToolUse = this.assistantMessageContent.some((block) => block.type === "tool_use") if (!didToolUse) { - if (isCallingAdvisor) { - // if the last request was a request to advisor then it wouldn't have used a tool - this.userMessageContent.push({ - type: "text", - text: "Please continue with the task, taking into account the advisor's response provided above.", - }) - } else { - // normal request where tool use is required - this.userMessageContent.push({ - type: "text", - text: formatResponse.noToolsUsed(), - }) - this.consecutiveMistakeCount++ - } + // normal request where tool use is required + this.userMessageContent.push({ + type: "text", + text: formatResponse.noToolsUsed(), + }) + this.consecutiveMistakeCount++ } const recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent) diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index 3ba167c527..e3ba253e0e 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -19,7 +19,6 @@ export const toolUseNames = [ "browser_action", "use_mcp_tool", "access_mcp_resource", - "consult_advisor", "ask_followup_question", "plan_mode_response", "attempt_completion", @@ -45,7 +44,6 @@ export const toolParamNames = [ "tool_name", "arguments", "uri", - "problem", "question", "response", "result", diff --git a/src/core/prompts/advisor.ts b/src/core/prompts/advisor.ts deleted file mode 100644 index e0e3403e85..0000000000 --- a/src/core/prompts/advisor.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const ADVISOR_SYSTEM_PROMPT = - () => `You are a senior AI advisor with deep expertise in software development, system architecture, and technical problem-solving. Your role is to assist another AI agent by providing strategic guidance and solutions to coding challenges. - -==== - -INPUT FORMAT - -You will receive: -1. The autonomous agent's conversation history thus far -2. A specific problem or question the agent needs help with - -==== - -HOW TO RESPOND - -After being given the necessary context, you may start by assessing the problem and key challenges, focusing on the most critical aspects that need to be addressed. - -You may then recommend a strategy or solution, broken down into clear, actionable steps. Include rationale for key decisions and potential trade-offs considered. Use specific technical guidance, including code snippets, architecture recommendations, or debugging strategies as needed. Focus on practical, implementable advice the agent can use to apply the solution. - -==== - -Remember: Your goal is to provide clear, actionable guidance that helps the agent make progress. Focus on practical solutions rather than theoretical discussions.` diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 5ccf469eaf..302de8f96e 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -9,7 +9,6 @@ export const SYSTEM_PROMPT = async ( supportsComputerUse: boolean, mcpHub: McpHub, browserSettings: BrowserSettings, - supportsConsultAdvisor: boolean, ) => `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices. ==== @@ -205,20 +204,7 @@ Usage: server name here resource URI here -${ - supportsConsultAdvisor - ? ` - -## consult_advisor -Description: Request to consult an advanced-reasoning AI model about a problem or question you are facing. This can be used to resolve errors you are stuck on, or get input from the model to work through a challenge you are facing. The relevant conversation history leading to the problem will also be provided to the advisor for additional context. -Parameters: -- problem: (required) A string describing the issue, question, or context you want the advisor to address. -Usage: - -Your problem or question here -` - : "" -} + ## ask_followup_question Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth. @@ -839,69 +825,7 @@ You have access to two tools for working with files: **write_to_file** and **rep 3. For major overhauls or initial file creation, rely on write_to_file. 4. Once the file has been edited with either write_to_file or replace_in_file, the system will provide you with the final state of the modified file. Use this updated content as the reference point for any subsequent SEARCH/REPLACE operations, since it reflects any auto-formatting or user-applied changes. -By thoughtfully selecting between write_to_file and replace_in_file, you can make your file editing process smoother, safer, and more efficient.${ - supportsConsultAdvisor - ? ` - -==== - -CONSULTING THE ADVISOR MODEL - -You can use the consult_advisor tool to get suggestions from an advisor model, a powerful AI model that can provide strategic guidance and help solve complex problems. The conversation history that led to the current situation is automatically passed to the advisor, allowing it to provide contextually relevant guidance based on the full picture of the task at hand. - -# When to Use the Advisor - -- When stuck on persistent bugs that you cannot resolve -- If you've tried multiple approaches without success -- When facing complex type errors or package incompatibilities -- When debugging intricate interactions between multiple systems -- If you need deeper insight into system behavior that may not be apparent - -# How to Use Effectively - -## Provide Clear Context -- Explain the current situation and challenge -- Include relevant code snippets or error messages -- Describe what you've already tried -- Specify what kind of guidance you're seeking - -## Ask Specific Questions -- Instead of "Why isn't this working?" -- Better: "I'm encountering this specific type error when integrating these packages, here's what I've tried..." - -Example Usage: - - -I'm encountering persistent type errors while working with @types/react-query v4.0.0: - -Error: Type 'QueryClient' is not assignable to parameter of type 'never'. - The types of 'getQueryCache().notify' are incompatible between these types. - -I've tried: -- Checking package versions compatibility -- Explicitly typing the QueryClient instance -- Updating @types/react and @types/react-query - -Current package versions: -react-query: ^3.39.3 -@types/react-query: ^4.0.0 -react: ^18.2.0 -typescript: ^4.9.5 - -The error persists despite these attempts. Could this be due to version mismatches or breaking changes I'm not aware of? - - - -# Benefits of Using the Advisor - -- Break through debugging roadblocks -- Get fresh perspectives on complex issues -- Understand root causes of persistent bugs -- Solve challenging technical issues - -Remember: While you should attempt to solve problems with your own reasoning first, the advisor is a powerful resource available when you're stuck on a bug. Don't hesitate to consult it when you've hit a persistent roadblock that you cannot resolve.` - : "" -} +By thoughtfully selecting between write_to_file and replace_in_file, you can make your file editing process smoother, safer, and more efficient. ==== @@ -929,9 +853,7 @@ CAPABILITIES - You have access to tools that let you execute CLI commands on the user's computer, list files, view source code definitions, regex search${ supportsComputerUse ? ", use the browser" : "" -}, read and edit files${ - supportsConsultAdvisor ? ", consult an advisor" : "" -}, and ask follow-up questions. These tools help you effectively accomplish a wide range of tasks, such as writing code, making edits or improvements to existing files, understanding the current state of a project, performing system operations, and much more. +}, read and edit files, and ask follow-up questions. These tools help you effectively accomplish a wide range of tasks, such as writing code, making edits or improvements to existing files, understanding the current state of a project, performing system operations, and much more. - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd.toPosix()}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. @@ -941,11 +863,7 @@ CAPABILITIES ? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser." : "" } -- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively.${ - supportsConsultAdvisor - ? "\n- When you hit a roadblock, such as an error you've attempted to resolve several times without success, you can use the consult_advisor tool to get suggestions from an advanced-reasoning AI model. The conversation history that led to the current situation is automatically passed to the advisor, allowing it to provide contextually relevant guidance based on the full picture of the task at hand." - : "" -} +- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. ==== diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index e5aeed0ea0..f3d735cc19 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -46,7 +46,6 @@ type SecretKey = type GlobalStateKey = | "apiProvider" | "apiModelId" - | "anthropicAdvisorModelId" | "awsRegion" | "awsUseCrossRegionInference" | "vertexProjectId" @@ -63,9 +62,7 @@ type GlobalStateKey = | "anthropicBaseUrl" | "azureApiVersion" | "openRouterModelId" - | "openRouterAdvisorModelId" | "openRouterModelInfo" - | "openRouterAdvisorModelInfo" | "autoApprovalSettings" | "browserSettings" | "chatSettings" @@ -372,13 +369,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) await this.postStateToWebview() } - if (apiConfiguration.openRouterAdvisorModelId) { - await this.updateGlobalState( - "openRouterAdvisorModelInfo", - openRouterModels[apiConfiguration.openRouterAdvisorModelId], - ) - await this.postStateToWebview() - } } }) break @@ -398,7 +388,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { const { apiProvider, apiModelId, - anthropicAdvisorModelId, apiKey, openRouterApiKey, awsAccessKey, @@ -423,13 +412,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { azureApiVersion, openRouterModelId, openRouterModelInfo, - openRouterAdvisorModelId, - openRouterAdvisorModelInfo, vsCodeLmModelSelector, } = message.apiConfiguration await this.updateGlobalState("apiProvider", apiProvider) await this.updateGlobalState("apiModelId", apiModelId) - await this.updateGlobalState("anthropicAdvisorModelId", anthropicAdvisorModelId) await this.storeSecret("apiKey", apiKey) await this.storeSecret("openRouterApiKey", openRouterApiKey) await this.storeSecret("awsAccessKey", awsAccessKey) @@ -454,8 +440,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("azureApiVersion", azureApiVersion) await this.updateGlobalState("openRouterModelId", openRouterModelId) await this.updateGlobalState("openRouterModelInfo", openRouterModelInfo) - await this.updateGlobalState("openRouterAdvisorModelId", openRouterAdvisorModelId) - await this.updateGlobalState("openRouterAdvisorModelInfo", openRouterAdvisorModelInfo) await this.updateGlobalState("vsCodeLmModelSelector", vsCodeLmModelSelector) if (this.cline) { this.cline.api = buildApiHandler(message.apiConfiguration) @@ -607,11 +591,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { case "cancelTask": this.cancelTask() break - case "openAdvisorModelSettings": - this.postMessageToWebview({ - type: "openAdvisorModelSettings", - }) - break case "getLatestState": await this.postStateToWebview() break @@ -1106,7 +1085,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { const [ storedApiProvider, apiModelId, - anthropicAdvisorModelId, apiKey, openRouterApiKey, awsAccessKey, @@ -1131,8 +1109,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { azureApiVersion, openRouterModelId, openRouterModelInfo, - openRouterAdvisorModelId, - openRouterAdvisorModelInfo, lastShownAnnouncementId, customInstructions, taskHistory, @@ -1143,7 +1119,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, - this.getGlobalState("anthropicAdvisorModelId") as Promise, this.getSecret("apiKey") as Promise, this.getSecret("openRouterApiKey") as Promise, this.getSecret("awsAccessKey") as Promise, @@ -1168,8 +1143,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("azureApiVersion") as Promise, this.getGlobalState("openRouterModelId") as Promise, this.getGlobalState("openRouterModelInfo") as Promise, - this.getGlobalState("openRouterAdvisorModelId") as Promise, - this.getGlobalState("openRouterAdvisorModelInfo") as Promise, this.getGlobalState("lastShownAnnouncementId") as Promise, this.getGlobalState("customInstructions") as Promise, this.getGlobalState("taskHistory") as Promise, @@ -1197,7 +1170,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { apiConfiguration: { apiProvider, apiModelId, - anthropicAdvisorModelId, apiKey, openRouterApiKey, awsAccessKey, @@ -1222,8 +1194,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { azureApiVersion, openRouterModelId, openRouterModelInfo, - openRouterAdvisorModelId, - openRouterAdvisorModelInfo, vsCodeLmModelSelector, }, lastShownAnnouncementId, diff --git a/src/shared/AutoApprovalSettings.ts b/src/shared/AutoApprovalSettings.ts index 80f5f5a932..28376d4e06 100644 --- a/src/shared/AutoApprovalSettings.ts +++ b/src/shared/AutoApprovalSettings.ts @@ -8,7 +8,6 @@ export interface AutoApprovalSettings { executeCommands: boolean // Execute safe commands useBrowser: boolean // Use browser useMcp: boolean // Use MCP servers - consultAdvisor?: boolean // Consult the advisor model } // Global settings maxRequests: number // Maximum number of auto-approved requests @@ -23,7 +22,6 @@ export const DEFAULT_AUTO_APPROVAL_SETTINGS: AutoApprovalSettings = { executeCommands: false, useBrowser: false, useMcp: false, - consultAdvisor: false, }, maxRequests: 20, enableNotifications: false, diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 8b524240bf..ce6502774e 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -22,7 +22,6 @@ export interface ExtensionMessage { | "openRouterModels" | "mcpServers" | "relinquishControl" - | "openAdvisorModelSettings" | "vsCodeLmModels" | "requestVsCodeLmModels" text?: string @@ -81,7 +80,6 @@ export type ClineAsk = | "auto_approval_max_req_reached" | "browser_action_launch" | "use_mcp_server" - | "consult_advisor" export type ClineSay = | "task" @@ -103,10 +101,8 @@ export type ClineSay = | "mcp_server_request_started" | "mcp_server_response" | "use_mcp_server" - | "consult_advisor" | "diff_error" | "deleted_api_reqs" - | "advisor_response" export interface ClineSayTool { tool: @@ -149,12 +145,6 @@ export interface ClineAskUseMcpServer { uri?: string } -export interface ClineConsultAdvisor { - problem: string - advisorModelId?: string - estimatedCost?: number -} - export interface ClineApiReqInfo { request?: string tokensIn?: number diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index ce405303a0..6783bc0d79 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -33,7 +33,6 @@ export interface WebviewMessage { | "checkpointDiff" | "checkpointRestore" | "taskCompletionViewChanges" - | "openAdvisorModelSettings" | "requestVsCodeLmModels" | "toggleToolAutoApprove" | "toggleMcpServer" diff --git a/src/shared/api.ts b/src/shared/api.ts index 139c5e0544..f753525fc3 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -15,13 +15,10 @@ export type ApiProvider = export interface ApiHandlerOptions { apiModelId?: string apiKey?: string // anthropic - anthropicAdvisorModelId?: string anthropicBaseUrl?: string openRouterApiKey?: string openRouterModelId?: string - openRouterAdvisorModelId?: string openRouterModelInfo?: ModelInfo - openRouterAdvisorModelInfo?: ModelInfo awsAccessKey?: string awsSecretKey?: string awsSessionToken?: string @@ -63,13 +60,10 @@ export interface ModelInfo { description?: string } -export type ModelType = "base" | "advisor" - // Anthropic // https://docs.anthropic.com/en/docs/about-claude/models // prices updated 2025-01-02 export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-5-sonnet-20241022" -export const anthropicDefaultAdvisorModelId: AnthropicModelId = "claude-3-opus-20240229" export const anthropicModels = { "claude-3-5-sonnet-20241022": { maxTokens: 8192, @@ -186,18 +180,6 @@ export const openRouterDefaultModelInfo: ModelInfo = { description: "The new Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\n\n- Coding: New Sonnet scores ~49% on SWE-Bench Verified, higher than the last best score, and without any fancy prompt scaffolding\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\n\n#multimodal\n\n_This is a faster endpoint, made available in collaboration with Anthropic, that is self-moderated: response moderation happens on the provider's side instead of OpenRouter's. For requests that pass moderation, it's identical to the [Standard](/anthropic/claude-3.5-sonnet) variant._", } -export const openRouterDefaultAdvisorModelId = "openai/o1-preview" // will always exist in openRouterModels -export const openRouterDefaultAdvisorModelInfo: ModelInfo = { - maxTokens: 33_000, - contextWindow: 128_000, - supportsImages: true, - supportsComputerUse: false, - supportsPromptCache: false, - inputPrice: 15, - outputPrice: 60, - description: - "The latest and strongest model family from OpenAI, o1 is designed to spend more time thinking before responding.\n\nThe o1 models are optimized for math, science, programming, and other STEM-related tasks. They consistently exhibit PhD-level accuracy on benchmarks in physics, chemistry, and biology. Learn more in the [launch announcement](https://openai.com/o1).\n\nNote: This model is currently experimental and not suitable for production use-cases, and may be heavily rate-limited.", -} // Vertex AI // https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index 6381abf5ee..20ab2ee952 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -31,18 +31,8 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
  • - - New Consult Advisor tool - {" "} - lets Cline ask a powerful model like o1 for help when stuck. Cline provides the full context of the problem, - and the Advisor model responds with a solution. (Available with OpenRouter and Anthropic.){" "} - - See a demo here! - -
  • -
  • - Plan/Act mode toggle: Plan mode lets Cline ask clarifying questions, brainstorm ideas, and architect a - solution. Switch back to Act mode to let him execute the plan! + Plan/Act mode toggle: Plan mode lets Cline focus on gathering information, asking clarifying questions, + brainstorm ideas, and architect a solution. Switch back to Act mode to let him execute the plan!
  • Quick API/model switching with a new popup menu under the chat field diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index 4ede2742c1..0c2d9afc72 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -46,25 +46,16 @@ const ACTION_METADATA: { shortName: "MCP", description: "Allows use of configured MCP servers which may modify filesystem or interact with APIs.", }, - { - id: "consultAdvisor", - label: "Consult the Advisor model", - shortName: "Advisor", - description: "Allows Cline to consult the Advisor model to get advice on how to proceed.", - }, ] const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { - const { autoApprovalSettings, apiConfiguration } = useExtensionState() + const { autoApprovalSettings } = useExtensionState() const [isExpanded, setIsExpanded] = useState(false) const [isHoveringCollapsibleSection, setIsHoveringCollapsibleSection] = useState(false) // Careful not to use partials to mutate since spread operator only does shallow copy - const supportsAdvisor = apiConfiguration?.apiProvider === "openrouter" || apiConfiguration?.apiProvider === "anthropic" - const actionMetadata = ACTION_METADATA.filter((action) => supportsAdvisor || action.id !== "consultAdvisor") - - const enabledActions = actionMetadata.filter((action) => autoApprovalSettings.actions[action.id]) + const enabledActions = ACTION_METADATA.filter((action) => autoApprovalSettings.actions[action.id]) const enabledActionsList = enabledActions.map((action) => action.shortName).join(", ") const hasEnabledActions = enabledActions.length > 0 @@ -228,7 +219,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { Auto-approve allows Cline to perform the following actions without asking for permission. Please use with caution and only enable if you understand the risks. - {actionMetadata.map((action) => ( + {ACTION_METADATA.map((action) => (
    { - const { mcpServers, apiConfiguration } = useExtensionState() + const { mcpServers } = useExtensionState() const [seeNewChangesDisabled, setSeeNewChangesDisabled] = useState(false) @@ -145,10 +141,6 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi useEvent("message", handleMessage) - const { selectedAdvisorModelId } = useMemo(() => { - return normalizeApiConfiguration(apiConfiguration) - }, [apiConfiguration]) - const [icon, title] = useMemo(() => { switch (type) { case "error": @@ -224,23 +216,6 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi )} , ] - case "consult_advisor": - // const consultAdvisor = JSON.parse(message.text || "{}") as ClineConsultAdvisor - const consultAdvisor = JSON.parse(message.text || "{}") as ClineConsultAdvisor - return [ - , - - <> - Cline wants to consult{" "} - {{isLast ? selectedAdvisorModelId : consultAdvisor.advisorModelId} || "Advisor model"}: - - , - ] case "completion_result": return [ server.name === useMcpServer.serverName) - return ( - <> -
    - {icon} - {title} -
    - -
    -
    - -
    - {consultAdvisor.estimatedCost != null && ( -
    - Estimated cost: ${Number(consultAdvisor.estimatedCost).toFixed(4)} -
    - )} -
    - -
    - You can change the Advisor model Cline consults with{" "} - vscode.postMessage({ type: "openAdvisorModelSettings" })}> - in API Settings. - -
    - - ) - } - switch (message.type) { case "say": switch (message.say) { @@ -926,32 +842,6 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi
    ) - case "advisor_response": - return ( -
    -
    - Advisor Response -
    - -
    - ) case "user_feedback": return (
    ( const [intendedCursorPosition, setIntendedCursorPosition] = useState(null) const contextMenuContainerRef = useRef(null) const [showModelSelector, setShowModelSelector] = useState(false) - const [showModelSelectorWithAdvisor, setShowModelSelectorWithAdvisor] = useState(false) const modelSelectorRef = useRef(null) const { width: viewportWidth, height: viewportHeight } = useWindowSize() const buttonRef = useRef(null) @@ -657,9 +652,8 @@ const ChatTextArea = forwardRef( const submitApiConfig = useCallback(() => { const apiValidationResult = validateApiConfiguration(apiConfiguration) const modelIdValidationResult = validateModelId(apiConfiguration, openRouterModels) - const advisorModelIdValidationResult = validateAdvisorModelId(apiConfiguration, openRouterModels) - if (!apiValidationResult && !modelIdValidationResult && !advisorModelIdValidationResult) { + if (!apiValidationResult && !modelIdValidationResult) { vscode.postMessage({ type: "apiConfiguration", apiConfiguration }) } else { vscode.postMessage({ type: "getLatestState" }) @@ -734,14 +728,12 @@ const ChatTextArea = forwardRef( } }, [showModelSelector, viewportWidth, viewportHeight]) - // Reset advisor settings when model selector is closed useEffect(() => { if (!showModelSelector) { // Attempt to save if possible // NOTE: we cannot call this here since it will create an infinite loop between this effect and the callback since getLatestState will update state. Instead we should submitapiconfig when the menu is explicitly closed, rather than as an effect of showModelSelector changing. // handleApiConfigSubmit() - setShowModelSelectorWithAdvisor(false) // Reset any active styling by blurring the button const button = buttonRef.current?.querySelector("a") if (button) { @@ -750,18 +742,6 @@ const ChatTextArea = forwardRef( } }, [showModelSelector]) - const handleMessage = useCallback((e: MessageEvent) => { - const message: ExtensionMessage = e.data - switch (message.type) { - case "openAdvisorModelSettings": - setShowModelSelector(true) - setShowModelSelectorWithAdvisor(true) - break - } - }, []) - - useEvent("message", handleMessage) - return (
    ( }}> diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 2aa30d995f..aec4e544a9 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -155,13 +155,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie setPrimaryButtonText("Approve") setSecondaryButtonText("Reject") break - case "consult_advisor": - setTextAreaDisabled(isPartial) - setClineAsk("consult_advisor") - setEnableButtons(!isPartial) - setPrimaryButtonText("Approve") - setSecondaryButtonText("Reject") - break case "completion_result": // extension waiting for feedback. but we can just present a new task button setTextAreaDisabled(isPartial) @@ -205,13 +198,11 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie case "error": case "api_req_finished": case "text": - case "advisor_response": case "browser_action": case "browser_action_result": case "browser_action_launch": case "command": case "use_mcp_server": - case "consult_advisor": case "command_output": case "mcp_server_request_started": case "mcp_server_response": @@ -284,7 +275,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie case "command": // user can provide feedback to a tool or command use case "command_output": // user can send input to command stdin case "use_mcp_server": - case "consult_advisor": case "completion_result": // if this happens then the user has feedback for the completion result case "resume_task": case "resume_completed_task": @@ -327,7 +317,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie case "tool": case "browser_action_launch": case "use_mcp_server": - case "consult_advisor": case "resume_task": case "mistake_limit_reached": case "auto_approval_max_req_reached": @@ -367,7 +356,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie case "tool": case "browser_action_launch": case "use_mcp_server": - case "consult_advisor": // responds to the API with a "This operation failed" and lets it try again vscode.postMessage({ type: "askResponse", diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 54b6b04621..07f0076dc3 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -13,8 +13,6 @@ import { ApiConfiguration, ApiProvider, ModelInfo, - ModelType, - anthropicDefaultAdvisorModelId, anthropicDefaultModelId, anthropicModels, azureOpenAiDefaultApiVersion, @@ -29,8 +27,6 @@ import { openAiModelInfoSaneDefaults, openAiNativeDefaultModelId, openAiNativeModels, - openRouterDefaultAdvisorModelId, - openRouterDefaultAdvisorModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo, vertexDefaultModelId, @@ -48,47 +44,9 @@ interface ApiOptionsProps { showModelOptions: boolean apiErrorMessage?: string modelIdErrorMessage?: string - advisorModelIdErrorMessage?: string - showAdvisorModelSettings?: boolean isPopup?: boolean } -const TabPanel = ({ children, isSelected }: { children: React.ReactNode; isSelected: boolean }) => { - if (!isSelected) return null - return
    {children}
    -} - -const StyledTabButton = styled.button<{ isSelected: boolean }>` - background: transparent; - border: none; - padding: 8px 16px; - color: ${(props) => (props.isSelected ? "var(--vscode-tab-activeForeground)" : "var(--vscode-tab-inactiveForeground)")}; - cursor: pointer; - border-bottom: 2px solid ${(props) => (props.isSelected ? "var(--vscode-foreground)" : "transparent")}; - font-size: 12px; - font-weight: 500; - - &:hover { - color: var(--vscode-tab-activeForeground); - } -` - -const TabButton = ({ - isSelected, - onClick, - children, -}: { - isSelected: boolean - onClick: () => void - children: React.ReactNode -}) => { - return ( - - {children} - - ) -} - // This is necessary to ensure dropdown opens downward, important for when this is used in popup const DROPDOWN_Z_INDEX = 1001 // Higher than the OpenRouterModelPicker's and ModelSelectorTooltip's z-index @@ -113,14 +71,7 @@ declare module "vscode" { } } -const ApiOptions = ({ - showModelOptions, - apiErrorMessage, - modelIdErrorMessage, - advisorModelIdErrorMessage, - showAdvisorModelSettings, - isPopup, -}: ApiOptionsProps) => { +const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, isPopup }: ApiOptionsProps) => { const { apiConfiguration, setApiConfiguration, uriScheme } = useExtensionState() const [ollamaModels, setOllamaModels] = useState([]) const [lmStudioModels, setLmStudioModels] = useState([]) @@ -128,7 +79,6 @@ const ApiOptions = ({ const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) - const [selectedTab, setSelectedTab] = useState(showAdvisorModelSettings ? "advisor" : "base") const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => { setApiConfiguration({ @@ -137,7 +87,7 @@ const ApiOptions = ({ }) } - const { selectedProvider, selectedModelId, selectedModelInfo, selectedAdvisorModelId } = useMemo(() => { + const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => { return normalizeApiConfiguration(apiConfiguration) }, [apiConfiguration]) @@ -187,16 +137,12 @@ const ApiOptions = ({ As a workaround, we create separate instances of the dropdown for each provider, and then conditionally render the one that matches the current provider. */ - const createDropdown = (models: Record, modelType?: ModelType) => { + const createDropdown = (models: Record) => { return ( Select a model... {Object.keys(models).map((modelId) => ( @@ -866,7 +812,6 @@ const ApiOptions = ({ )} {selectedProvider !== "openrouter" && - selectedProvider !== "anthropic" && selectedProvider !== "openai" && selectedProvider !== "ollama" && selectedProvider !== "lmstudio" && @@ -877,6 +822,7 @@ const ApiOptions = ({ + {selectedProvider === "anthropic" && createDropdown(anthropicModels)} {selectedProvider === "bedrock" && createDropdown(bedrockModels)} {selectedProvider === "vertex" && createDropdown(vertexModels)} {selectedProvider === "gemini" && createDropdown(geminiModels)} @@ -895,7 +841,9 @@ const ApiOptions = ({ )} - {selectedProvider !== "openrouter" && selectedProvider !== "anthropic" && modelIdErrorMessage && ( + {selectedProvider === "openrouter" && showModelOptions && } + + {modelIdErrorMessage && (

    )} - - {(selectedProvider === "openrouter" || selectedProvider === "anthropic") && showModelOptions && ( -

    -
    - setSelectedTab("base")}> - Cline Model - - setSelectedTab("advisor")}> - Advisor Model - -
    - - -

    - This model is the default driver for Cline. It will read and edit files, run commands, and more, with - your permission at each step. -

    - {selectedProvider === "anthropic" && ( -
    - {createDropdown(anthropicModels, "base")} -
    - )} - {selectedProvider === "openrouter" && ( - - )} - {modelIdErrorMessage && ( -

    - {modelIdErrorMessage} -

    - )} -
    - - -

    - The Cline model can consult this more powerful model for advice when running into roadblocks, such as - an error it cannot resolve. -

    - {selectedProvider === "anthropic" && ( -
    - {createDropdown(anthropicModels, "advisor")} -
    - )} - {selectedProvider === "openrouter" && ( - - )} - {advisorModelIdErrorMessage && ( -

    - {advisorModelIdErrorMessage} -

    - )} -
    -
    - )}
    ) } @@ -1127,8 +1002,6 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration): selectedProvider: ApiProvider selectedModelId: string selectedModelInfo: ModelInfo - selectedAdvisorModelId?: string - selectedAdvisorModelInfo?: ModelInfo } { const provider = apiConfiguration?.apiProvider || "anthropic" const modelId = apiConfiguration?.apiModelId @@ -1151,10 +1024,7 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration): } switch (provider) { case "anthropic": - return { - ...getProviderData(anthropicModels, anthropicDefaultModelId), - selectedAdvisorModelId: apiConfiguration?.anthropicAdvisorModelId || anthropicDefaultAdvisorModelId, - } + return getProviderData(anthropicModels, anthropicDefaultModelId) case "bedrock": return getProviderData(bedrockModels, bedrockDefaultModelId) case "vertex": @@ -1172,8 +1042,6 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration): selectedProvider: provider, selectedModelId: apiConfiguration?.openRouterModelId || openRouterDefaultModelId, selectedModelInfo: apiConfiguration?.openRouterModelInfo || openRouterDefaultModelInfo, - selectedAdvisorModelId: apiConfiguration?.openRouterAdvisorModelId || openRouterDefaultAdvisorModelId, - selectedAdvisorModelInfo: apiConfiguration?.openRouterAdvisorModelInfo || openRouterDefaultAdvisorModelInfo, } case "openai": return { diff --git a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx index 5407a715ac..37b0bbfad3 100644 --- a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx +++ b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx @@ -4,12 +4,7 @@ import React, { KeyboardEvent, memo, useEffect, useMemo, useRef, useState } from import { useRemark } from "react-remark" import { useMount } from "react-use" import styled from "styled-components" -import { - ModelType, - openRouterDefaultAdvisorModelId, - openRouterDefaultAdvisorModelInfo, - openRouterDefaultModelId, -} from "../../../../src/shared/api" +import { openRouterDefaultModelId } from "../../../../src/shared/api" import { useExtensionState } from "../../context/ExtensionStateContext" import { vscode } from "../../utils/vscode" import { highlight } from "../history/HistoryView" @@ -17,17 +12,12 @@ import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions" import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" export interface OpenRouterModelPickerProps { - modelType: ModelType isPopup?: boolean } -const OpenRouterModelPicker: React.FC = ({ modelType, isPopup }) => { +const OpenRouterModelPicker: React.FC = ({ isPopup }) => { const { apiConfiguration, setApiConfiguration, openRouterModels } = useExtensionState() - const [searchTerm, setSearchTerm] = useState( - modelType === "advisor" - ? apiConfiguration?.openRouterAdvisorModelId || openRouterDefaultAdvisorModelId - : apiConfiguration?.openRouterModelId || openRouterDefaultModelId, - ) + const [searchTerm, setSearchTerm] = useState(apiConfiguration?.openRouterModelId || openRouterDefaultModelId) const [isDropdownVisible, setIsDropdownVisible] = useState(false) const [selectedIndex, setSelectedIndex] = useState(-1) const dropdownRef = useRef(null) @@ -39,20 +29,15 @@ const OpenRouterModelPicker: React.FC = ({ modelType // could be setting invalid model id/undefined info but validation will catch it setApiConfiguration({ ...apiConfiguration, - ...(modelType === "advisor" - ? { - openRouterAdvisorModelId: newModelId, - openRouterAdvisorModelInfo: openRouterModels[newModelId], - } - : { - openRouterModelId: newModelId, - openRouterModelInfo: openRouterModels[newModelId], - }), + ...{ + openRouterModelId: newModelId, + openRouterModelInfo: openRouterModels[newModelId], + }, }) setSearchTerm(newModelId) } - const { selectedModelId, selectedModelInfo, selectedAdvisorModelId, selectedAdvisorModelInfo } = useMemo(() => { + const { selectedModelId, selectedModelInfo } = useMemo(() => { return normalizeApiConfiguration(apiConfiguration) }, [apiConfiguration]) @@ -161,9 +146,9 @@ const OpenRouterModelPicker: React.FC = ({ modelType `}
    - {/* = ({ modelType {hasInfo ? ( = ({ modelType marginTop: 0, color: "var(--vscode-descriptionForeground)", }}> - {modelType === "base" ? ( - <> - The extension automatically fetches the latest list of models available on{" "} - - OpenRouter. - - If you're unsure which model to choose, Cline works best with{" "} - handleModelChange("anthropic/claude-3.5-sonnet:beta")}> - anthropic/claude-3.5-sonnet:beta. - - You can also try searching "free" for no-cost options currently available. - - ) : ( - <> - It's recommended using a higher-reasoning model such as{" "} - handleModelChange("openai/o1-preview")}> - openai/o1-preview - - for the best results. - - )} + <> + The extension automatically fetches the latest list of models available on{" "} + + OpenRouter. + + If you're unsure which model to choose, Cline works best with{" "} + handleModelChange("anthropic/claude-3.5-sonnet:beta")}> + anthropic/claude-3.5-sonnet:beta. + + You can also try searching "free" for no-cost options currently available. +

    )}
    diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 91e9d136c8..8f13de7914 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -1,7 +1,7 @@ import { VSCodeButton, VSCodeLink, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { memo, useEffect, useState } from "react" import { useExtensionState } from "../../context/ExtensionStateContext" -import { validateAdvisorModelId, validateApiConfiguration, validateModelId } from "../../utils/validate" +import { validateApiConfiguration, validateModelId } from "../../utils/validate" import { vscode } from "../../utils/vscode" import ApiOptions from "./ApiOptions" @@ -15,18 +15,15 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { const { apiConfiguration, version, customInstructions, setCustomInstructions, openRouterModels } = useExtensionState() const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const [modelIdErrorMessage, setModelIdErrorMessage] = useState(undefined) - const [advisorModelIdErrorMessage, setAdvisorModelIdErrorMessage] = useState(undefined) const handleSubmit = () => { const apiValidationResult = validateApiConfiguration(apiConfiguration) const modelIdValidationResult = validateModelId(apiConfiguration, openRouterModels) - const advisorModelIdValidationResult = validateAdvisorModelId(apiConfiguration, openRouterModels) setApiErrorMessage(apiValidationResult) setModelIdErrorMessage(modelIdValidationResult) - setAdvisorModelIdErrorMessage(advisorModelIdValidationResult) - if (!apiValidationResult && !modelIdValidationResult && !advisorModelIdValidationResult) { + if (!apiValidationResult && !modelIdValidationResult) { vscode.postMessage({ type: "apiConfiguration", apiConfiguration }) vscode.postMessage({ type: "customInstructions", @@ -39,7 +36,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { useEffect(() => { setApiErrorMessage(undefined) setModelIdErrorMessage(undefined) - setAdvisorModelIdErrorMessage(undefined) }, [apiConfiguration]) // validate as soon as the component is mounted @@ -95,7 +91,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { showModelOptions={true} apiErrorMessage={apiErrorMessage} modelIdErrorMessage={modelIdErrorMessage} - advisorModelIdErrorMessage={advisorModelIdErrorMessage} />
    diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 69e67f1a3d..75db746f02 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -2,14 +2,7 @@ import React, { createContext, useCallback, useContext, useEffect, useState } fr import { useEvent } from "react-use" import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "../../../src/shared/AutoApprovalSettings" import { ExtensionMessage, ExtensionState } from "../../../src/shared/ExtensionMessage" -import { - ApiConfiguration, - ModelInfo, - openRouterDefaultAdvisorModelId, - openRouterDefaultAdvisorModelInfo, - openRouterDefaultModelId, - openRouterDefaultModelInfo, -} from "../../../src/shared/api" +import { ApiConfiguration, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "../../../src/shared/api" import { findLastIndex } from "../../../src/shared/array" import { McpServer } from "../../../src/shared/mcp" import { convertTextMateToHljs } from "../utils/textMateToHljs" @@ -49,7 +42,6 @@ export const ExtensionStateContextProvider: React.FC<{ const [filePaths, setFilePaths] = useState([]) const [openRouterModels, setOpenRouterModels] = useState>({ [openRouterDefaultModelId]: openRouterDefaultModelInfo, - [openRouterDefaultAdvisorModelId]: openRouterDefaultAdvisorModelInfo, }) const [mcpServers, setMcpServers] = useState([]) @@ -107,7 +99,6 @@ export const ExtensionStateContextProvider: React.FC<{ const updatedModels = message.openRouterModels ?? {} setOpenRouterModels({ [openRouterDefaultModelId]: openRouterDefaultModelInfo, // in case the extension sent a model list without the default model - [openRouterDefaultAdvisorModelId]: openRouterDefaultAdvisorModelInfo, ...updatedModels, }) break diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index e0b06429e1..beafc65572 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -1,4 +1,4 @@ -import { ApiConfiguration, openRouterDefaultAdvisorModelId, openRouterDefaultModelId } from "../../../src/shared/api" +import { ApiConfiguration, openRouterDefaultModelId } from "../../../src/shared/api" import { ModelInfo } from "../../../src/shared/api" export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): string | undefined { if (apiConfiguration) { @@ -88,23 +88,3 @@ export function validateModelId( } return undefined } - -export function validateAdvisorModelId( - apiConfiguration?: ApiConfiguration, - openRouterModels?: Record, -): string | undefined { - if (apiConfiguration) { - switch (apiConfiguration.apiProvider) { - case "openrouter": - const advisorModelId = apiConfiguration.openRouterAdvisorModelId || openRouterDefaultAdvisorModelId // in case the user hasn't changed the model id, it will be undefined by default - if (!advisorModelId) { - return "You must provide a model ID." - } - if (openRouterModels && !Object.keys(openRouterModels).includes(advisorModelId)) { - return "The model ID you provided is not available. Please choose a different model." - } - break - } - } - return undefined -}