error UI less overwhelming, gemini separated system message, improve Apply prompt

This commit is contained in:
Andrew Pareles 2025-04-30 21:42:27 -07:00
parent 1ed82ba174
commit d9f2fcd700
4 changed files with 90 additions and 91 deletions

View file

@ -810,7 +810,7 @@ const ToolHeaderWrapper = ({
const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters<ResultWrapper<'edit_file' | 'rewrite_file'>>[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<Res
/>
</ToolChildrenWrapper>
if (toolMessage.type !== 'tool_error') {
if (toolMessage.type === 'success' || toolMessage.type === 'rejected') {
const { result } = toolMessage
componentParams.bottomChildren = <BottomChildren title='Lint errors'>
{result?.lintErrors?.map((error, i) => (
<div key={i} className="">Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}</div>
<div key={i} className='whitespace-nowrap'>Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}</div>
))}
</BottomChildren>
}
else {
else if (toolMessage.type === 'tool_error') {
// error
const { result } = toolMessage
componentParams.bottomChildren = <BottomChildren title='Error'>
{result}
<CodeChildren>
{result}
</CodeChildren>
</BottomChildren>
}
}
@ -1579,7 +1581,7 @@ const BottomChildren = ({ children, title }: { children: React.ReactNode, title:
<div
className={`overflow-hidden transition-all duration-200 ease-in-out ${isOpen ? 'opacity-100' : 'max-h-0 opacity-0'} text-xs pl-4`}
>
<div className="flex flex-col gap-0.5 overflow-x-auto whitespace-nowrap text-void-fg-4 opacity-90 border-l-2 border-void-warning px-2 py-0.5">
<div className="overflow-x-auto text-void-fg-4 opacity-90 border-l-2 border-void-warning px-2 py-0.5">
{children}
</div>
</div>
@ -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 = <ToolChildrenWrapper>
{result}
</ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</BottomChildren>
}
else if (toolMessage.type === 'running_now') {
componentParams.children = <div ref={divRef} className='relative h-[300px] text-sm' />
@ -1742,7 +1746,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
else if (toolMessage.type === 'tool_error') {
const { result } = toolMessage
componentParams.desc2 = <JumpToFileButton uri={params.uri} />
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
@ -1787,7 +1791,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
}
else {
const { result } = toolMessage
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
@ -1835,7 +1839,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
}
else {
const { result } = toolMessage
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
@ -1881,7 +1885,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
}
else {
const { result } = toolMessage
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
@ -1930,7 +1934,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
}
else {
const { result } = toolMessage
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
}
@ -1986,7 +1990,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
</CodeChildren>
</ToolChildrenWrapper>
}
else {
else if (toolMessage.type === 'tool_error') {
const { result } = toolMessage;
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>;
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />;
@ -2042,7 +2046,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
else if (toolMessage.type === 'tool_error') {
const { result } = toolMessage
if (params) componentParams.desc2 = <JumpToFileButton uri={params.uri} />
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
@ -2078,7 +2082,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
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 = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
else if (toolMessage.type === 'running_now') {
// nothing more is needed
@ -2121,7 +2125,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
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 = <ToolChildrenWrapper>
componentParams.children = componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
else if (toolMessage.type === 'running_now') {
const { result } = toolMessage
@ -2196,7 +2200,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
}
else if (toolMessage.type === 'tool_error') {
const { result } = toolMessage
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />
@ -2233,7 +2237,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
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<T>,
}
else if (toolMessage.type === 'tool_error') {
const { result } = toolMessage
componentParams.children = <ToolChildrenWrapper>
componentParams.bottomChildren = <BottomChildren title='Error'>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
</BottomChildren>
}
return <ToolHeaderWrapper {...componentParams} />

View file

@ -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);

View file

@ -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,
},

View file

@ -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<T extends Record<string, any>> = {
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()}.`)