From 20b0fd6af92585aca55af2a897979266d51d33f8 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 14 Apr 2025 10:57:26 -0700 Subject: [PATCH 1/6] add 4.1 --- .../contrib/void/common/modelCapabilities.ts | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index eaf61c38..a672c7e4 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -48,11 +48,14 @@ export const defaultProviderSettings = { export const defaultModelsOfProvider = { openAI: [ // https://platform.openai.com/docs/models/gp + 'gpt-4.1', + 'gpt-4.1-mini', + 'gpt-4.1-nano', 'o3-mini', - 'o1', - 'o1-mini', - 'gpt-4o', - 'gpt-4o-mini', + // 'o1', + // 'o1-mini', + // 'gpt-4o', + // 'gpt-4o-mini', ], anthropic: [ // https://docs.anthropic.com/en/docs/about-claude/models 'claude-3-7-sonnet-latest', @@ -445,6 +448,33 @@ const anthropicSettings: VoidStaticProviderInfo = { // ---------------- OPENAI ---------------- const openAIModelOptions = { // https://platform.openai.com/docs/pricing + 'gpt-4.1': { + contextWindow: 1_047_576, + maxOutputTokens: 32_768, + cost: { input: 2.00, output: 8.00, cache_read: 0.50 }, + downloadable: false, + supportsFIM: false, + supportsSystemMessage: 'developer-role', + reasoningCapabilities: false, + }, + 'gpt-4.1-mini': { + contextWindow: 1_047_576, + maxOutputTokens: 32_768, + cost: { input: 0.40, output: 1.60, cache_read: 0.10 }, + downloadable: false, + supportsFIM: false, + supportsSystemMessage: 'developer-role', + reasoningCapabilities: false, + }, + 'gpt-4.1-nano': { + contextWindow: 1_047_576, + maxOutputTokens: 32_768, + cost: { input: 0.10, output: 0.40, cache_read: 0.03 }, + downloadable: false, + supportsFIM: false, + supportsSystemMessage: 'developer-role', + reasoningCapabilities: false, + }, 'o1': { contextWindow: 128_000, maxOutputTokens: 100_000, From a80883c10fa6cc3edbd66004c9953531bbbfb25c Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 14 Apr 2025 11:10:52 -0700 Subject: [PATCH 2/6] 4.1 fallback for OSS providers --- src/vs/workbench/contrib/void/common/modelCapabilities.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index a672c7e4..f29c6a52 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -348,12 +348,16 @@ const extensiveModelFallback: VoidStaticProviderInfo['modelOptionsFallback'] = ( if (lower.includes('quasar') || lower.includes('quaser')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['quasar'] }) + if (lower.includes('gpt') && lower.includes('mini') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions['gpt-4.1-mini']) + if (lower.includes('gpt') && lower.includes('nano') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions['gpt-4.1-nano']) + if (lower.includes('gpt') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions['gpt-4.1']) + if (lower.includes('4o') && lower.includes('mini')) return toFallback(openAIModelOptions['gpt-4o-mini']) if (lower.includes('4o')) return toFallback(openAIModelOptions['gpt-4o']) + if (lower.includes('o1') && lower.includes('mini')) return toFallback(openAIModelOptions['o1-mini']) if (lower.includes('o1')) return toFallback(openAIModelOptions['o1']) if (lower.includes('o3') && lower.includes('mini')) return toFallback(openAIModelOptions['o3-mini']) - // if (lower.includes('o3')) return toFallback(openAIModelOptions['o3']) if (Object.keys(openSourceModelOptions_assumingOAICompat).map(k => k.toLowerCase()).includes(lower)) return toFallback(openSourceModelOptions_assumingOAICompat[lower as keyof typeof openSourceModelOptions_assumingOAICompat]) From 2ea20243a8ca5dc6f4f6bfe755b45dfe80a4d5f8 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 14 Apr 2025 12:31:31 -0700 Subject: [PATCH 3/6] lint error UI + misc --- .../react/src/markdown/ChatMarkdownRender.tsx | 10 +-- .../react/src/sidebar-tsx/SidebarChat.tsx | 71 ++++++++++++++++--- .../contrib/void/common/voidSettingsTypes.ts | 5 +- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index 2507f796..acf731f4 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -304,12 +304,6 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. return

{contents}

} - if (t.type === "html") { - const contents = t.raw - if (inPTag) return {contents} - return

{contents}

- } - if (t.type === "text" || t.type === "escape") { return {t.raw} } @@ -324,7 +318,7 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. onClick={() => { window.open(t.href) }} href={t.href} title={t.title ?? undefined} - className='underline cursor-pointer hover:brightness-90 transition-all duration-200' + className='underline cursor-pointer hover:brightness-90 transition-all duration-200 text-void-fg-2' > {t.text} @@ -349,7 +343,7 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. } // inline code - if (t.type === "codespan") { + if (t.type === "codespan" || t.type === "html") { if (options.isLinkDetectionEnabled && chatMessageLocation) { return void; - isOpen?: boolean, + isOpen?: boolean; + className?: string; } const ToolHeaderWrapper = ({ @@ -679,7 +679,7 @@ const ToolHeaderWrapper = ({ isOpen, isRejected, className, // applies to the main content -}: ToolHeaderParams & { className?: string }) => { +}: ToolHeaderParams) => { const [isOpen_, setIsOpen] = useState(false); const isExpanded = isOpen !== undefined ? isOpen : isOpen_ @@ -1176,7 +1176,8 @@ const titleOfToolName = { 'create_file_or_folder': { done: `Created`, proposed: `Create`, running: loadingTitleWrapper(`Creating`) }, 'delete_file_or_folder': { done: `Deleted`, proposed: `Delete`, running: loadingTitleWrapper(`Deleting`) }, 'edit_file': { done: `Edited file`, proposed: 'Edit file', running: loadingTitleWrapper('Editing file') }, - 'run_terminal_command': { done: `Ran terminal`, proposed: 'Run terminal', running: loadingTitleWrapper('Running terminal') } + 'run_terminal_command': { done: `Ran terminal`, proposed: 'Run terminal', running: loadingTitleWrapper('Running terminal') }, + 'read_lint_errors': { done: `Read lint errors`, proposed: 'Read lint errors', running: loadingTitleWrapper('Reading lint errors') }, } as const satisfies Record const getTitle = (toolMessage: Pick): React.ReactNode => { @@ -1343,6 +1344,15 @@ const EditToolChildren = ({ uri, changeDescription }: { uri: URI | undefined, ch } + +const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => { + return
+ {lintErrors.map((error, i) => ( +
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
+ ))} +
+} + const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => { if (lintErrors.length === 0) return null; @@ -1351,12 +1361,8 @@ const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => return (
- { setIsOpen(o => !o) }} > -
- {lintErrors.map((error, i) => ( -
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
- ))} -
+ { setIsOpen(o => !o) }} > +
@@ -1427,6 +1433,13 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, const isError = toolMessage.type === 'tool_error' const componentParams: ToolHeaderParams = { title, desc1, isError, icon } + if (toolMessage.params.startLine !== null || toolMessage.params.endLine !== null) { + const start = toolMessage.params.startLine === null ? `start` : `${toolMessage.params.startLine}` + const end = toolMessage.params.endLine === null ? `end` : `${toolMessage.params.endLine}` + const addStr = `(${start}-${end})` + componentParams.title += ` ${addStr}` + } + if (toolMessage.type === 'success') { const { params, result } = toolMessage componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } @@ -1626,6 +1639,44 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } }, + 'read_lint_errors': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + + const title = getTitle(toolMessage) + + const { uri } = toolMessage.params ?? {} + const desc1 = uri ? getBasename(uri.fsPath) : ''; + const icon = null + + if (toolMessage.type === 'tool_request') return null + if (toolMessage.type === 'rejected') return null // will never happen, not rejectable + if (toolMessage.type === 'running_now') return null // do not show running + + const isError = toolMessage.type === 'tool_error' + const componentParams: ToolHeaderParams = { title, desc1, isError, icon } + + if (toolMessage.type === 'success') { + const { params, result } = toolMessage + componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } + + + } + else if (toolMessage.type === 'tool_error') { + const { params, result } = toolMessage + if (params) componentParams.desc2 = + componentParams.children = + + {result} + + + } + + return + }, + }, + // --- 'create_file_or_folder': { diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 284b61fd..b07e930c 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -143,8 +143,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'gemini' ? 'Get your [API Key here](https://aistudio.google.com/apikey).' : providerName === 'groq' ? 'Get your [API Key here](https://console.groq.com/keys).' : providerName === 'xAI' ? 'Get your [API Key here](https://console.x.ai).' : - providerName === 'openAICompatible' ? undefined : - '', + providerName === 'mistral' ? 'Get your [API Key here](https://console.mistral.ai/api-keys).' : + providerName === 'openAICompatible' ? `Use any OpenAI-compatible endpoint (LM Studio, LiteLM, etc).` : + '', isPasswordField: true, } } From 680125baec9954912d6d8018539dbb0ecf5fcfd9 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 14 Apr 2025 12:48:44 -0700 Subject: [PATCH 4/6] hot-swap out default models on mount --- .../void/common/voidSettingsService.ts | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index ad237922..09be5a02 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -71,8 +71,8 @@ export interface IVoidSettingsService { -const _updatedModelsAfterDefaultModelsChange = (defaultModelNames: string[], options: { existingModels: VoidStatefulModelInfo[] }) => { - const { existingModels } = options +const _updatedModelsAfterDefaultModelsChange = (defaultModelNames: string[], options: { existingModels: VoidStatefulModelInfo[], didAutoDetect: boolean }) => { + const { existingModels, didAutoDetect } = options const existingModelsMap: Record = {} for (const existingModel of existingModels) { @@ -82,7 +82,7 @@ const _updatedModelsAfterDefaultModelsChange = (defaultModelNames: string[], opt const newDefaultModels = defaultModelNames.map((modelName, i) => ({ modelName, isDefault: true, - isAutodetected: true, + isAutodetected: didAutoDetect, isHidden: !!existingModelsMap[modelName]?.isHidden, })) @@ -101,7 +101,30 @@ export const modelFilterOfFeatureName: { [featureName in FeatureName]: { filter: } -const _validatedModelState = (state: Omit) => { +const _stateWithUpdatedDefaultModels = (state: VoidSettingsState): VoidSettingsState => { + let newSettingsOfProvider = state.settingsOfProvider + + // recompute default models + for (const providerName of providerNames) { + const defaultModels = defaultSettingsOfProvider[providerName]?.models ?? [] + const currentModels = newSettingsOfProvider[providerName]?.models ?? [] + const defaultModelNames = defaultModels.map(m => m.modelName) + const newModels = _updatedModelsAfterDefaultModelsChange(defaultModelNames, { existingModels: currentModels, didAutoDetect: false }) + newSettingsOfProvider = { + ...newSettingsOfProvider, + [providerName]: { + ...newSettingsOfProvider[providerName], + models: newModels, + }, + } + } + return { + ...state, + settingsOfProvider: newSettingsOfProvider, + } +} + +const _validatedModelState = (state: Omit): VoidSettingsState => { let newSettingsOfProvider = state.settingsOfProvider @@ -222,8 +245,10 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } // the stored data structure might be outdated, so we need to update it here - const finalState = readS - this.state = _validatedModelState(finalState); + this.state = readS + this.state = _stateWithUpdatedDefaultModels(this.state) + this.state = _validatedModelState(this.state); + this._resolver(); this._onDidChangeState.fire(); @@ -353,7 +378,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { const { models } = this.state.settingsOfProvider[providerName] const oldModelNames = models.map(m => m.modelName) - const newModels = _updatedModelsAfterDefaultModelsChange(autodetectedModelNames, { existingModels: models }) + const newModels = _updatedModelsAfterDefaultModelsChange(autodetectedModelNames, { existingModels: models, didAutoDetect: true }) this.setSettingOfProvider(providerName, 'models', newModels) // if the models changed, log it From a8cf0b361f9c7f4ff0a32200310cdc7d49080e5f Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 14 Apr 2025 13:29:13 -0700 Subject: [PATCH 5/6] prompt --- src/vs/workbench/contrib/void/common/prompt/prompts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index c3e910dc..03264665 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -241,8 +241,8 @@ ${availableXMLToolsStr(tools)}`) const toolCallXMLGuidelines = (`\ Tool calling details: - Once you write a tool call, you must STOP and WAIT for the result. +- To call a tool, write its name and parameters in one of the XML formats specified above at the BOTTOM of your response. - All parameters are REQUIRED unless noted otherwise. -- To call a tool, write its name and parameters in one of the XML formats specified above. - You are only allowed to output ONE tool call, and it must be at the END of your response. - Your tool call will be executed immediately, and the results will appear in the following user message.`) From 9b3d4cbdba0e6b5c4975b5b6a97307416429f5d0 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 14 Apr 2025 13:33:49 -0700 Subject: [PATCH 6/6] lint errors --- .../void/browser/react/src/sidebar-tsx/SidebarChat.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 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 5daab642..78e23395 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 @@ -1361,7 +1361,7 @@ const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => return (
- { setIsOpen(o => !o) }} > + { setIsOpen(o => !o) }} > @@ -1660,7 +1660,10 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, if (toolMessage.type === 'success') { const { params, result } = toolMessage componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) } - + if (result.lintErrors) + componentParams.children = + else + componentParams.children = `No lint errors found.` } else if (toolMessage.type === 'tool_error') {