From d9f2fcd70020ca599a335c28128d875b83563a7f Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Wed, 30 Apr 2025 21:42:27 -0700 Subject: [PATCH] error UI less overwhelming, gemini separated system message, improve Apply prompt --- .../react/src/sidebar-tsx/SidebarChat.tsx | 92 ++++++++++--------- .../contrib/void/common/helpers/colors.ts | 37 +++++--- .../contrib/void/common/modelCapabilities.ts | 18 ++-- .../contrib/void/common/prompt/prompts.ts | 34 +++---- 4 files changed, 90 insertions(+), 91 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 2cba8331..120ebadb 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -810,7 +810,7 @@ const ToolHeaderWrapper = ({ const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters>[0] & { content: string }) => { const accessor = useAccessor() - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const title = getTitle(toolMessage) @@ -851,19 +851,21 @@ const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters - if (toolMessage.type !== 'tool_error') { + if (toolMessage.type === 'success' || toolMessage.type === 'rejected') { const { result } = toolMessage componentParams.bottomChildren = {result?.lintErrors?.map((error, i) => ( -
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
+
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
))}
} - else { + else if (toolMessage.type === 'tool_error') { // error const { result } = toolMessage componentParams.bottomChildren = - {result} + + {result} + } } @@ -1579,7 +1581,7 @@ const BottomChildren = ({ children, title }: { children: React.ReactNode, title:
-
+
{children}
@@ -1641,7 +1643,7 @@ const CommandTool = ({ toolMessage, type, threadId }: { threadId: string } & ({ const terminalToolsService = accessor.get('ITerminalToolService') const toolsService = accessor.get('IToolsService') const terminalService = accessor.get('ITerminalService') - const isError = toolMessage.type === 'tool_error' + const isError = false const title = getTitle(toolMessage) const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) const icon = null @@ -1711,9 +1713,11 @@ const CommandTool = ({ toolMessage, type, threadId }: { threadId: string } & ({ } else if (toolMessage.type === 'tool_error') { const { result } = toolMessage - componentParams.children = - {result} - + componentParams.bottomChildren = + + {result} + + } else if (toolMessage.type === 'running_now') { componentParams.children =
@@ -1742,7 +1746,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'tool_request') return null // do not show past requests if (toolMessage.type === 'running_now') return null // do not show running - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const { rawParams, params } = toolMessage const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } @@ -1765,11 +1769,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, else if (toolMessage.type === 'tool_error') { const { result } = toolMessage componentParams.desc2 = - componentParams.children = + componentParams.bottomChildren = {result} - + } return @@ -1787,7 +1791,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'tool_request') return null // do not show past requests if (toolMessage.type === 'running_now') return null // do not show running - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const { rawParams, params } = toolMessage const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } @@ -1812,11 +1816,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } else { const { result } = toolMessage - componentParams.children = + componentParams.bottomChildren = {result} - + } return @@ -1835,7 +1839,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'tool_request') return null // do not show past requests if (toolMessage.type === 'running_now') return null // do not show running - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const { rawParams, params } = toolMessage const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } @@ -1867,11 +1871,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } else { const { result } = toolMessage - componentParams.children = + componentParams.bottomChildren = {result} - + } return @@ -1881,7 +1885,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const title = getTitle(toolMessage) const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) @@ -1916,11 +1920,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } else { const { result } = toolMessage - componentParams.children = + componentParams.bottomChildren = {result} - + } return @@ -1930,7 +1934,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const title = getTitle(toolMessage) const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) @@ -1971,11 +1975,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } else { const { result } = toolMessage - componentParams.children = + componentParams.bottomChildren = {result} - + } return } @@ -1986,7 +1990,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, const accessor = useAccessor(); const toolsService = accessor.get('IToolsService'); const title = getTitle(toolMessage); - const isError = toolMessage.type === 'tool_error'; + const isError = false const isRejected = toolMessage.type === 'rejected' const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); const icon = null; @@ -2015,13 +2019,13 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } - else { + else if (toolMessage.type === 'tool_error') { const { result } = toolMessage; - componentParams.children = + componentParams.bottomChildren = {result} - ; + } return ; @@ -2042,7 +2046,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'tool_request') return null // do not show past requests if (toolMessage.type === 'running_now') return null // do not show running - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const { rawParams, params } = toolMessage const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } @@ -2061,11 +2065,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, else if (toolMessage.type === 'tool_error') { const { result } = toolMessage if (params) componentParams.desc2 = - componentParams.children = + componentParams.bottomChildren = {result} - + } return @@ -2078,7 +2082,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const title = getTitle(toolMessage) const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) @@ -2100,11 +2104,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, else if (toolMessage.type === 'tool_error') { const { result } = toolMessage if (params) { componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } } - componentParams.children = componentParams.children = + componentParams.bottomChildren = {result} - + } else if (toolMessage.type === 'running_now') { // nothing more is needed @@ -2121,7 +2125,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, const accessor = useAccessor() const commandService = accessor.get('ICommandService') const isFolder = toolMessage.params?.isFolder ?? false - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const title = getTitle(toolMessage) const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) @@ -2142,11 +2146,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, else if (toolMessage.type === 'tool_error') { const { result } = toolMessage if (params) { componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } } - componentParams.children = componentParams.children = + componentParams.children = componentParams.bottomChildren = {result} - + } else if (toolMessage.type === 'running_now') { const { result } = toolMessage @@ -2196,7 +2200,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'tool_request') return null // do not show past requests if (toolMessage.type === 'running_now') return null // do not show running - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const { rawParams, params } = toolMessage const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } @@ -2210,11 +2214,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } else if (toolMessage.type === 'tool_error') { const { result } = toolMessage - componentParams.children = + componentParams.bottomChildren = {result} - + } return @@ -2233,7 +2237,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'tool_request') return null // do not show past requests if (toolMessage.type === 'running_now') return null // do not show running - const isError = toolMessage.type === 'tool_error' + const isError = false const isRejected = toolMessage.type === 'rejected' const { rawParams, params } = toolMessage const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } @@ -2245,11 +2249,11 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } else if (toolMessage.type === 'tool_error') { const { result } = toolMessage - componentParams.children = + componentParams.bottomChildren = {result} - + } return diff --git a/src/vs/workbench/contrib/void/common/helpers/colors.ts b/src/vs/workbench/contrib/void/common/helpers/colors.ts index ffc5324f..17978f76 100644 --- a/src/vs/workbench/contrib/void/common/helpers/colors.ts +++ b/src/vs/workbench/contrib/void/common/helpers/colors.ts @@ -1,33 +1,40 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + import { Color, RGBA } from '../../../../../base/common/color.js'; import { registerColor } from '../../../../../platform/theme/common/colorUtils.js'; +// editCodeService colors +const sweepBG = new Color(new RGBA(100, 100, 100, .2)); +const highlightBG = new Color(new RGBA(100, 100, 100, .1)); +const sweepIdxBG = new Color(new RGBA(100, 100, 100, .5)); + +const acceptBG = new Color(new RGBA(155, 185, 85, .1)); // default is RGBA(155, 185, 85, .2) +const rejectBG = new Color(new RGBA(255, 0, 0, .1)); // default is RGBA(255, 0, 0, .2) // Widget colors -export const acceptBg = '#1a7431' -export const acceptAllBg = '#1e8538' -export const acceptBorder = '1px solid #145626' -export const rejectBg = '#b42331' -export const rejectAllBg = '#cf2838' -export const rejectBorder = '1px solid #8e1c27' +export const acceptAllBg = 'rgb(30, 133, 56)' +export const acceptBg = 'rgb(26, 116, 48)' +export const acceptBorder = '1px solid rgb(20, 86, 38)' + +export const rejectAllBg = 'rgb(207, 40, 56)' +export const rejectBg = 'rgb(180, 35, 49)' +export const rejectBorder = '1px solid rgb(142, 28, 39)' + export const buttonFontSize = '11px' export const buttonTextColor = 'white' -// editCodeService colors -export const greenBG = new Color(new RGBA(155, 185, 85, .1)); // default is RGBA(155, 185, 85, .2) -export const redBG = new Color(new RGBA(255, 0, 0, .1)); // default is RGBA(255, 0, 0, .2) -export const sweepBG = new Color(new RGBA(100, 100, 100, .2)); -export const highlightBG = new Color(new RGBA(100, 100, 100, .1)); -export const sweepIdxBG = new Color(new RGBA(100, 100, 100, .5)); - const configOfBG = (color: Color) => { return { dark: color, light: color, hcDark: color, hcLight: color, } } // gets converted to --vscode-void-greenBG, see void.css, asCssVariable -registerColor('void.greenBG', configOfBG(greenBG), '', true); -registerColor('void.redBG', configOfBG(redBG), '', true); +registerColor('void.greenBG', configOfBG(acceptBG), '', true); +registerColor('void.redBG', configOfBG(rejectBG), '', true); registerColor('void.sweepBG', configOfBG(sweepBG), '', true); registerColor('void.highlightBG', configOfBG(highlightBG), '', true); registerColor('void.sweepIdxBG', configOfBG(sweepIdxBG), '', true); diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 9e62168f..79039f7b 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -107,8 +107,8 @@ export const defaultModelsOfProvider = { 'anthropic/claude-3.5-sonnet', 'deepseek/deepseek-r1', 'deepseek/deepseek-r1-zero:free', - 'openrouter/quasar-alpha', - 'google/gemini-2.5-pro-preview-03-25', + // 'openrouter/quasar-alpha', + // 'google/gemini-2.5-pro-preview-03-25', // 'mistralai/codestral-2501', // 'qwen/qwen-2.5-coder-32b-instruct', // 'mistralai/mistral-small-3.1-24b-instruct:free', @@ -647,7 +647,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 0.15, output: .60 }, // TODO $3.50 output with thinking not included downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, @@ -657,7 +657,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 0, output: 0 }, downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, @@ -667,7 +667,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 0.10, output: 0.40 }, downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, @@ -677,7 +677,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 0.075, output: 0.30 }, downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, @@ -687,7 +687,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 0.075, output: 0.30 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, @@ -697,7 +697,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 1.25, output: 5.00 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, @@ -707,7 +707,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing cost: { input: 0.0375, output: 0.15 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now downloadable: false, supportsFIM: false, - supportsSystemMessage: 'system-role', + supportsSystemMessage: 'separated', specialToolFormat: 'gemini-style', reasoningCapabilities: false, }, diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index 42f68636..d0e27fcd 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -124,23 +124,18 @@ ${searchReplaceBlockTemplate} // ======================================================== tools ======================================================== -const changesExampleContent = `\ + + +const chatSuggestionDiffExample = `\ +${tripleTick[0]}typescript +/Users/username/Dekstop/my_project/app.ts // ... existing code ... // {{change 1}} // ... existing code ... // {{change 2}} // ... existing code ... // {{change 3}} -// ... existing code ...` - -const editToolDescriptionExample = `\ -${tripleTick[0]} -${changesExampleContent} -${tripleTick[1]}` - -const chatSuggestionDiffExample = `${tripleTick[0]}typescript -/Users/username/Dekstop/my_project/app.ts -${changesExampleContent} +// ... existing code ... ${tripleTick[1]}` @@ -185,15 +180,6 @@ export type SnakeCaseKeys> = { -const applyToolDescription = (type: 'edit tool' | 'chat suggestion') => `\ -${type === 'edit tool' ? 'A' : 'a'} code diff describing the change to make to the file. \ -Your DIFF is the only context that will be given to another LLM to apply the change, so it must be accurate and complete. \ -Your DIFF MUST be wrapped in triple backticks. \ -NEVER re-write the whole file. Always bias towards writing as little as possible. \ -Use comments like "// ... existing code ..." to condense your writing. \ -Here's an example of a good output:\n${type === 'edit tool' ? editToolDescriptionExample : chatSuggestionDiffExample}` - - // export const voidTools = { export const voidTools : { @@ -499,11 +485,13 @@ ${directoryStr} - The remaining contents of the file should proceed as usual.`) details.push(`If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S). -- The first line of the code block must be the FULL PATH of the related file. -- The remaining contents should be ${applyToolDescription('chat suggestion')}`) +- The first line of the code block must be the FULL PATH of the related file if known (otherwise omit). +- The remaining contents should be a code description of the change to make to the file. \ +Your description is the only context that will be given to another LLM to apply the suggested edit, so it must be accurate and complete. \ +Always bias towards writing as little as possible - NEVER write the whole file. Use comments like "// ... existing code ..." to condense your writing. \ +Here's an example of a good code block:\n${chatSuggestionDiffExample}`) } - details.push(`NEVER write the FULL PATH of a file when speaking with the user. Just write the file name ONLY.`) details.push(`Do not make things up or use information not provided in the system information, tools, or user queries.`) details.push(`Always use MARKDOWN to format lists, bullet points, etc. Do NOT write tables.`) details.push(`Today's date is ${new Date().toDateString()}.`)