mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Remove consult advisor tool
This commit is contained in:
parent
f1a9f3fed7
commit
6ceda01b24
23 changed files with 99 additions and 889 deletions
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<Anthropic.Beta.PromptCaching.Messages.RawPromptCachingBetaMessageStreamEvent>
|
||||
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],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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.`
|
||||
|
|
@ -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:
|
|||
<access_mcp_resource>
|
||||
<server_name>server name here</server_name>
|
||||
<uri>resource URI here</uri>
|
||||
</access_mcp_resource>${
|
||||
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:
|
||||
<consult_advisor>
|
||||
<problem>Your problem or question here</problem>
|
||||
</consult_advisor>`
|
||||
: ""
|
||||
}
|
||||
</access_mcp_resource>
|
||||
|
||||
## 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:
|
||||
<consult_advisor>
|
||||
<problem>
|
||||
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?
|
||||
</problem>
|
||||
</consult_advisor>
|
||||
|
||||
# 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.
|
||||
|
||||
====
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ApiProvider | undefined>,
|
||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||
this.getGlobalState("anthropicAdvisorModelId") as Promise<string | undefined>,
|
||||
this.getSecret("apiKey") as Promise<string | undefined>,
|
||||
this.getSecret("openRouterApiKey") as Promise<string | undefined>,
|
||||
this.getSecret("awsAccessKey") as Promise<string | undefined>,
|
||||
|
|
@ -1168,8 +1143,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
this.getGlobalState("azureApiVersion") as Promise<string | undefined>,
|
||||
this.getGlobalState("openRouterModelId") as Promise<string | undefined>,
|
||||
this.getGlobalState("openRouterModelInfo") as Promise<ModelInfo | undefined>,
|
||||
this.getGlobalState("openRouterAdvisorModelId") as Promise<string | undefined>,
|
||||
this.getGlobalState("openRouterAdvisorModelInfo") as Promise<ModelInfo | undefined>,
|
||||
this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>,
|
||||
this.getGlobalState("customInstructions") as Promise<string | undefined>,
|
||||
this.getGlobalState("taskHistory") as Promise<HistoryItem[] | undefined>,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ export interface WebviewMessage {
|
|||
| "checkpointDiff"
|
||||
| "checkpointRestore"
|
||||
| "taskCompletionViewChanges"
|
||||
| "openAdvisorModelSettings"
|
||||
| "requestVsCodeLmModels"
|
||||
| "toggleToolAutoApprove"
|
||||
| "toggleMcpServer"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,18 +31,8 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
|
|||
</h3>
|
||||
<ul style={{ margin: "0 0 8px", paddingLeft: "12px" }}>
|
||||
<li>
|
||||
<b>
|
||||
New <code>Consult Advisor</code> tool
|
||||
</b>{" "}
|
||||
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. <i>(Available with OpenRouter and Anthropic.)</i>{" "}
|
||||
<VSCodeLink href="https://x.com/sdrzn/status/1876378124126236949" style={{ display: "inline" }}>
|
||||
See a demo here!
|
||||
</VSCodeLink>
|
||||
</li>
|
||||
<li>
|
||||
<b>Plan/Act mode toggle:</b> Plan mode lets Cline ask clarifying questions, brainstorm ideas, and architect a
|
||||
solution. Switch back to Act mode to let him execute the plan!
|
||||
<b>Plan/Act mode toggle:</b> 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!
|
||||
</li>
|
||||
<li>
|
||||
<b>Quick API/model switching</b> with a new popup menu under the chat field
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
</div>
|
||||
{actionMetadata.map((action) => (
|
||||
{ACTION_METADATA.map((action) => (
|
||||
<div key={action.id} style={{ margin: "6px 0" }}>
|
||||
<VSCodeCheckbox
|
||||
checked={autoApprovalSettings.actions[action.id]}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { VSCodeBadge, VSCodeLink, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
|
||||
import { VSCodeBadge, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
|
||||
import deepEqual from "fast-deep-equal"
|
||||
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useEvent, useSize } from "react-use"
|
||||
|
|
@ -8,9 +8,8 @@ import {
|
|||
ClineAskUseMcpServer,
|
||||
ClineMessage,
|
||||
ClineSayTool,
|
||||
ExtensionMessage,
|
||||
COMPLETION_RESULT_CHANGES_FLAG,
|
||||
ClineConsultAdvisor,
|
||||
ExtensionMessage,
|
||||
} from "../../../../src/shared/ExtensionMessage"
|
||||
import { COMMAND_OUTPUT_STRING, COMMAND_REQ_APP_STRING } from "../../../../src/shared/combineCommandSequences"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
|
|
@ -25,7 +24,6 @@ import Thumbnails from "../common/Thumbnails"
|
|||
import McpResourceRow from "../mcp/McpResourceRow"
|
||||
import McpToolRow from "../mcp/McpToolRow"
|
||||
import { highlightMentions } from "./TaskHeader"
|
||||
import { normalizeApiConfiguration } from "../settings/ApiOptions"
|
||||
|
||||
const ChatRowContainer = styled.div`
|
||||
padding: 10px 6px 10px 15px;
|
||||
|
|
@ -64,9 +62,7 @@ const ChatRow = memo(
|
|||
message.say === "completion_result" ||
|
||||
message.ask === "completion_result" ||
|
||||
message.say === "use_mcp_server" ||
|
||||
message.ask === "use_mcp_server" ||
|
||||
message.say === "consult_advisor" ||
|
||||
message.ask === "consult_advisor")
|
||||
message.ask === "use_mcp_server")
|
||||
|
||||
if (shouldShowCheckpoints && isLast) {
|
||||
shouldShowCheckpoints =
|
||||
|
|
@ -103,7 +99,7 @@ const ChatRow = memo(
|
|||
export default ChatRow
|
||||
|
||||
export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessage, isLast }: ChatRowContentProps) => {
|
||||
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
|
|||
)}
|
||||
</span>,
|
||||
]
|
||||
case "consult_advisor":
|
||||
// const consultAdvisor = JSON.parse(message.text || "{}") as ClineConsultAdvisor
|
||||
const consultAdvisor = JSON.parse(message.text || "{}") as ClineConsultAdvisor
|
||||
return [
|
||||
<span
|
||||
className="codicon codicon-comment-discussion"
|
||||
style={{
|
||||
color: normalColor,
|
||||
marginBottom: "-1.5px",
|
||||
}}></span>,
|
||||
<span style={{ color: normalColor, fontWeight: "bold" }}>
|
||||
<>
|
||||
Cline wants to consult{" "}
|
||||
{<code>{isLast ? selectedAdvisorModelId : consultAdvisor.advisorModelId}</code> || "Advisor model"}:
|
||||
</>
|
||||
</span>,
|
||||
]
|
||||
case "completion_result":
|
||||
return [
|
||||
<span
|
||||
|
|
@ -332,8 +307,6 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi
|
|||
isMcpServerResponding,
|
||||
message.text,
|
||||
message.type,
|
||||
selectedAdvisorModelId,
|
||||
isLast,
|
||||
])
|
||||
|
||||
const headerStyle: React.CSSProperties = {
|
||||
|
|
@ -751,63 +724,6 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi
|
|||
)
|
||||
}
|
||||
|
||||
if (message.ask === "consult_advisor" || message.say === "consult_advisor") {
|
||||
const consultAdvisor = JSON.parse(message.text || "{}") as ClineConsultAdvisor
|
||||
// const server = mcpServers.find((server) => server.name === useMcpServer.serverName)
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
background: "var(--vscode-textCodeBlock-background)",
|
||||
borderRadius: "3px",
|
||||
padding: "8px 10px",
|
||||
marginTop: "8px",
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
wordBreak: "break-word",
|
||||
overflowWrap: "anywhere",
|
||||
marginBottom: -10,
|
||||
marginTop: -10,
|
||||
}}>
|
||||
<MarkdownBlock markdown={consultAdvisor.problem} />
|
||||
</div>
|
||||
{consultAdvisor.estimatedCost != null && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 8,
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
borderTop: "1px solid var(--vscode-textSeparator-foreground)",
|
||||
paddingTop: 8,
|
||||
}}>
|
||||
Estimated cost: ${Number(consultAdvisor.estimatedCost).toFixed(4)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
padding: 8,
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
You can change the Advisor model Cline consults with{" "}
|
||||
<VSCodeLink
|
||||
style={{ display: "inline", fontSize: "inherit" }}
|
||||
onClick={() => vscode.postMessage({ type: "openAdvisorModelSettings" })}>
|
||||
in API Settings.
|
||||
</VSCodeLink>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
switch (message.type) {
|
||||
case "say":
|
||||
switch (message.say) {
|
||||
|
|
@ -926,32 +842,6 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi
|
|||
<Markdown markdown={message.text} />
|
||||
</div>
|
||||
)
|
||||
case "advisor_response":
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid color-mix(in srgb, var(--vscode-descriptionForeground) 25%, transparent)",
|
||||
borderRadius: "4px",
|
||||
padding: "16px",
|
||||
position: "relative",
|
||||
marginTop: "10px",
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "-8px",
|
||||
left: "16px",
|
||||
backgroundColor: "var(--vscode-sideBar-background)",
|
||||
padding: "0 8px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "12px",
|
||||
textTransform: "uppercase",
|
||||
}}>
|
||||
Advisor Response
|
||||
</div>
|
||||
<Markdown markdown={message.text} />
|
||||
</div>
|
||||
)
|
||||
case "user_feedback":
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,28 +1,8 @@
|
|||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"
|
||||
import DynamicTextArea from "react-textarea-autosize"
|
||||
import { mentionRegex, mentionRegexGlobal } from "../../../../src/shared/context-mentions"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import {
|
||||
ContextMenuOptionType,
|
||||
getContextMenuOptions,
|
||||
insertMention,
|
||||
removeMention,
|
||||
shouldShowContextMenu,
|
||||
} from "../../utils/context-mentions"
|
||||
import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
|
||||
import ContextMenu from "./ContextMenu"
|
||||
import Thumbnails from "../common/Thumbnails"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import { useClickAway, useWindowSize } from "react-use"
|
||||
import styled from "styled-components"
|
||||
import { useEvent, useWindowSize } from "react-use"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
import ApiOptions from "../settings/ApiOptions"
|
||||
import { useClickAway } from "react-use"
|
||||
import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
|
||||
import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage"
|
||||
import { validateAdvisorModelId } from "../../utils/validate"
|
||||
import { validateModelId } from "../../utils/validate"
|
||||
import { validateApiConfiguration } from "../../utils/validate"
|
||||
import {
|
||||
anthropicDefaultModelId,
|
||||
bedrockDefaultModelId,
|
||||
|
|
@ -33,6 +13,22 @@ import {
|
|||
openRouterDefaultModelId,
|
||||
vertexDefaultModelId,
|
||||
} from "../../../../src/shared/api"
|
||||
import { mentionRegex, mentionRegexGlobal } from "../../../../src/shared/context-mentions"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import {
|
||||
ContextMenuOptionType,
|
||||
getContextMenuOptions,
|
||||
insertMention,
|
||||
removeMention,
|
||||
shouldShowContextMenu,
|
||||
} from "../../utils/context-mentions"
|
||||
import { validateApiConfiguration, validateModelId } from "../../utils/validate"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
|
||||
import Thumbnails from "../common/Thumbnails"
|
||||
import ApiOptions from "../settings/ApiOptions"
|
||||
import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
|
||||
import ContextMenu from "./ContextMenu"
|
||||
|
||||
interface ChatTextAreaProps {
|
||||
inputValue: string
|
||||
|
|
@ -237,7 +233,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
const [intendedCursorPosition, setIntendedCursorPosition] = useState<number | null>(null)
|
||||
const contextMenuContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [showModelSelector, setShowModelSelector] = useState(false)
|
||||
const [showModelSelectorWithAdvisor, setShowModelSelectorWithAdvisor] = useState(false)
|
||||
const modelSelectorRef = useRef<HTMLDivElement>(null)
|
||||
const { width: viewportWidth, height: viewportHeight } = useWindowSize()
|
||||
const buttonRef = useRef<HTMLDivElement>(null)
|
||||
|
|
@ -657,9 +652,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
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<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
}
|
||||
}, [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<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
}
|
||||
}, [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 (
|
||||
<div>
|
||||
<div
|
||||
|
|
@ -996,10 +976,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
}}>
|
||||
<ApiOptions
|
||||
showModelOptions={true}
|
||||
showAdvisorModelSettings={showModelSelectorWithAdvisor}
|
||||
apiErrorMessage={undefined}
|
||||
modelIdErrorMessage={undefined}
|
||||
advisorModelIdErrorMessage={undefined}
|
||||
isPopup={true}
|
||||
/>
|
||||
</ModelSelectorTooltip>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 <div style={{ marginTop: 10 }}>{children}</div>
|
||||
}
|
||||
|
||||
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 (
|
||||
<StyledTabButton isSelected={isSelected} onClick={onClick}>
|
||||
{children}
|
||||
</StyledTabButton>
|
||||
)
|
||||
}
|
||||
|
||||
// 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<string[]>([])
|
||||
const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
|
||||
|
|
@ -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<string, ModelInfo>, modelType?: ModelType) => {
|
||||
const createDropdown = (models: Record<string, ModelInfo>) => {
|
||||
return (
|
||||
<VSCodeDropdown
|
||||
id="model-id"
|
||||
// right now anthropic is the only non-openrouter provider that supports advisor models.
|
||||
// if anthropic then selectedAdvisorId will always have value
|
||||
value={modelType === "advisor" ? selectedAdvisorModelId || selectedModelId : selectedModelId}
|
||||
onChange={
|
||||
modelType === "advisor" ? handleInputChange("anthropicAdvisorModelId") : handleInputChange("apiModelId")
|
||||
}
|
||||
value={selectedModelId}
|
||||
onChange={handleInputChange("apiModelId")}
|
||||
style={{ width: "100%" }}>
|
||||
<VSCodeOption value="">Select a model...</VSCodeOption>
|
||||
{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 = ({
|
|||
<label htmlFor="model-id">
|
||||
<span style={{ fontWeight: 500 }}>Model</span>
|
||||
</label>
|
||||
{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 && <OpenRouterModelPicker isPopup={isPopup} />}
|
||||
|
||||
{modelIdErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-10px 0 4px 0",
|
||||
|
|
@ -905,79 +853,6 @@ const ApiOptions = ({
|
|||
{modelIdErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{(selectedProvider === "openrouter" || selectedProvider === "anthropic") && showModelOptions && (
|
||||
<div style={{ marginTop: -5 }}>
|
||||
<div style={{ display: "flex", borderBottom: "1px solid var(--vscode-panel-border)" }}>
|
||||
<TabButton isSelected={selectedTab === "base"} onClick={() => setSelectedTab("base")}>
|
||||
Cline Model
|
||||
</TabButton>
|
||||
<TabButton isSelected={selectedTab === "advisor"} onClick={() => setSelectedTab("advisor")}>
|
||||
Advisor Model
|
||||
</TabButton>
|
||||
</div>
|
||||
|
||||
<TabPanel isSelected={selectedTab === "base"}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginBottom: "10px",
|
||||
color: "var(--vscode-foreground)",
|
||||
}}>
|
||||
This model is the default driver for Cline. It will read and edit files, run commands, and more, with
|
||||
your permission at each step.
|
||||
</p>
|
||||
{selectedProvider === "anthropic" && (
|
||||
<div className="dropdown-container" style={{ marginBottom: 15 }}>
|
||||
{createDropdown(anthropicModels, "base")}
|
||||
</div>
|
||||
)}
|
||||
{selectedProvider === "openrouter" && (
|
||||
<OpenRouterModelPicker modelType="base" key="base-model-picker" isPopup={isPopup} />
|
||||
)}
|
||||
{modelIdErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-10px 0 4px 0",
|
||||
fontSize: 12,
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{modelIdErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel isSelected={selectedTab === "advisor"}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginBottom: "10px",
|
||||
color: "var(--vscode-foreground)",
|
||||
}}>
|
||||
The Cline model can consult this more powerful model for advice when running into roadblocks, such as
|
||||
an error it cannot resolve.
|
||||
</p>
|
||||
{selectedProvider === "anthropic" && (
|
||||
<div className="dropdown-container" style={{ marginBottom: 15 }}>
|
||||
{createDropdown(anthropicModels, "advisor")}
|
||||
</div>
|
||||
)}
|
||||
{selectedProvider === "openrouter" && (
|
||||
<OpenRouterModelPicker modelType="advisor" key="advisor-model-picker" isPopup={isPopup} />
|
||||
)}
|
||||
{advisorModelIdErrorMessage && (
|
||||
<p
|
||||
style={{
|
||||
margin: "-10px 0 4px 0",
|
||||
fontSize: 12,
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
{advisorModelIdErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
</TabPanel>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<OpenRouterModelPickerProps> = ({ modelType, isPopup }) => {
|
||||
const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = ({ 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<HTMLDivElement>(null)
|
||||
|
|
@ -39,20 +29,15 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = ({ 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<OpenRouterModelPickerProps> = ({ modelType
|
|||
`}
|
||||
</style>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
{/* <label htmlFor="model-search">
|
||||
<label htmlFor="model-search">
|
||||
<span style={{ fontWeight: 500 }}>Model</span>
|
||||
</label> */}
|
||||
</label>
|
||||
<DropdownWrapper ref={dropdownRef}>
|
||||
<VSCodeTextField
|
||||
id="model-search"
|
||||
|
|
@ -222,14 +207,8 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = ({ modelType
|
|||
|
||||
{hasInfo ? (
|
||||
<ModelInfoView
|
||||
selectedModelId={
|
||||
modelType === "advisor" ? selectedAdvisorModelId || openRouterDefaultAdvisorModelId : selectedModelId
|
||||
}
|
||||
modelInfo={
|
||||
modelType === "advisor"
|
||||
? selectedAdvisorModelInfo || openRouterDefaultAdvisorModelInfo
|
||||
: selectedModelInfo
|
||||
}
|
||||
selectedModelId={selectedModelId}
|
||||
modelInfo={selectedModelInfo}
|
||||
isDescriptionExpanded={isDescriptionExpanded}
|
||||
setIsDescriptionExpanded={setIsDescriptionExpanded}
|
||||
isPopup={isPopup}
|
||||
|
|
@ -241,31 +220,19 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = ({ modelType
|
|||
marginTop: 0,
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
{modelType === "base" ? (
|
||||
<>
|
||||
The extension automatically fetches the latest list of models available on{" "}
|
||||
<VSCodeLink style={{ display: "inline", fontSize: "inherit" }} href="https://openrouter.ai/models">
|
||||
OpenRouter.
|
||||
</VSCodeLink>
|
||||
If you're unsure which model to choose, Cline works best with{" "}
|
||||
<VSCodeLink
|
||||
style={{ display: "inline", fontSize: "inherit" }}
|
||||
onClick={() => handleModelChange("anthropic/claude-3.5-sonnet:beta")}>
|
||||
anthropic/claude-3.5-sonnet:beta.
|
||||
</VSCodeLink>
|
||||
You can also try searching "free" for no-cost options currently available.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
It's recommended using a higher-reasoning model such as{" "}
|
||||
<VSCodeLink
|
||||
style={{ display: "inline", fontSize: "inherit" }}
|
||||
onClick={() => handleModelChange("openai/o1-preview")}>
|
||||
openai/o1-preview
|
||||
</VSCodeLink>
|
||||
for the best results.
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
The extension automatically fetches the latest list of models available on{" "}
|
||||
<VSCodeLink style={{ display: "inline", fontSize: "inherit" }} href="https://openrouter.ai/models">
|
||||
OpenRouter.
|
||||
</VSCodeLink>
|
||||
If you're unsure which model to choose, Cline works best with{" "}
|
||||
<VSCodeLink
|
||||
style={{ display: "inline", fontSize: "inherit" }}
|
||||
onClick={() => handleModelChange("anthropic/claude-3.5-sonnet:beta")}>
|
||||
anthropic/claude-3.5-sonnet:beta.
|
||||
</VSCodeLink>
|
||||
You can also try searching "free" for no-cost options currently available.
|
||||
</>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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<string | undefined>(undefined)
|
||||
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [advisorModelIdErrorMessage, setAdvisorModelIdErrorMessage] = useState<string | undefined>(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}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string[]>([])
|
||||
const [openRouterModels, setOpenRouterModels] = useState<Record<string, ModelInfo>>({
|
||||
[openRouterDefaultModelId]: openRouterDefaultModelInfo,
|
||||
[openRouterDefaultAdvisorModelId]: openRouterDefaultAdvisorModelInfo,
|
||||
})
|
||||
const [mcpServers, setMcpServers] = useState<McpServer[]>([])
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ModelInfo>,
|
||||
): 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue