From 5ad5fe7014958d33ddd36f3ee5b2d31cb0abb252 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 03:14:20 -0800 Subject: [PATCH 01/13] clickable tool requests --- .../react/src/sidebar-tsx/SidebarChat.tsx | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 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 6dab5ce1..11c5d7e8 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 @@ -1034,9 +1034,13 @@ const toolNameToComponent: { [T in ToolName]: { } } = { 'read_file': { requestWrapper: ({ toolRequest }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] const { params } = toolRequest - return } /> + return } + onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} + /> }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1198,9 +1202,13 @@ const toolNameToComponent: { [T in ToolName]: { }, 'delete_uri': { requestWrapper: ({ toolRequest }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] const { params } = toolRequest - return + return { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} + /> }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1218,9 +1226,13 @@ const toolNameToComponent: { [T in ToolName]: { }, 'edit': { requestWrapper: ({ toolRequest }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] const { params } = toolRequest - return } /> + return } + onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} + /> }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1240,9 +1252,13 @@ const toolNameToComponent: { [T in ToolName]: { }, 'terminal_command': { requestWrapper: ({ toolRequest }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] const { params } = toolRequest - return } /> + return } + // TODO!!! open the terminal with that ID + /> }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() From 2b7df8c604d629bf83ab0e004c597a9a33fe46eb Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 19:37:33 -0800 Subject: [PATCH 02/13] remove rawAnthropicContent --- .../contrib/void/browser/chatThreadService.ts | 25 ++++++++-- .../react/src/sidebar-tsx/SidebarChat.tsx | 2 +- .../react/src/void-settings-tsx/Settings.tsx | 2 +- .../contrib/void/common/llmMessageTypes.ts | 22 +-------- .../llmMessage/preprocessLLMMessages.ts | 48 ++++--------------- .../llmMessage/sendLLMMessage.impl.ts | 18 +++++-- 6 files changed, 49 insertions(+), 68 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 9d4326bf..df577509 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -14,7 +14,7 @@ import { IRange } from '../../../../editor/common/core/range.js'; import { ILLMMessageService } from '../common/llmMessageService.js'; import { chat_userMessageContent, chat_systemMessage, chat_lastUserMessageWithFilesAdded, chat_selectionsString } from './prompt/prompts.js'; import { InternalToolInfo, IToolsService, ToolCallParams, ToolResultType, ToolName, toolNamesThatRequireApproval, voidTools } from './toolsService.js'; -import { LLMChatMessage, toLLMChatMessage, ToolCallType } from '../common/llmMessageTypes.js'; +import { LLMChatMessage, ToolCallType } from '../common/llmMessageTypes.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IVoidFileService } from '../common/voidFileService.js'; import { generateUuid } from '../../../../base/common/uuid.js'; @@ -32,6 +32,23 @@ const findLastIndex = (arr: T[], condition: (t: T) => boolean): number => { } + +const toLLMChatMessage = (c: ChatMessage): LLMChatMessage | null => { + if (c.role === 'user') { + return { role: c.role, content: c.content || '(empty message)' } + } + else if (c.role === 'assistant') + return { role: c.role, content: c.content || '(empty message)' } + else if (c.role === 'tool') + return { role: c.role, id: c.id, name: c.name, params: c.paramsStr, content: c.content || '(empty output)' } + else if (c.role === 'tool_request') + return null + else { + throw new Error(`Role ${(c as any).role} not recognized.`) + } +} + + // one of the square items that indicates a selection in a chat bubble (NOT a file, a Selection of text) export type CodeSelection = { type: 'Selection'; @@ -73,7 +90,7 @@ export type ToolRequestApproval = { // WARNING: changing this format is a big deal!!!!!! need to migrate old format to new format on users' computers so people don't get errors. export type ChatMessage = - { + | { role: 'user'; content: string | null; // content displayed to the LLM on future calls - allowed to be '', will be replaced with (empty) displayContent: string | null; // content displayed to user - allowed to be '', will be ignored @@ -460,6 +477,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { catch (e) { console.log('ERR2') + // TODO!!! test rejection + // if (Math.random() > 0) throw new Error('TESTING') + const errorMessage = 'Tool call was rejected by the user.' this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) res_() @@ -486,7 +506,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { console.log('H') toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) - // if (Math.random() > 0) throw new Error('This is not an allowed repo.') } catch (error) { const errorMessage = `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}` 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 11c5d7e8..82170820 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 @@ -1257,7 +1257,7 @@ const toolNameToComponent: { [T in ToolName]: { const title = toolNameToTitle[toolRequest.name] const { params } = toolRequest return } - // TODO!!! open the terminal with that ID + // TODO!!! open the terminal with that ID /> }, resultWrapper: ({ toolMessage }) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index 9a380878..51efa796 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -459,7 +459,7 @@ export const FeaturesTab = () => {

{displayInfoOfFeatureName('Apply')}

-
We recommend the smartest model you{`'`}ve got, like Claude 3.7 or GPT 4o.
+
We recommend using Claude 3.7 or GPT 4o.
diff --git a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts index 13b078aa..6b21718d 100644 --- a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts +++ b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts @@ -34,7 +34,6 @@ export type LLMChatMessage = { } | { role: 'assistant', content: string; // text content - rawAnthropicAssistantContent?: RawAnthropicAssistantContent[]; // used for anthropic signing } | { role: 'tool'; content: string; // result @@ -50,30 +49,13 @@ export type ToolCallType = { id: string; } -export type RawAnthropicAssistantContent = { type: 'thinking'; thinking: string; signature: string; } | { type: 'redacted_thinking'; data: string } | { type: 'text', text: string } - export type OnText = (p: { fullText: string; fullReasoning: string }) => void -export type OnFinalMessage = (p: { fullText: string, toolCalls?: ToolCallType[], fullReasoning?: string, rawAnthropicAssistantContent?: RawAnthropicAssistantContent[] }) => void // id is tool_use_id +export type OnFinalMessage = (p: { fullText: string, toolCalls?: ToolCallType[], fullReasoning?: string }) => void // id is tool_use_id export type OnError = (p: { message: string, fullError: Error | null }) => void export type AbortRef = { current: (() => void) | null } -export const toLLMChatMessage = (c: ChatMessage): LLMChatMessage | null => { - if (c.role === 'user') { - return { role: c.role, content: c.content || '(empty message)' } - } - else if (c.role === 'assistant') - return { role: c.role, content: c.content || '(empty message)' } - else if (c.role === 'tool') - return { role: c.role, id: c.id, name: c.name, params: c.paramsStr, content: c.content || '(empty output)' } - else if (c.role === 'tool_request') - return null - else { - throw new Error(`Role ${(c as any).role} not recognized.`) - } -} - export type LLMFIMMessage = { prefix: string; @@ -98,7 +80,7 @@ export type ServiceSendLLMMessageParams = { onError: OnError; logging: { loggingName: string, }; useProviderFor: FeatureName; -} & SendLLMType +} & SendLLMType; // params to the true sendLLMMessage function export type SendLLMMessageParams = { diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts index 02bf0e0d..eb912711 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { RawAnthropicAssistantContent, LLMChatMessage, LLMFIMMessage } from '../../common/llmMessageTypes.js'; +import { LLMChatMessage, LLMFIMMessage } from '../../common/llmMessageTypes.js'; import { deepClone } from '../../../../../base/common/objects.js'; @@ -22,8 +22,7 @@ type InternalLLMChatMessage = { content: string; } | { role: 'assistant', - content: string | (RawAnthropicAssistantContent | { type: 'text'; text: string })[]; - rawAnthropicAssistantContent?: RawAnthropicAssistantContent[] | undefined; + content: string | ({ type: 'text'; text: string })[]; } | { role: 'tool'; content: string; // result @@ -41,12 +40,12 @@ const prepareMessages_normalize = ({ messages: messages_ }: { messages: LLMChatM // remove duplicate roles for (let i = 1; i < messages.length; i += 1) { const curr = messages[i] - const prev = messages[i - 1] - // if found a repeated role, put the current content in the prev - if ((curr.role === 'user' && prev.role === 'user') || (curr.role === 'assistant' && prev.role === 'assistant')) { - prev.content += '\n' + curr.content - continue - } + // const prev = messages[i - 1] + // // if found a repeated role, put the current content in the prev + // if ((curr.role === 'assistant' && prev.role === 'assistant')) { + // prev.content += '\n' + curr.content + // continue + // } // add the message newMessages.push(curr) } @@ -58,29 +57,6 @@ const prepareMessages_normalize = ({ messages: messages_ }: { messages: LLMChatM -// remove rawAnthropicAssistantContent, and make content equal to it if supportsAnthropicContent -const prepareMessages_anthropicContent = ({ messages, supportsAnthropicContent }: { messages: LLMChatMessage[], supportsAnthropicContent: boolean }) => { - const newMessages: InternalLLMChatMessage[] = [] - for (const m of messages) { - if (m.role !== 'assistant') { - newMessages.push(m) - continue - } - let newMessage: InternalLLMChatMessage - if (supportsAnthropicContent) { - const newContent = m.rawAnthropicAssistantContent - newMessage = { role: 'assistant', content: newContent ?? m.content } - } - else { - newMessage = m - } - delete newMessage.rawAnthropicAssistantContent // important to delete this field - newMessages.push(m) - } - return { messages: newMessages } -} - - // no matter whether the model supports a system message or not (or what format it supports), add it in some way @@ -255,7 +231,6 @@ const prepareMessages_tools_anthropic = ({ messages }: { messages: InternalLLMCh Exclude | { role: 'assistant', content: string | ( - | RawAnthropicAssistantContent | { type: 'text'; text: string; @@ -366,18 +341,15 @@ export const prepareMessages = ({ aiInstructions, supportsSystemMessage, supportsTools, - supportsAnthropicContent, }: { messages: LLMChatMessage[], aiInstructions: string, supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated', supportsTools: false | 'anthropic-style' | 'openai-style', - supportsAnthropicContent: boolean, }) => { const { messages: messages1 } = prepareMessages_normalize({ messages }) - const { messages: messages2 } = prepareMessages_anthropicContent({ messages: messages1, supportsAnthropicContent }) - const { messages: messages3, separateSystemMessageStr } = prepareMessages_systemMessage({ messages: messages2, aiInstructions, supportsSystemMessage }) - const { messages: messages4 } = prepareMessages_tools({ messages: messages3, supportsTools }) + const { messages: messages2, separateSystemMessageStr } = prepareMessages_systemMessage({ messages: messages1, aiInstructions, supportsSystemMessage }) + const { messages: messages4 } = prepareMessages_tools({ messages: messages2, supportsTools }) return { messages: messages4 as any, diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index f58eae38..d93ff32a 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -168,7 +168,7 @@ const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage const { providerReasoningIOSettings } = getProviderCapabilities(providerName) - const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicContent: false }) // can change supportsAnthropicContent if e.g. OpenRouter starts supporting anthropic extended thinking + const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools }) const tools = (supportsTools && ((tools_?.length ?? 0) !== 0)) ? tools_?.map(tool => toOpenAICompatibleTool(tool)) : undefined const includeInPayload = canIOReasoning ? providerReasoningIOSettings?.input?.includeInPayload || {} : {} @@ -280,7 +280,7 @@ const toAnthropicTool = (toolInfo: InternalToolInfo) => { } satisfies Anthropic.Messages.Tool } -const toolCallsFrom_AnthropicContent = (content: Anthropic.Messages.ContentBlock[]): ToolCallsFrom_ReturnType => { +const toolCallsFrom_Anthropic = (content: Anthropic.Messages.ContentBlock[]): ToolCallsFrom_ReturnType => { return content.map(c => { if (c.type !== 'tool_use') return null if (!isAToolName(c.name)) return null @@ -301,7 +301,10 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM reasoningBudget, } = getModelSelectionState(providerName, modelName_, optionsOfModelSelection) // user's modelName_ here - const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicContent: true }) + const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools }) + + + console.log('MESSAGES!!!!', JSON.stringify(messages, null, 5)) const thisConfig = settingsOfProvider.anthropic const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }); @@ -370,8 +373,8 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM // on done - (or when error/fail) - this is called AFTER last streamEvent stream.on('finalMessage', (response) => { - const toolCalls = toolCallsFrom_AnthropicContent(response.content) - onFinalMessage({ fullText, fullReasoning, toolCalls, rawAnthropicAssistantContent: response.content as any }) + const toolCalls = toolCallsFrom_Anthropic(response.content) + onFinalMessage({ fullText, fullReasoning, toolCalls }) }) // on error stream.on('error', (error) => { @@ -494,6 +497,11 @@ export const sendLLMMessageToProviderImplementation = { sendFIM: null, list: null, }, + // mistral: { + // sendChat: , // TODO + // sendFIM: , // TODO // https://docs.mistral.ai/api/#tag/fim + // list: null, + // }, ollama: { sendChat: (params) => _sendOpenAICompatibleChat(params), sendFIM: sendOllamaFIM, From f89b036d17a730b063c850181a2cfd54dad45c27 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 19:37:39 -0800 Subject: [PATCH 03/13] fix empty message order --- .../contrib/void/browser/chatThreadService.ts | 43 ++-- .../react/src/sidebar-tsx/SidebarChat.tsx | 4 +- .../contrib/void/common/llmMessageTypes.ts | 10 +- .../llmMessage/preprocessLLMMessages.ts | 189 +++++++++++------- .../llmMessage/sendLLMMessage.impl.ts | 4 +- 5 files changed, 150 insertions(+), 100 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index df577509..f95fd790 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -33,19 +33,24 @@ const findLastIndex = (arr: T[], condition: (t: T) => boolean): number => { -const toLLMChatMessage = (c: ChatMessage): LLMChatMessage | null => { - if (c.role === 'user') { - return { role: c.role, content: c.content || '(empty message)' } - } - else if (c.role === 'assistant') - return { role: c.role, content: c.content || '(empty message)' } - else if (c.role === 'tool') - return { role: c.role, id: c.id, name: c.name, params: c.paramsStr, content: c.content || '(empty output)' } - else if (c.role === 'tool_request') - return null - else { - throw new Error(`Role ${(c as any).role} not recognized.`) +const toLLMChatMessages = (chatMessages: ChatMessage[]): LLMChatMessage[] => { + const llmChatMessages: LLMChatMessage[] = [] + for (const c of chatMessages) { + if (c.role === 'user') { + llmChatMessages.push({ role: c.role, content: c.content }) + } + else if (c.role === 'assistant') + llmChatMessages.push({ role: c.role, content: c.content }) + else if (c.role === 'tool') + llmChatMessages.push({ role: c.role, id: c.id, name: c.name, params: c.paramsStr, content: c.content }) + else if (c.role === 'tool_request') { + // pass + } + else { + throw new Error(`Role ${(c as any).role} not recognized.`) + } } + return llmChatMessages } @@ -92,7 +97,7 @@ export type ToolRequestApproval = { export type ChatMessage = | { role: 'user'; - content: string | null; // content displayed to the LLM on future calls - allowed to be '', will be replaced with (empty) + content: string; // content displayed to the LLM on future calls - allowed to be '', will be replaced with (empty) displayContent: string | null; // content displayed to user - allowed to be '', will be ignored selections: StagingSelectionItem[] | null; // the user's selection state: { @@ -101,8 +106,8 @@ export type ChatMessage = } } | { role: 'assistant'; - content: string | null; // content received from LLM - allowed to be '', will be replaced with (empty) - reasoning: string | null; // reasoning from the LLM, used for step-by-step thinking + content: string; // content received from LLM - allowed to be '', will be replaced with (empty) + reasoning: string; // reasoning from the LLM, used for step-by-step thinking } | ToolMessage | ToolRequestApproval @@ -307,9 +312,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { // ---------- streaming ---------- - private _finishStreamingTextMessage = (threadId: string, options: { content: string, reasoning?: string }, error?: { message: string, fullError: Error | null }) => { + private _finishStreamingTextMessage = (threadId: string, options: { content: string, reasoning: string }, error?: { message: string, fullError: Error | null }) => { // add assistant's message to chat history, and clear selection - this._addMessageToThread(threadId, { role: 'assistant', content: options.content, reasoning: options.reasoning || null }) + this._addMessageToThread(threadId, { role: 'assistant', content: options.content, reasoning: options.reasoning }) this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined, error }) } @@ -401,7 +406,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { const awaitable = new Promise((res, rej) => { res_ = res }) // replace last userMessage with userMessageFullContent (which contains all the files too) - const messages_ = this.getCurrentThread().messages.map(m => (toLLMChatMessage(m))).filter(m => !!m) + const messages_ = toLLMChatMessages(this.getCurrentThread().messages) const lastUserMsgIdx = findLastIndex(messages_, m => m.role === 'user') if (lastUserMsgIdx === -1) throw new Error(`Void: No user message found.`) // should never be -1 @@ -430,7 +435,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { this._finishStreamingTextMessage(threadId, { content: fullText, reasoning: fullReasoning }) } else { - this._addMessageToThread(threadId, { role: 'assistant', content: fullText, reasoning: fullReasoning || null }) + this._addMessageToThread(threadId, { role: 'assistant', content: fullText, reasoning: fullReasoning }) this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined }) // clear streaming message // deal with the tool 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 82170820..3fded5d4 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 @@ -1436,8 +1436,8 @@ export const SidebarChat = () => { : null diff --git a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts index 6b21718d..5bac84cc 100644 --- a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts +++ b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts @@ -3,7 +3,6 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import type { ChatMessage } from '../browser/chatThreadService.js' import type { InternalToolInfo, ToolName } from '../browser/toolsService.js' import { FeatureName, OptionsOfModelSelection, ProviderName, SettingsOfProvider } from './voidSettingsTypes.js' @@ -29,7 +28,10 @@ export const getErrorMessage: (error: unknown) => string = (error) => { export type LLMChatMessage = { - role: 'system' | 'user'; + role: 'system'; + content: string; +} | { + role: 'user'; content: string; } | { role: 'assistant', @@ -51,8 +53,8 @@ export type ToolCallType = { export type OnText = (p: { fullText: string; fullReasoning: string }) => void -export type OnFinalMessage = (p: { fullText: string, toolCalls?: ToolCallType[], fullReasoning?: string }) => void // id is tool_use_id -export type OnError = (p: { message: string, fullError: Error | null }) => void +export type OnFinalMessage = (p: { fullText: string; fullReasoning: string; toolCalls?: ToolCallType[]; }) => void // id is tool_use_id +export type OnError = (p: { message: string; fullError: Error | null }) => void export type AbortRef = { current: (() => void) | null } diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts index eb912711..007899cb 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -32,6 +32,9 @@ type InternalLLMChatMessage = { } +const EMPTY_MESSAGE = '(empty message)' +const EMPTY_TOOL_CONTENT = '(empty content)' + const prepareMessages_normalize = ({ messages: messages_ }: { messages: LLMChatMessage[] }) => { const messages = deepClone(messages_) const newMessages: LLMChatMessage[] = [] @@ -149,27 +152,28 @@ openai on prompting - https://platform.openai.com/docs/guides/reasoning#advice-o openai on developer system message - https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command */ +type PrepareMessagesToolsOpenAI = ( + Exclude | { + role: 'assistant', + content: string | { type: 'text'; text: string }[]; + tool_calls?: { + type: 'function'; + id: string; + function: { + name: string; + arguments: string; + } + }[] + } | { + role: 'tool', + id: string; // old val + tool_call_id: string; // new val + content: string; + } +)[] const prepareMessages_tools_openai = ({ messages }: { messages: InternalLLMChatMessage[], }) => { - const newMessages: ( - Exclude | { - role: 'assistant', - content: string | object[]; - tool_calls?: { - type: 'function'; - id: string; - function: { - name: string; - arguments: string; - } - }[] - } | { - role: 'tool', - id: string; // old val - tool_call_id: string; // new val - content: string; - } - )[] = []; + const newMessages: PrepareMessagesToolsOpenAI = []; for (let i = 0; i < messages.length; i += 1) { const currMsg = messages[i] @@ -196,7 +200,7 @@ const prepareMessages_tools_openai = ({ messages }: { messages: InternalLLMChatM newMessages.push({ role: 'tool', id: currMsg.id, - content: currMsg.content, + content: currMsg.content || EMPTY_TOOL_CONTENT, tool_call_id: currMsg.id, }) } @@ -226,33 +230,43 @@ anthropic RESPONSE (role=user): }] */ -const prepareMessages_tools_anthropic = ({ messages }: { messages: InternalLLMChatMessage[], }) => { - const newMessages: ( - Exclude | { - role: 'assistant', - content: string | ( - | { - type: 'text'; - text: string; - } - | { - type: 'tool_use'; - name: string; - input: Record; - id: string; - })[] - } | { - role: 'user', - content: string | ({ +type PrepareMessagesToolsAnthropic = ( + Exclude | { + role: 'assistant', + content: string | ( + | { type: 'text'; text: string; - } | { - type: 'tool_result'; - tool_use_id: string; - content: string; + } + | { + type: 'tool_use'; + name: string; + input: Record; + id: string; })[] - } - )[] = messages; + } | { + role: 'user', + content: string | ({ + type: 'text'; + text: string; + } | { + type: 'tool_result'; + tool_use_id: string; + content: string; + })[] + } +)[] +/* +Converts: + +assistant: ...content +tool: (id, name, params) +-> +assistant: ...content, call(name, id, params) +user: ...content, result(id, content) +*/ +const prepareMessages_tools_anthropic = ({ messages }: { messages: InternalLLMChatMessage[], }) => { + const newMessages: PrepareMessagesToolsAnthropic = messages; for (let i = 0; i < newMessages.length; i += 1) { @@ -282,8 +296,9 @@ const prepareMessages_tools_anthropic = ({ messages }: { messages: InternalLLMCh +type PrepareMessagesTools = PrepareMessagesToolsAnthropic | PrepareMessagesToolsOpenAI -const prepareMessages_tools = ({ messages, supportsTools }: { messages: InternalLLMChatMessage[], supportsTools: false | 'anthropic-style' | 'openai-style' }) => { +const prepareMessages_tools = ({ messages, supportsTools }: { messages: InternalLLMChatMessage[], supportsTools: false | 'anthropic-style' | 'openai-style' }): { messages: PrepareMessagesTools } => { if (!supportsTools) { return { messages: messages } } @@ -302,36 +317,27 @@ const prepareMessages_tools = ({ messages, supportsTools }: { messages: Internal -/* -Gemini has this, but they're openai-compat so we don't need to implement this -gemini request: -{ "role": "assistant", - "content": null, - "function_call": { - "name": "get_weather", - "arguments": { - "latitude": 48.8566, - "longitude": 2.3522 +// do this at end +const prepareMessages_noEmptyMessage = ({ messages }: { messages: PrepareMessagesTools }): { messages: PrepareMessagesTools } => { + for (const currMsg of messages) { + + if (currMsg.role === 'assistant' || currMsg.role === 'user') { + if (typeof currMsg.content === 'string') { + currMsg.content = currMsg.content || EMPTY_MESSAGE + } + else { + for (const c of currMsg.content) { + if (c.type === 'text') c.text = c.text || EMPTY_MESSAGE + else if (c.type === 'tool_use') { } + else if (c.type === 'tool_result') { } + } + } } + } + return { messages } } -gemini response: -{ "role": "assistant", - "function_response": { - "name": "get_weather", - "response": { - "temperature": "15°C", - "condition": "Cloudy" - } - } -} -*/ - - - - - // --- CHAT --- @@ -349,8 +355,8 @@ export const prepareMessages = ({ }) => { const { messages: messages1 } = prepareMessages_normalize({ messages }) const { messages: messages2, separateSystemMessageStr } = prepareMessages_systemMessage({ messages: messages1, aiInstructions, supportsSystemMessage }) - const { messages: messages4 } = prepareMessages_tools({ messages: messages2, supportsTools }) - + const { messages: messages3 } = prepareMessages_tools({ messages: messages2, supportsTools }) + const { messages: messages4 } = prepareMessages_noEmptyMessage({ messages: messages3 }) return { messages: messages4 as any, separateSystemMessageStr @@ -386,3 +392,40 @@ ${messages.prefix}` const ret = { prefix, suffix, stopTokens, maxTokens: 300 } as const return ret } + + + + + + + +/* +Gemini has this, but they're openai-compat so we don't need to implement this +gemini request: +{ "role": "assistant", + "content": null, + "function_call": { + "name": "get_weather", + "arguments": { + "latitude": 48.8566, + "longitude": 2.3522 + } + } +} + +gemini response: +{ "role": "assistant", + "function_response": { + "name": "get_weather", + "response": { + "temperature": "15°C", + "condition": "Cloudy" + } + } +} +*/ + + + + + diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index d93ff32a..925d5be3 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -140,7 +140,7 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError }) .then(async response => { const fullText = response.choices[0]?.text - onFinalMessage({ fullText, }); + onFinalMessage({ fullText, fullReasoning: '' }); }) .catch(error => { if (error instanceof OpenAI.APIError && error.status === 401) { onError({ message: invalidApiKeyMessage(providerName), fullError: error }); } @@ -458,7 +458,7 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO const newText = chunk.response fullText += newText } - onFinalMessage({ fullText }) + onFinalMessage({ fullText, fullReasoning: '' }) }) // when error/fail .catch((error) => { From 7558d4dc1c50f295333133bee342fff903b4afa4 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 20:28:23 -0800 Subject: [PATCH 04/13] anthropic reasoning works with tools --- .../contrib/void/browser/chatThreadService.ts | 27 ++++++------ .../contrib/void/browser/editCodeService.ts | 2 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 1 + .../contrib/void/browser/toolsService.ts | 2 +- .../contrib/void/common/llmMessageTypes.ts | 5 ++- .../llmMessage/preprocessLLMMessages.ts | 44 +++++++++++++++---- .../llmMessage/sendLLMMessage.impl.ts | 15 ++++--- 7 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index f95fd790..8dcd8f72 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -14,7 +14,7 @@ import { IRange } from '../../../../editor/common/core/range.js'; import { ILLMMessageService } from '../common/llmMessageService.js'; import { chat_userMessageContent, chat_systemMessage, chat_lastUserMessageWithFilesAdded, chat_selectionsString } from './prompt/prompts.js'; import { InternalToolInfo, IToolsService, ToolCallParams, ToolResultType, ToolName, toolNamesThatRequireApproval, voidTools } from './toolsService.js'; -import { LLMChatMessage, ToolCallType } from '../common/llmMessageTypes.js'; +import { AnthropicReasoning, LLMChatMessage, ToolCallType } from '../common/llmMessageTypes.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IVoidFileService } from '../common/voidFileService.js'; import { generateUuid } from '../../../../base/common/uuid.js'; @@ -40,7 +40,7 @@ const toLLMChatMessages = (chatMessages: ChatMessage[]): LLMChatMessage[] => { llmChatMessages.push({ role: c.role, content: c.content }) } else if (c.role === 'assistant') - llmChatMessages.push({ role: c.role, content: c.content }) + llmChatMessages.push({ role: c.role, content: c.content, anthropicReasoning: c.anthropicReasoning }) else if (c.role === 'tool') llmChatMessages.push({ role: c.role, id: c.id, name: c.name, params: c.paramsStr, content: c.content }) else if (c.role === 'tool_request') { @@ -108,6 +108,8 @@ export type ChatMessage = role: 'assistant'; content: string; // content received from LLM - allowed to be '', will be replaced with (empty) reasoning: string; // reasoning from the LLM, used for step-by-step thinking + + anthropicReasoning: AnthropicReasoning[] | null; // anthropic reasoning } | ToolMessage | ToolRequestApproval @@ -312,13 +314,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { // ---------- streaming ---------- - private _finishStreamingTextMessage = (threadId: string, options: { content: string, reasoning: string }, error?: { message: string, fullError: Error | null }) => { - // add assistant's message to chat history, and clear selection - this._addMessageToThread(threadId, { role: 'assistant', content: options.content, reasoning: options.reasoning }) - this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined, error }) - } - - async editUserMessageAndStreamResponse({ userMessage, chatMode, messageIdx }: { userMessage: string, chatMode: ChatMode, messageIdx: number }) { @@ -429,13 +424,14 @@ class ChatThreadService extends Disposable implements IChatThreadService { onText: ({ fullText, fullReasoning }) => { this._setStreamState(threadId, { messageSoFar: fullText, reasoningSoFar: fullReasoning }) }, - onFinalMessage: async ({ fullText, toolCalls, fullReasoning }) => { + onFinalMessage: async ({ fullText, toolCalls, fullReasoning, anthropicReasoning }) => { if ((toolCalls?.length ?? 0) === 0) { - this._finishStreamingTextMessage(threadId, { content: fullText, reasoning: fullReasoning }) + this._addMessageToThread(threadId, { role: 'assistant', content: fullText, reasoning: fullReasoning, anthropicReasoning }) + this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined }) } else { - this._addMessageToThread(threadId, { role: 'assistant', content: fullText, reasoning: fullReasoning }) + this._addMessageToThread(threadId, { role: 'assistant', content: fullText, reasoning: fullReasoning, anthropicReasoning }) this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined }) // clear streaming message // deal with the tool @@ -530,7 +526,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { onError: (error) => { const messageSoFar = this.streamState[threadId]?.messageSoFar ?? '' const reasoningSoFar = this.streamState[threadId]?.reasoningSoFar ?? '' - this._finishStreamingTextMessage(threadId, { content: messageSoFar, reasoning: reasoningSoFar }, error) + // add assistant's message to chat history, and clear selection + this._addMessageToThread(threadId, { role: 'assistant', content: messageSoFar, reasoning: reasoningSoFar, anthropicReasoning: null }) + this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined, error }) res_() }, }) @@ -552,7 +550,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { if (llmCancelToken !== undefined) this._llmMessageService.abort(llmCancelToken) const messageSoFar = this.streamState[threadId]?.messageSoFar ?? '' const reasoningSoFar = this.streamState[threadId]?.reasoningSoFar ?? '' - this._finishStreamingTextMessage(threadId, { content: messageSoFar, reasoning: reasoningSoFar }) + this._addMessageToThread(threadId, { role: 'assistant', content: messageSoFar, reasoning: reasoningSoFar, anthropicReasoning: null }) + this._setStreamState(threadId, { messageSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined }) } dismissStreamError(threadId: string): void { diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index 2424f218..d67d84b4 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -1613,7 +1613,7 @@ class EditCodeService extends Disposable implements IEditCodeService { if (typeof originalBounds === 'string') { const content = errMsgOfInvalidStr(originalBounds, block.orig) messages.push( - { role: 'assistant', content: fullText }, // latest output + { role: 'assistant', content: fullText, anthropicReasoning: null }, // latest output { role: 'user', content: content } // user explanation of what's wrong ) 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 3fded5d4..89a635b5 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 @@ -1438,6 +1438,7 @@ export const SidebarChat = () => { role: 'assistant', content: messageSoFar ?? '', reasoning: reasoningSoFar ?? '', + anthropicReasoning: null, }} isLoading={isStreaming} /> : null diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts index 40c55dbd..a598c254 100644 --- a/src/vs/workbench/contrib/void/browser/toolsService.ts +++ b/src/vs/workbench/contrib/void/browser/toolsService.ts @@ -395,7 +395,7 @@ export class ToolsService implements IToolsService { const fromIdx = MAX_FILE_CHARS_PAGE * (pageNumber - 1) const toIdx = MAX_FILE_CHARS_PAGE * pageNumber - 1 - const fileContents = readFileContents.slice(fromIdx, toIdx + 1) || '(empty)' // paginate + const fileContents = readFileContents.slice(fromIdx, toIdx + 1) // paginate const hasNextPage = (readFileContents.length - 1) - toIdx >= 1 return { fileContents, hasNextPage } }, diff --git a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts index 5bac84cc..09830aee 100644 --- a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts +++ b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts @@ -36,6 +36,7 @@ export type LLMChatMessage = { } | { role: 'assistant', content: string; // text content + anthropicReasoning: AnthropicReasoning[] | null; } | { role: 'tool'; content: string; // result @@ -51,14 +52,14 @@ export type ToolCallType = { id: string; } +export type AnthropicReasoning = ({ type: 'thinking'; thinking: any; signature: string; } | { type: 'redacted_thinking', data: any }) export type OnText = (p: { fullText: string; fullReasoning: string }) => void -export type OnFinalMessage = (p: { fullText: string; fullReasoning: string; toolCalls?: ToolCallType[]; }) => void // id is tool_use_id +export type OnFinalMessage = (p: { fullText: string; fullReasoning: string; toolCalls?: ToolCallType[]; anthropicReasoning: AnthropicReasoning[] | null }) => void // id is tool_use_id export type OnError = (p: { message: string; fullError: Error | null }) => void export type AbortRef = { current: (() => void) | null } - export type LLMFIMMessage = { prefix: string; suffix: string; diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts index 007899cb..55f92819 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { LLMChatMessage, LLMFIMMessage } from '../../common/llmMessageTypes.js'; +import { AnthropicReasoning, LLMChatMessage, LLMFIMMessage } from '../../common/llmMessageTypes.js'; import { deepClone } from '../../../../../base/common/objects.js'; @@ -22,7 +22,7 @@ type InternalLLMChatMessage = { content: string; } | { role: 'assistant', - content: string | ({ type: 'text'; text: string })[]; + content: string | (AnthropicReasoning | { type: 'text'; text: string })[]; } | { role: 'tool'; content: string; // result @@ -35,7 +35,7 @@ type InternalLLMChatMessage = { const EMPTY_MESSAGE = '(empty message)' const EMPTY_TOOL_CONTENT = '(empty content)' -const prepareMessages_normalize = ({ messages: messages_ }: { messages: LLMChatMessage[] }) => { +const prepareMessages_normalize = ({ messages: messages_ }: { messages: LLMChatMessage[] }): { messages: LLMChatMessage[] } => { const messages = deepClone(messages_) const newMessages: LLMChatMessage[] = [] if (messages.length >= 0) newMessages.push(messages[0]) @@ -155,7 +155,7 @@ openai on developer system message - https://cdn.openai.com/spec/model-spec-2024 type PrepareMessagesToolsOpenAI = ( Exclude | { role: 'assistant', - content: string | { type: 'text'; text: string }[]; + content: string | (AnthropicReasoning | { type: 'text'; text: string })[]; tool_calls?: { type: 'function'; id: string; @@ -234,6 +234,7 @@ type PrepareMessagesToolsAnthropic = ( Exclude | { role: 'assistant', content: string | ( + | AnthropicReasoning | { type: 'text'; text: string; @@ -285,7 +286,7 @@ const prepareMessages_tools_anthropic = ({ messages }: { messages: InternalLLMCh newMessages[i] = { role: 'user', content: [ - ...[{ type: 'tool_result', tool_use_id: currMsg.id, content: currMsg.content }] as const, + ...[{ type: 'tool_result', tool_use_id: currMsg.id, content: currMsg.content || EMPTY_TOOL_CONTENT }] as const, ...currMsg.content ? [{ type: 'text', text: currMsg.content }] as const : [], ] } @@ -314,6 +315,28 @@ const prepareMessages_tools = ({ messages, supportsTools }: { messages: Internal } +// remove rawAnthropicAssistantContent, and make content equal to it if supportsAnthropicContent +const prepareMessages_anthropicContent = ({ messages, supportsAnthropicReasoningSignature }: { messages: LLMChatMessage[], supportsAnthropicReasoningSignature: boolean }) => { + const newMessages: InternalLLMChatMessage[] = [] + for (const m of messages) { + if (m.role !== 'assistant') { + newMessages.push(m) + continue + } + let newMessage: InternalLLMChatMessage + if (supportsAnthropicReasoningSignature && m.anthropicReasoning) { + const content = m.content ? [...m.anthropicReasoning, { type: 'text' as const, text: m.content }] : m.anthropicReasoning + newMessage = { role: 'assistant', content: content } + } + else { + newMessage = { role: 'assistant', content: m.content } + } + newMessages.push(newMessage) + } + return { messages: newMessages } +} + + @@ -347,18 +370,21 @@ export const prepareMessages = ({ aiInstructions, supportsSystemMessage, supportsTools, + supportsAnthropicReasoningSignature, }: { messages: LLMChatMessage[], aiInstructions: string, supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated', supportsTools: false | 'anthropic-style' | 'openai-style', + supportsAnthropicReasoningSignature: boolean, }) => { const { messages: messages1 } = prepareMessages_normalize({ messages }) - const { messages: messages2, separateSystemMessageStr } = prepareMessages_systemMessage({ messages: messages1, aiInstructions, supportsSystemMessage }) - const { messages: messages3 } = prepareMessages_tools({ messages: messages2, supportsTools }) - const { messages: messages4 } = prepareMessages_noEmptyMessage({ messages: messages3 }) + const { messages: messages2 } = prepareMessages_anthropicContent({ messages: messages1, supportsAnthropicReasoningSignature }) + const { messages: messages3, separateSystemMessageStr } = prepareMessages_systemMessage({ messages: messages2, aiInstructions, supportsSystemMessage }) + const { messages: messages4 } = prepareMessages_tools({ messages: messages3, supportsTools }) + const { messages: messages5 } = prepareMessages_noEmptyMessage({ messages: messages4 }) return { - messages: messages4 as any, + messages: messages5 as any, separateSystemMessageStr } as const } diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 925d5be3..901ff013 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -140,7 +140,7 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError }) .then(async response => { const fullText = response.choices[0]?.text - onFinalMessage({ fullText, fullReasoning: '' }); + onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }); }) .catch(error => { if (error instanceof OpenAI.APIError && error.status === 401) { onError({ message: invalidApiKeyMessage(providerName), fullError: error }); } @@ -168,7 +168,7 @@ const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage const { providerReasoningIOSettings } = getProviderCapabilities(providerName) - const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools }) + const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: false }) const tools = (supportsTools && ((tools_?.length ?? 0) !== 0)) ? tools_?.map(tool => toOpenAICompatibleTool(tool)) : undefined const includeInPayload = canIOReasoning ? providerReasoningIOSettings?.input?.includeInPayload || {} : {} @@ -222,9 +222,9 @@ const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage else { if (manuallyParseReasoning) { const { fullText, fullReasoning } = extractReasoningOnFinalMessage(fullTextSoFar, openSourceThinkTags) - onFinalMessage({ fullText, fullReasoning, toolCalls }); + onFinalMessage({ fullText, fullReasoning, toolCalls, anthropicReasoning: null }); } else { - onFinalMessage({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, toolCalls }); + onFinalMessage({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, toolCalls, anthropicReasoning: null }); } } }) @@ -301,7 +301,7 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM reasoningBudget, } = getModelSelectionState(providerName, modelName_, optionsOfModelSelection) // user's modelName_ here - const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools }) + const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: true }) console.log('MESSAGES!!!!', JSON.stringify(messages, null, 5)) @@ -374,7 +374,8 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM // on done - (or when error/fail) - this is called AFTER last streamEvent stream.on('finalMessage', (response) => { const toolCalls = toolCallsFrom_Anthropic(response.content) - onFinalMessage({ fullText, fullReasoning, toolCalls }) + const anthropicReasoning = response.content.filter(c => c.type === 'thinking' || c.type === 'redacted_thinking') + onFinalMessage({ fullText, fullReasoning, toolCalls, anthropicReasoning }) }) // on error stream.on('error', (error) => { @@ -458,7 +459,7 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO const newText = chunk.response fullText += newText } - onFinalMessage({ fullText, fullReasoning: '' }) + onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }) }) // when error/fail .catch((error) => { From db7ec0272adf3ba39867ff31c2d96118f86c9468 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 20:48:57 -0800 Subject: [PATCH 05/13] don't display empty assistant messages (these are only used for LLMMessage) --- .../browser/react/src/sidebar-tsx/SidebarChat.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 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 89a635b5..f577588b 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 @@ -727,8 +727,6 @@ const DropdownComponent = ({ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubbleProps & { chatMessage: ChatMessage & { role: 'user' } }) => { - const role = chatMessage.role - const accessor = useAccessor() const chatThreadsService = accessor.get('IChatThreadService') @@ -758,7 +756,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble const _mustInitialize = useRef(true) const _justEnabledEdit = useRef(false) useEffect(() => { - const canInitialize = role === 'user' && mode === 'edit' && textAreaRefState + const canInitialize = mode === 'edit' && textAreaRefState const shouldInitialize = _justEnabledEdit.current || _mustInitialize.current if (canInitialize && shouldInitialize) { setStagingSelections(chatMessage.selections || []) @@ -771,7 +769,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble _mustInitialize.current = false } - }, [chatMessage, role, mode, _justEnabledEdit, textAreaRefState, textAreaFnsRef.current, _justEnabledEdit.current, _mustInitialize.current]) + }, [chatMessage, mode, _justEnabledEdit, textAreaRefState, textAreaFnsRef.current, _justEnabledEdit.current, _mustInitialize.current]) const onOpenEdit = () => { setIsBeingEdited(true) @@ -893,7 +891,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble - {role === 'user' && {/* reasoning token */} @@ -1293,6 +1295,8 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps) => const role = chatMessage.role + console.log('ROLE', role) + if (role === 'user') { return Date: Fri, 7 Mar 2025 20:54:55 -0800 Subject: [PATCH 06/13] switch --- .../react/src/void-settings-tsx/Settings.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index 51efa796..5b0e13d0 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -366,15 +366,20 @@ export const AutoRefreshToggle = () => { // right now this is just `enabled_autoRefreshModels` const enabled = voidSettingsState.globalSettings[settingName] - return { - voidSettingsService.setGlobalSetting(settingName, !enabled) - metricsService.capture('Click', { action: 'Autorefresh Toggle', settingName, enabled: !enabled }) - }} - text={`Automatically detect local providers and models (${refreshableProviderNames.map(providerName => displayInfoOfProviderName(providerName).title).join(', ')}).`} - icon={enabled ? : } - disabled={false} - /> + return
+ { + voidSettingsService.setGlobalSetting(settingName, !enabled) + metricsService.capture('Click', { action: 'Autorefresh Toggle', settingName, enabled: newVal }) + }} /> + + + {`Automatically detect local providers and models (${refreshableProviderNames.map(providerName => displayInfoOfProviderName(providerName).title).join(', ')}).`} + +
+ } From 6be0b74047e87ead1529c57b132f8493eceab66a Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 20:55:42 -0800 Subject: [PATCH 07/13] update --- .../void/browser/react/src/void-settings-tsx/Settings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index 5b0e13d0..3a4580fb 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -366,12 +366,12 @@ export const AutoRefreshToggle = () => { // right now this is just `enabled_autoRefreshModels` const enabled = voidSettingsState.globalSettings[settingName] - return
+ return
{ - voidSettingsService.setGlobalSetting(settingName, !enabled) + voidSettingsService.setGlobalSetting(settingName, newVal) metricsService.capture('Click', { action: 'Autorefresh Toggle', settingName, enabled: newVal }) }} /> From c42a782bde0a2a8fb44872e8fdc682c9dd8688cb Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 20:56:03 -0800 Subject: [PATCH 08/13] rm void- --- .../void/browser/react/src/void-settings-tsx/Settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index 3a4580fb..10068af2 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -366,7 +366,7 @@ export const AutoRefreshToggle = () => { // right now this is just `enabled_autoRefreshModels` const enabled = voidSettingsState.globalSettings[settingName] - return
+ return
Date: Fri, 7 Mar 2025 22:55:06 -0800 Subject: [PATCH 09/13] make it so model does not change during agent mode even if user switches --- .../void/browser/autocompleteService.ts | 14 +++++++-- .../contrib/void/browser/chatThreadService.ts | 24 +++++++++++---- .../contrib/void/browser/editCodeService.ts | 19 ++++++++++-- .../react/src/sidebar-tsx/SidebarChat.tsx | 27 ++++++----------- .../contrib/void/common/llmMessageService.ts | 30 ++++--------------- .../contrib/void/common/llmMessageTypes.ts | 11 +++---- .../contrib/void/common/modelCapabilities.ts | 10 +++---- .../void/common/voidSettingsService.ts | 8 +++-- .../contrib/void/common/voidSettingsTypes.ts | 4 +-- .../llmMessage/sendLLMMessage.impl.ts | 8 ++--- .../llmMessage/sendLLMMessage.ts | 11 +++---- 11 files changed, 89 insertions(+), 77 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 40746fe9..25b2536d 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -19,6 +19,8 @@ import { extractCodeFromRegular } from './helpers/extractCodeFromResult.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import { ILLMMessageService } from '../common/llmMessageService.js'; import { isWindows } from '../../../../base/common/platform.js'; +import { IVoidSettingsService } from '../common/voidSettingsService.js'; +import { FeatureName } from '../common/voidSettingsTypes.js'; // import { IContextGatheringService } from './contextGatheringService.js'; @@ -788,8 +790,14 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ console.log('starting autocomplete...', predictionType) + const featureName: FeatureName = 'Autocomplete' + const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] + const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[modelSelection.providerName]?.[modelSelection.modelName] : undefined + + const isEnabled = this._settingsService.state.globalSettings.enableAutocomplete + // set parameters of `newAutocompletion` appropriately - newAutocompletion.llmPromise = new Promise((resolve, reject) => { + newAutocompletion.llmPromise = isEnabled ? new Promise((resolve, reject) => reject('Autocomplete is disabled')) : new Promise((resolve, reject) => { const requestId = this._llmMessageService.sendLLMMessage({ messagesType: 'FIMMessage', @@ -798,7 +806,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ suffix: llmSuffix, stopTokens: stopTokens, }, - useProviderFor: 'Autocomplete', + modelSelection, + modelSelectionOptions, logging: { loggingName: 'Autocomplete' }, onText: () => { }, // unused in FIMMessage // onText: async ({ fullText, newText }) => { @@ -882,6 +891,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ @ILLMMessageService private readonly _llmMessageService: ILLMMessageService, @IEditorService private readonly _editorService: IEditorService, @IModelService private readonly _modelService: IModelService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, // @IContextGatheringService private readonly _contextGatheringService: IContextGatheringService, ) { super() diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 8dcd8f72..87cc24d7 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -19,7 +19,8 @@ import { IWorkspaceContextService } from '../../../../platform/workspace/common/ import { IVoidFileService } from '../common/voidFileService.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { getErrorMessage } from '../../../../base/common/errors.js'; -import { ChatMode } from '../common/voidSettingsTypes.js'; +import { ChatMode, FeatureName } from '../common/voidSettingsTypes.js'; +import { IVoidSettingsService } from '../common/voidSettingsService.js'; const findLastIndex = (arr: T[], condition: (t: T) => boolean): number => { @@ -172,7 +173,11 @@ const newThreadObject = () => { } satisfies ChatThreads[string] } -export const THREAD_STORAGE_KEY = 'void.chatThreadStorage' + +// past values: +// 'void.chatThreadStorage' + +export const THREAD_STORAGE_KEY = 'void.chatThreadStorageI' export interface IChatThreadService { @@ -237,6 +242,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { @ILLMMessageService private readonly _llmMessageService: ILLMMessageService, @IToolsService private readonly _toolsService: IToolsService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, ) { super() this.state = { allThreads: {}, currentThreadId: null as unknown as string } // default state @@ -383,6 +389,12 @@ class ChatThreadService extends Disposable implements IChatThreadService { : chatMode === 'agent' ? Object.keys(voidTools).map(toolName => voidTools[toolName as ToolName]) : undefined) + // these settings should not change throughout the loop (eg anthropic breaks if you change its thinking mode and it's using tools) + const featureName: FeatureName = 'Chat' + const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] + const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[modelSelection.providerName]?.[modelSelection.modelName] : undefined + + // agent loop const agentLoop = async () => { @@ -413,14 +425,14 @@ class ChatThreadService extends Disposable implements IChatThreadService { ...messages_.slice(lastUserMsgIdx + 1, Infinity), ] + const llmCancelToken = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', - useProviderFor: 'Ctrl+L', - logging: { loggingName: `Agent` }, messages, - tools: tools, - + modelSelection, + modelSelectionOptions, + logging: { loggingName: `Agent` }, onText: ({ fullText, fullReasoning }) => { this._setStreamState(threadId, { messageSoFar: fullText, reasoningSoFar: fullReasoning }) }, diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index d67d84b4..8940ec5f 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -43,6 +43,8 @@ import { LLMChatMessage, OnError, errorDetails } from '../common/llmMessageTypes import { IMetricsService } from '../common/metricsService.js'; import { IVoidFileService } from '../common/voidFileService.js'; import { IEditCodeService, URIStreamState, AddCtrlKOpts, StartApplyingOpts } from './editCodeServiceInterface.js'; +import { IVoidSettingsService } from '../common/voidSettingsService.js'; +import { FeatureName } from '../common/voidSettingsTypes.js'; const configOfBG = (color: Color) => { return { dark: color, light: color, hcDark: color, hcLight: color, } @@ -268,6 +270,7 @@ class EditCodeService extends Disposable implements IEditCodeService { @INotificationService private readonly _notificationService: INotificationService, @ICommandService private readonly _commandService: ICommandService, @IVoidFileService private readonly _voidFileService: IVoidFileService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, ) { super(); @@ -1377,6 +1380,10 @@ class EditCodeService extends Disposable implements IEditCodeService { let fullTextSoFar = '' // so far (INCLUDING ignored suffix) let prevIgnoredSuffix = '' + const featureName: FeatureName = opts.from === 'ClickApply' ? 'Apply' : 'Ctrl+K' + const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] + const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[modelSelection.providerName]?.[modelSelection.modelName] : undefined + const writeover = async () => { let resMessageDonePromise: () => void = () => { } @@ -1384,9 +1391,10 @@ class EditCodeService extends Disposable implements IEditCodeService { streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', - useProviderFor: opts.from === 'ClickApply' ? 'Apply' : 'Ctrl+K', logging: { loggingName: `Edit (Writeover) - ${from}` }, messages, + modelSelection, + modelSelectionOptions, onText: (params) => { const { fullText: fullText_ } = params const newText_ = fullText_.substring(fullTextSoFar.length, Infinity) @@ -1559,6 +1567,12 @@ class EditCodeService extends Disposable implements IEditCodeService { let oldBlocks: ExtractedSearchReplaceBlock[] = [] + + const featureName: FeatureName = 'Apply' + const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] + const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[modelSelection.providerName]?.[modelSelection.modelName] : undefined + + const retryLoop = async () => { // this generates >>>>>>> ORIGINAL <<<<<<< REPLACE blocks and and simultaneously applies it let shouldSendAnotherMessage = true @@ -1573,9 +1587,10 @@ class EditCodeService extends Disposable implements IEditCodeService { streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', - useProviderFor: 'Apply', logging: { loggingName: `Edit (Search/Replace) - ${from}` }, messages, + modelSelection, + modelSelectionOptions, onText: (params) => { const { fullText } = params // blocks are [done done done ... {writingFinal|writingOriginal}] 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 f577588b..1a9da51f 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 @@ -154,7 +154,7 @@ const getChatBubbleId = (threadId: string, messageIdx: number) => `${threadId}-$ // const voidSettingsService = accessor.get('IVoidSettingsService') // const voidSettingsState = useSettingsState() -// const modelSelection = voidSettingsState.modelSelectionOfFeature['Ctrl+L'] +// const modelSelection = voidSettingsState.modelSelectionOfFeature['Chat'] // if (!modelSelection) return null // const { modelName, providerName } = modelSelection @@ -210,13 +210,13 @@ const ReasoningOptionDropdown = () => { const voidSettingsService = accessor.get('IVoidSettingsService') const voidSettingsState = useSettingsState() - const modelSelection = voidSettingsState.modelSelectionOfFeature['Ctrl+L'] + const modelSelection = voidSettingsState.modelSelectionOfFeature['Chat'] if (!modelSelection) return null const { modelName, providerName } = modelSelection const { canToggleReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName).supportsReasoning || {} - const { isReasoningEnabled } = getModelSelectionState(providerName, modelName, voidSettingsState.optionsOfModelSelection) + const { isReasoningEnabled } = getModelSelectionState(providerName, modelName, voidSettingsState.optionsOfModelSelection[providerName]?.[modelName]) if (canToggleReasoning && !reasoningBudgetSlider) { // if it's just a on/off toggle without a power slider (no models right now) return null // unused right now @@ -277,7 +277,6 @@ interface VoidChatAreaProps { divRef?: React.RefObject; // UI customization - featureName: FeatureName; className?: string; showModelDropdown?: boolean; showSelections?: boolean; @@ -304,7 +303,6 @@ export const VoidChatArea: React.FC = ({ isDisabled = false, className = '', showModelDropdown = true, - featureName, showSelections = false, showProspectiveSelections = true, selections, @@ -363,7 +361,7 @@ export const VoidChatArea: React.FC = ({ {showModelDropdown && (
- +
)} @@ -841,7 +839,6 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble isDisabled={isDisabled} showSelections={true} showProspectiveSelections={false} - featureName="Ctrl+L" selections={stagingSelections} setSelections={setStagingSelections} > @@ -1314,22 +1311,17 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps) => else if (role === 'tool_request') { const isLastMessage = true // TODO!!! fix this if (!isLastMessage) return null - const ToolMessageComponent = toolNameToComponent[chatMessage.name].requestWrapper as React.FC<{ toolRequest: any }> // ts isnt smart enough... + const ToolRequestComponent = toolNameToComponent[chatMessage.name].requestWrapper as React.FC<{ toolRequest: any }> // ts isnt smart enough... return <> - + } else if (role === 'tool') { const title = toolNameToTitle[chatMessage.name] if (chatMessage.result.type === 'error') return - - const ToolMessageComponent = toolNameToComponent[chatMessage.name].resultWrapper as React.FC<{ toolMessage: any }> // ts isnt smart enough... - return + const ToolResultComponent = toolNameToComponent[chatMessage.name].resultWrapper as React.FC<{ toolMessage: any }> // ts isnt smart enough... + return } @@ -1383,7 +1375,7 @@ export const SidebarChat = () => { const initVal = '' const [instructionsAreEmpty, setInstructionsAreEmpty] = useState(!initVal) - const isDisabled = instructionsAreEmpty || !!isFeatureNameDisabled('Ctrl+L', settingsState) + const isDisabled = instructionsAreEmpty || !!isFeatureNameDisabled('Chat', settingsState) const [sidebarRef, sidebarDimensions] = useResizeObserver() const [chatAreaRef, chatAreaDimensions] = useResizeObserver() @@ -1513,7 +1505,6 @@ export const SidebarChat = () => { selections={selections} setSelections={setSelections} onClickAnywhere={() => { textAreaRef.current?.focus() }} - featureName="Ctrl+L" > { @@ -82,7 +82,8 @@ export type ServiceSendLLMMessageParams = { onFinalMessage: OnFinalMessage; onError: OnError; logging: { loggingName: string, }; - useProviderFor: FeatureName; + modelSelection: ModelSelection | null; + modelSelectionOptions: ModelSelectionOptions | undefined; } & SendLLMType; // params to the true sendLLMMessage function @@ -95,10 +96,10 @@ export type SendLLMMessageParams = { aiInstructions: string; - providerName: ProviderName; - modelName: string; + modelSelection: ModelSelection; + modelSelectionOptions: ModelSelectionOptions | undefined; + settingsOfProvider: SettingsOfProvider; - optionsOfModelSelection: OptionsOfModelSelection; } & SendLLMType diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 13173b50..95de9604 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { OptionsOfModelSelection, ProviderName } from './voidSettingsTypes.js'; +import { ModelSelectionOptions, ProviderName } from './voidSettingsTypes.js'; export const defaultModelsOfProvider = { @@ -633,11 +633,11 @@ export const getProviderCapabilities = (providerName: ProviderName) => { } // state from optionsOfModelSelection -export const getModelSelectionState = (providerName: ProviderName, modelName: string, optionsOfModelSelection: OptionsOfModelSelection): { isReasoningEnabled: boolean, reasoningBudget: number | undefined } => { - const { canToggleReasoning } = getModelCapabilities(providerName, modelName).supportsReasoning || {} +export const getModelSelectionState = (providerName: ProviderName, modelName: string, modelSelectionOptions: ModelSelectionOptions | undefined): { isReasoningEnabled: boolean, reasoningBudget: number | undefined } => { + const { canToggleReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName).supportsReasoning || {} const defaultEnabledVal = canToggleReasoning ? true : false - const isReasoningEnabled = optionsOfModelSelection[providerName]?.[modelName]?.reasoningEnabled ?? defaultEnabledVal - const reasoningBudget = optionsOfModelSelection[providerName]?.[modelName]?.reasoningBudget + const isReasoningEnabled = modelSelectionOptions?.reasoningEnabled ?? defaultEnabledVal + const reasoningBudget = reasoningBudgetSlider?.type === 'slider' ? modelSelectionOptions?.reasoningBudget ?? reasoningBudgetSlider?.default : undefined return { isReasoningEnabled, reasoningBudget } } diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index 0df1ec3b..278d63da 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -14,8 +14,10 @@ import { IMetricsService } from './metricsService.js'; import { getModelCapabilities } from './modelCapabilities.js'; import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, defaultProviderSettings, ModelSelectionOptions, OptionsOfModelSelection } from './voidSettingsTypes.js'; +// past values: +// 'void.settingsServiceStorage' -const STORAGE_KEY = 'void.settingsServiceStorage' +const STORAGE_KEY = 'void.settingsServiceStorageI' // name is the name in the dropdown @@ -97,7 +99,7 @@ const _updatedModelsAfterDefaultModelsChange = (defaultModelNames: string[], opt export const modelFilterOfFeatureName: { [featureName in FeatureName]: { filter: (o: ModelSelection) => boolean; emptyMessage: string | null } } = { 'Autocomplete': { filter: o => getModelCapabilities(o.providerName, o.modelName).supportsFIM, emptyMessage: 'No models support FIM' }, - 'Ctrl+L': { filter: o => true, emptyMessage: null }, + 'Chat': { filter: o => true, emptyMessage: null }, 'Ctrl+K': { filter: o => true, emptyMessage: null }, 'Apply': { filter: o => true, emptyMessage: null }, } @@ -172,7 +174,7 @@ const _validatedState = (state: Omit) => { const defaultState = () => { const d: VoidSettingsState = { settingsOfProvider: deepClone(defaultSettingsOfProvider), - modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null }, + modelSelectionOfFeature: { 'Chat': null, 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null }, globalSettings: deepClone(defaultGlobalSettings), optionsOfModelSelection: {}, _modelOptions: [], // computed later diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index bda7e3ce..5636790a 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -310,7 +310,7 @@ export const modelSelectionsEqual = (m1: ModelSelection, m2: ModelSelection) => } // this is a state -export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete', 'Apply'] as const +export const featureNames = ['Chat', 'Ctrl+K', 'Autocomplete', 'Apply'] as const export type ModelSelectionOfFeature = Record<(typeof featureNames)[number], ModelSelection | null> export type FeatureName = keyof ModelSelectionOfFeature @@ -321,7 +321,7 @@ export const displayInfoOfFeatureName = (featureName: FeatureName) => { else if (featureName === 'Ctrl+K') return 'Quick Edit' // sidebar: - else if (featureName === 'Ctrl+L') + else if (featureName === 'Chat') return 'Chat' else if (featureName === 'Apply') return 'Fast Apply' diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 901ff013..e1047926 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -11,7 +11,7 @@ import { Model as OpenAIModel } from 'openai/resources/models.js'; import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../browser/helpers/extractCodeFromResult.js'; import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/llmMessageTypes.js'; import { InternalToolInfo, isAToolName, ToolName } from '../../browser/toolsService.js'; -import { defaultProviderSettings, displayInfoOfProviderName, OptionsOfModelSelection, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; +import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; import { prepareFIMMessage, prepareMessages } from './preprocessLLMMessages.js'; import { getModelSelectionState, getModelCapabilities, getProviderCapabilities } from '../../common/modelCapabilities.js'; @@ -23,7 +23,7 @@ type InternalCommonMessageParams = { onError: OnError; providerName: ProviderName; settingsOfProvider: SettingsOfProvider; - optionsOfModelSelection: OptionsOfModelSelection; + modelSelectionOptions: ModelSelectionOptions | undefined; modelName: string; _setAborter: (aborter: () => void) => void; } @@ -288,7 +288,7 @@ const toolCallsFrom_Anthropic = (content: Anthropic.Messages.ContentBlock[]): To }).filter(t => !!t) } -const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalMessage, onError, settingsOfProvider, optionsOfModelSelection, modelName: modelName_, _setAborter, aiInstructions, tools: tools_ }: SendChatParams_Internal) => { +const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName: modelName_, _setAborter, aiInstructions, tools: tools_ }: SendChatParams_Internal) => { const { modelName, supportsSystemMessage, @@ -299,7 +299,7 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM const { isReasoningEnabled, reasoningBudget, - } = getModelSelectionState(providerName, modelName_, optionsOfModelSelection) // user's modelName_ here + } = getModelSelectionState(providerName, modelName_, modelSelectionOptions) // user's modelName_ here const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: true }) diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts index 0c51bae2..ae47628d 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts @@ -19,9 +19,8 @@ export const sendLLMMessage = ({ abortRef: abortRef_, logging: { loggingName }, settingsOfProvider, - optionsOfModelSelection, - providerName, - modelName, + modelSelection, + modelSelectionOptions, tools, }: SendLLMMessageParams, @@ -29,6 +28,8 @@ export const sendLLMMessage = ({ ) => { + const { providerName, modelName } = modelSelection + // only captures number of messages and message "shape", no actual code, instructions, prompts, etc const captureLLMEvent = (eventId: string, extras?: object) => { metricsService.capture(eventId, { @@ -105,12 +106,12 @@ export const sendLLMMessage = ({ } const { sendFIM, sendChat } = implementation if (messagesType === 'chatMessages') { - sendChat({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, optionsOfModelSelection, modelName, _setAborter, providerName, aiInstructions, tools }) + sendChat({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName, _setAborter, providerName, aiInstructions, tools }) return } if (messagesType === 'FIMMessage') { if (sendFIM) { - sendFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, optionsOfModelSelection, modelName, _setAborter, providerName, aiInstructions }) + sendFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName, _setAborter, providerName, aiInstructions }) return } onError({ message: `Error: This provider does not support Autocomplete yet.`, fullError: null }) From 4061e6382a19c456a81dbef9438ec6bbf7a89f02 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 22:58:17 -0800 Subject: [PATCH 10/13] rm console.logs --- .../contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx | 2 -- .../void/electron-main/llmMessage/sendLLMMessage.impl.ts | 3 --- 2 files changed, 5 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 1a9da51f..30cefbb3 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 @@ -1292,8 +1292,6 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps) => const role = chatMessage.role - console.log('ROLE', role) - if (role === 'user') { return toAnthropicTool(tool)) : undefined From 93c0bb1d04b9b792934f457d1f82ce56ea6490e1 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 23:03:11 -0800 Subject: [PATCH 11/13] rename --- src/vs/code/electron-main/app.ts | 6 +++--- .../workbench/contrib/void/browser/autocompleteService.ts | 2 +- src/vs/workbench/contrib/void/browser/chatThreadService.ts | 4 ++-- src/vs/workbench/contrib/void/browser/editCodeService.ts | 4 ++-- .../contrib/void/browser/helpers/extractCodeFromResult.ts | 2 +- .../contrib/void/browser/react/src/util/services.tsx | 2 +- src/vs/workbench/contrib/void/browser/void.contribution.ts | 2 +- src/vs/workbench/contrib/void/common/refreshModelService.ts | 4 ++-- .../{llmMessageService.ts => sendLLMMessageService.ts} | 4 ++-- .../common/{llmMessageTypes.ts => sendLLMMessageTypes.ts} | 0 .../void/electron-main/llmMessage/preprocessLLMMessages.ts | 2 +- .../void/electron-main/llmMessage/sendLLMMessage.impl.ts | 2 +- .../contrib/void/electron-main/llmMessage/sendLLMMessage.ts | 2 +- .../{llmMessageChannel.ts => sendLLMMessageChannel.ts} | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) rename src/vs/workbench/contrib/void/common/{llmMessageService.ts => sendLLMMessageService.ts} (99%) rename src/vs/workbench/contrib/void/common/{llmMessageTypes.ts => sendLLMMessageTypes.ts} (100%) rename src/vs/workbench/contrib/void/electron-main/{llmMessageChannel.ts => sendLLMMessageChannel.ts} (99%) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index ce66f600..f234324d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -125,7 +125,7 @@ import { IMetricsService } from '../../workbench/contrib/void/common/metricsServ import { IVoidUpdateService } from '../../workbench/contrib/void/common/voidUpdateService.js'; import { MetricsMainService } from '../../workbench/contrib/void/electron-main/metricsMainService.js'; import { VoidMainUpdateService } from '../../workbench/contrib/void/electron-main/voidUpdateMainService.js'; -import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/llmMessageChannel.js'; +import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/sendLLMMessageChannel.js'; /** * The main VS Code application. There will only ever be one instance, @@ -1256,8 +1256,8 @@ export class CodeApplication extends Disposable { const voidUpdatesChannel = ProxyChannel.fromService(accessor.get(IVoidUpdateService), disposables); mainProcessElectronServer.registerChannel('void-channel-update', voidUpdatesChannel); - const llmMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService)); - mainProcessElectronServer.registerChannel('void-channel-llmMessageService', llmMessageChannel); + const sendLLMMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService)); + mainProcessElectronServer.registerChannel('void-channel-llmMessage', sendLLMMessageChannel); // Extension Host Debug Broadcasting const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService)); diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 25b2536d..12d83d2c 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -17,7 +17,7 @@ import { EditorResourceAccessor } from '../../../common/editor.js'; import { IModelService } from '../../../../editor/common/services/model.js'; import { extractCodeFromRegular } from './helpers/extractCodeFromResult.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; -import { ILLMMessageService } from '../common/llmMessageService.js'; +import { ILLMMessageService } from '../common/sendLLMMessageService.js'; import { isWindows } from '../../../../base/common/platform.js'; import { IVoidSettingsService } from '../common/voidSettingsService.js'; import { FeatureName } from '../common/voidSettingsTypes.js'; diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 87cc24d7..ad130a16 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -11,10 +11,10 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IRange } from '../../../../editor/common/core/range.js'; -import { ILLMMessageService } from '../common/llmMessageService.js'; +import { ILLMMessageService } from '../common/sendLLMMessageService.js'; import { chat_userMessageContent, chat_systemMessage, chat_lastUserMessageWithFilesAdded, chat_selectionsString } from './prompt/prompts.js'; import { InternalToolInfo, IToolsService, ToolCallParams, ToolResultType, ToolName, toolNamesThatRequireApproval, voidTools } from './toolsService.js'; -import { AnthropicReasoning, LLMChatMessage, ToolCallType } from '../common/llmMessageTypes.js'; +import { AnthropicReasoning, LLMChatMessage, ToolCallType } from '../common/sendLLMMessageTypes.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IVoidFileService } from '../common/voidFileService.js'; import { generateUuid } from '../../../../base/common/uuid.js'; diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index 8940ec5f..3d365f66 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -38,8 +38,8 @@ import { EditorOption } from '../../../../editor/common/config/editorOptions.js' import { Emitter } from '../../../../base/common/event.js'; import { VOID_OPEN_SETTINGS_ACTION_ID } from './voidSettingsPane.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { ILLMMessageService } from '../common/llmMessageService.js'; -import { LLMChatMessage, OnError, errorDetails } from '../common/llmMessageTypes.js'; +import { ILLMMessageService } from '../common/sendLLMMessageService.js'; +import { LLMChatMessage, OnError, errorDetails } from '../common/sendLLMMessageTypes.js'; import { IMetricsService } from '../common/metricsService.js'; import { IVoidFileService } from '../common/voidFileService.js'; import { IEditCodeService, URIStreamState, AddCtrlKOpts, StartApplyingOpts } from './editCodeServiceInterface.js'; diff --git a/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts b/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts index ab722209..ee138358 100644 --- a/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts +++ b/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { OnText } from '../../common/llmMessageTypes.js' +import { OnText } from '../../common/sendLLMMessageTypes.js' import { DIVIDER, FINAL, ORIGINAL } from '../prompt/prompts.js' class SurroundingsRemover { diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx index ef656894..447854aa 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx @@ -21,7 +21,7 @@ import { IContextViewService, IContextMenuService } from '../../../../../../../p import { IFileService } from '../../../../../../../platform/files/common/files.js'; import { IHoverService } from '../../../../../../../platform/hover/browser/hover.js'; import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; -import { ILLMMessageService } from '../../../../../../../workbench/contrib/void/common/llmMessageService.js'; +import { ILLMMessageService } from '../../../../common/sendLLMMessageService.js'; import { IRefreshModelService } from '../../../../../../../workbench/contrib/void/common/refreshModelService.js'; import { IVoidSettingsService } from '../../../../../../../workbench/contrib/void/common/voidSettingsService.js'; import { IEditCodeService, URIStreamState } from '../../../editCodeServiceInterface.js' diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index d5e78754..f5570488 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -38,7 +38,7 @@ import './voidUpdateActions.js' // ---------- common (unclear if these actually need to be imported, because they're already imported wherever they're used) ---------- // llmMessage -import '../common/llmMessageService.js' +import '../common/sendLLMMessageService.js' // voidSettings import '../common/voidSettingsService.js' diff --git a/src/vs/workbench/contrib/void/common/refreshModelService.ts b/src/vs/workbench/contrib/void/common/refreshModelService.ts index 1d68b304..8123441d 100644 --- a/src/vs/workbench/contrib/void/common/refreshModelService.ts +++ b/src/vs/workbench/contrib/void/common/refreshModelService.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------*/ import { IVoidSettingsService } from './voidSettingsService.js'; -import { ILLMMessageService } from './llmMessageService.js'; +import { ILLMMessageService } from './sendLLMMessageService.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { RefreshableProviderName, refreshableProviderNames, SettingsOfProvider } from './voidSettingsTypes.js'; -import { OllamaModelResponse, VLLMModelResponse } from './llmMessageTypes.js'; +import { OllamaModelResponse, VLLMModelResponse } from './sendLLMMessageTypes.js'; import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/void/common/llmMessageService.ts b/src/vs/workbench/contrib/void/common/sendLLMMessageService.ts similarity index 99% rename from src/vs/workbench/contrib/void/common/llmMessageService.ts rename to src/vs/workbench/contrib/void/common/sendLLMMessageService.ts index 8cad21f8..1213b256 100644 --- a/src/vs/workbench/contrib/void/common/llmMessageService.ts +++ b/src/vs/workbench/contrib/void/common/sendLLMMessageService.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainSendLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, VLLMModelResponse, } from './llmMessageTypes.js'; +import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainSendLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, VLLMModelResponse, } from './sendLLMMessageTypes.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; @@ -66,7 +66,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService // const service = ProxyChannel.toService(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service // see llmMessageChannel.ts - this.channel = this.mainProcessService.getChannel('void-channel-llmMessageService') + this.channel = this.mainProcessService.getChannel('void-channel-llmMessage') // .listen sets up an IPC channel and takes a few ms, so we set up listeners immediately and add hooks to them instead // llm diff --git a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts b/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts similarity index 100% rename from src/vs/workbench/contrib/void/common/llmMessageTypes.ts rename to src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts index 55f92819..514e3125 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { AnthropicReasoning, LLMChatMessage, LLMFIMMessage } from '../../common/llmMessageTypes.js'; +import { AnthropicReasoning, LLMChatMessage, LLMFIMMessage } from '../../common/sendLLMMessageTypes.js'; import { deepClone } from '../../../../../base/common/objects.js'; diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 49a83cd2..dd75882e 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -9,7 +9,7 @@ import OpenAI, { ClientOptions } from 'openai'; import { Model as OpenAIModel } from 'openai/resources/models.js'; import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../browser/helpers/extractCodeFromResult.js'; -import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/llmMessageTypes.js'; +import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js'; import { InternalToolInfo, isAToolName, ToolName } from '../../browser/toolsService.js'; import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; import { prepareFIMMessage, prepareMessages } from './preprocessLLMMessages.js'; diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts index ae47628d..da65b992 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { SendLLMMessageParams, OnText, OnFinalMessage, OnError } from '../../common/llmMessageTypes.js'; +import { SendLLMMessageParams, OnText, OnFinalMessage, OnError } from '../../common/sendLLMMessageTypes.js'; import { IMetricsService } from '../../common/metricsService.js'; import { displayInfoOfProviderName } from '../../common/voidSettingsTypes.js'; import { sendLLMMessageToProviderImplementation } from './sendLLMMessage.impl.js'; diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessageChannel.ts b/src/vs/workbench/contrib/void/electron-main/sendLLMMessageChannel.ts similarity index 99% rename from src/vs/workbench/contrib/void/electron-main/llmMessageChannel.ts rename to src/vs/workbench/contrib/void/electron-main/sendLLMMessageChannel.ts index c1b5ed13..12b0d984 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessageChannel.ts +++ b/src/vs/workbench/contrib/void/electron-main/sendLLMMessageChannel.ts @@ -8,7 +8,7 @@ import { IServerChannel } from '../../../../base/parts/ipc/common/ipc.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainSendLLMMessageParams, AbortRef, SendLLMMessageParams, MainLLMMessageAbortParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, VLLMModelResponse, MainModelListParams, } from '../common/llmMessageTypes.js'; +import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainSendLLMMessageParams, AbortRef, SendLLMMessageParams, MainLLMMessageAbortParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, VLLMModelResponse, MainModelListParams, } from '../common/sendLLMMessageTypes.js'; import { sendLLMMessage } from './llmMessage/sendLLMMessage.js' import { IMetricsService } from '../common/metricsService.js'; import { sendLLMMessageToProviderImplementation } from './llmMessage/sendLLMMessage.impl.js'; From 75ef9d552825f48147005323aac866918cf22615 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 7 Mar 2025 23:59:58 -0800 Subject: [PATCH 12/13] rm id and add tool_use_id --- .../void/browser/autocompleteService.ts | 2 - .../contrib/void/browser/chatThreadService.ts | 4 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 7 +++- .../contrib/void/browser/toolsService.ts | 3 +- .../llmMessage/preprocessLLMMessages.ts | 39 ++++++++++++------- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 12d83d2c..8d219f1c 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -769,8 +769,6 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ - // console.log('B') - // create a new autocompletion and add it to cache const newAutocompletion: Autocompletion = { id: this._autocompletionId++, diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index ad130a16..afd62eda 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -547,9 +547,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { if (llmCancelToken === null) break this._setStreamState(threadId, { streamingToken: llmCancelToken }) - console.log('awaiting agentloop') + console.log('awaiting agentloop...') await awaitable - console.log('done') + console.log('done with agentloop!') } } 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 30cefbb3..cc9d779e 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 @@ -1231,7 +1231,9 @@ const toolNameToComponent: { [T in ToolName]: { const { params } = toolRequest return } onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} - /> + > + + }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -1392,6 +1394,9 @@ export const SidebarChat = () => { // send message to LLM const userMessage = textAreaRef.current?.value ?? '' + + // getModelCapabilities() // TODO!!! check if can go into agent mode + await chatThreadsService.addUserMessageAndStreamResponse({ userMessage, chatMode: 'agent' }) setSelections([]) // clear staging diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts index a598c254..fe8dc41c 100644 --- a/src/vs/workbench/contrib/void/browser/toolsService.ts +++ b/src/vs/workbench/contrib/void/browser/toolsService.ts @@ -376,6 +376,7 @@ export class ToolsService implements IToolsService { const uri = validateURI(uriStr) const changeDescription = validateStr('changeDescription', changeDescriptionUnknown) + console.log('done validating!!!') return { uri, changeDescription } }, @@ -453,8 +454,6 @@ export class ToolsService implements IToolsService { from: 'ClickApply', type: 'searchReplace', }) ?? [] - console.log('B') - await p return {} }, diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts index 514e3125..6ff7906e 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -166,8 +166,7 @@ type PrepareMessagesToolsOpenAI = ( }[] } | { role: 'tool', - id: string; // old val - tool_call_id: string; // new val + tool_call_id: string; content: string; } )[] @@ -199,9 +198,8 @@ const prepareMessages_tools_openai = ({ messages }: { messages: InternalLLMChatM // add the tool newMessages.push({ role: 'tool', - id: currMsg.id, - content: currMsg.content || EMPTY_TOOL_CONTENT, tool_call_id: currMsg.id, + content: currMsg.content || EMPTY_TOOL_CONTENT, }) } return { messages: newMessages } @@ -344,16 +342,31 @@ const prepareMessages_anthropicContent = ({ messages, supportsAnthropicReasoning const prepareMessages_noEmptyMessage = ({ messages }: { messages: PrepareMessagesTools }): { messages: PrepareMessagesTools } => { for (const currMsg of messages) { - if (currMsg.role === 'assistant' || currMsg.role === 'user') { - if (typeof currMsg.content === 'string') { - currMsg.content = currMsg.content || EMPTY_MESSAGE + // don't do this for tools + if (currMsg.role === 'tool') continue + + // don't do this for assistant or user messages that have tool_calls or tool_results + const oai = currMsg as PrepareMessagesToolsOpenAI[0] + if (oai.role === 'assistant') { + if (oai.tool_calls) continue + } + const anth = currMsg as PrepareMessagesToolsAnthropic[0] + if (anth.role === 'assistant' || anth.role === 'user') { + if (typeof anth.content !== 'string') { + const hasContent = anth.content.find(c => c.type === 'tool_use' || c.type === 'tool_result') + if (hasContent) continue } - else { - for (const c of currMsg.content) { - if (c.type === 'text') c.text = c.text || EMPTY_MESSAGE - else if (c.type === 'tool_use') { } - else if (c.type === 'tool_result') { } - } + } + + + if (typeof currMsg.content === 'string') { + currMsg.content = currMsg.content || EMPTY_MESSAGE + } + else { + for (const c of currMsg.content) { + if (c.type === 'text') c.text = c.text || EMPTY_MESSAGE + else if (c.type === 'tool_use') { } + else if (c.type === 'tool_result') { } } } From 009dfbef37494fc7236d54e57d20be7f409ca19b Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 8 Mar 2025 02:24:39 -0800 Subject: [PATCH 13/13] improve prompt --- .../contrib/void/browser/chatThreadService.ts | 25 +---------- .../contrib/void/browser/editCodeService.ts | 38 ++++++++--------- .../contrib/void/browser/prompt/prompts.ts | 41 ++++++++++--------- .../contrib/void/browser/toolsService.ts | 7 +--- .../llmMessage/sendLLMMessage.ts | 1 + 5 files changed, 41 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index afd62eda..f02f9e2c 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -458,14 +458,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 1. validate tool params let toolParams: ToolCallParams[typeof toolName] try { - console.log('A') - const params = await this._toolsService.validateParams[toolName](tool.paramsStr) - console.log('B') - toolParams = params } catch (error) { - console.log('ERR1') const errorMessage = getErrorMessage(error) this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) res_() @@ -474,25 +469,16 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 2. if tool requires approval, await the approval if (toolNamesThatRequireApproval.has(toolName)) { - console.log('C') - const voidToolId = generateUuid() - console.log('D') const toolApprovalPromise = new Promise((res, rej) => { this.resRejOfToolAwaitingApproval[voidToolId] = { res, rej } }) - console.log('E') this._addMessageToThread(threadId, { role: 'tool_request', name: toolName, params: toolParams, voidToolId: voidToolId }) try { - console.log('F') - await toolApprovalPromise // accepted tool } catch (e) { - console.log('ERR2') - // TODO!!! test rejection // if (Math.random() > 0) throw new Error('TESTING') - const errorMessage = 'Tool call was rejected by the user.' this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) res_() @@ -503,10 +489,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 3. call the tool let toolResult: ToolResultType[typeof toolName] try { - console.log('G') toolResult = await this._toolsService.callTool[toolName](toolParams as any) // typescript is so bad it doesn't even couple the type of ToolResult with the type of the function being called here } catch (error) { - console.log('ERR3') const errorMessage = getErrorMessage(error) this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) res_() @@ -516,10 +500,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 4. stringify the result to give the LLM let toolResultStr: string try { - - console.log('H') toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) - } catch (error) { const errorMessage = `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}` this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) @@ -527,8 +508,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { return } - console.log('I') - // 5. add to history this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: toolResultStr, result: { type: 'success', params: toolParams, value: toolResult }, }) res_() @@ -547,13 +526,11 @@ class ChatThreadService extends Disposable implements IChatThreadService { if (llmCancelToken === null) break this._setStreamState(threadId, { streamingToken: llmCancelToken }) - console.log('awaiting agentloop...') await awaitable - console.log('done with agentloop!') } } - agentLoop() // DO NOT AWAIT THIS, add fn should resolve when we've added message (this lets us interrupt the agent loop correctly instead of waiting for it to resolve) + agentLoop() } diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index 3d365f66..9ef06ede 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -1191,20 +1191,13 @@ class EditCodeService extends Disposable implements IEditCodeService { // throws if there's an error public startApplying(opts: StartApplyingOpts): [URI, Promise] | null { - if (opts.type === 'rewrite') { - const added = this._initializeWriteoverStream(opts) - if (!added) return null - const [diffZone, promise] = added - return [diffZone._URI, promise] - } - else if (opts.type === 'searchReplace') { - const added = this._initializeSearchAndReplaceStream(opts) - if (!added) return null - if (!added) return null - const [diffZone, promise] = added - return [diffZone._URI, promise] - } - return null + let res: [DiffZone, Promise] | undefined = undefined + if (opts.type === 'rewrite') res = this._initializeWriteoverStream(opts) + else if (opts.type === 'searchReplace') res = this._initializeSearchAndReplaceStream(opts) + + if (!res) return null + const [diffZone, applyDonePromise] = res + return [diffZone._URI, applyDonePromise] } @@ -1479,13 +1472,13 @@ class EditCodeService extends Disposable implements IEditCodeService { // promise that resolves when the apply is done - let resApplyPromise: () => void - let rejApplyPromise: (e: any) => void - const applyPromise = new Promise((res_, rej_) => { resApplyPromise = res_; rejApplyPromise = rej_ }) + let resApplyDonePromise: () => void + let rejApplyDonePromise: (e: any) => void + const applyDonePromise = new Promise((res_, rej_) => { resApplyDonePromise = res_; rejApplyDonePromise = rej_ }) // add to history const { onFinishEdit } = this._addToHistory(uri, { - onUndo: () => { if (diffZone._streamState.isStreaming) rejApplyPromise(new Error('Edit was interrupted by pressing undo.')) } + onUndo: () => { if (diffZone._streamState.isStreaming) rejApplyDonePromise(new Error('Edit was interrupted by pressing undo.')) } }) // TODO replace these with whatever block we're on initially if already started (caching apply) @@ -1583,7 +1576,7 @@ class EditCodeService extends Disposable implements IEditCodeService { nMessagesSent += 1 let resMessageDonePromise: () => void = () => { } - const messageDonePromise = new Promise((res_) => { resMessageDonePromise = res_ }) + const messageDonePromise = new Promise((res, rej) => { resMessageDonePromise = res }) streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', @@ -1632,7 +1625,6 @@ class EditCodeService extends Disposable implements IEditCodeService { { role: 'user', content: content } // user explanation of what's wrong ) - if (streamRequestIdRef.current) this._llmMessageService.abort(streamRequestIdRef.current) // REVERT const numLines = this._getNumLines(uri) @@ -1653,7 +1645,9 @@ class EditCodeService extends Disposable implements IEditCodeService { oldBlocks = [] addedTrackingZoneOfBlockNum.splice(0, Infinity) // clear the array + // abort and resolve shouldSendAnotherMessage = true + if (streamRequestIdRef.current) this._llmMessageService.abort(streamRequestIdRef.current) this._refreshStylesAndDiffsInURI(uri) resMessageDonePromise() return @@ -1765,9 +1759,9 @@ class EditCodeService extends Disposable implements IEditCodeService { } // end retryLoop - retryLoop().then(() => resApplyPromise()).catch((e) => rejApplyPromise(e)) + retryLoop().then(() => resApplyDonePromise()).catch((e) => rejApplyDonePromise(e)) - return [diffZone, applyPromise] + return [diffZone, applyDonePromise] } diff --git a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts index 5e32d0a3..3199364f 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts @@ -17,41 +17,46 @@ export const tripleTick = ['```', '```'] export const editToolDesc_toolDescription = `\ A high level description of the change you'd like to make in the file. This description will be handed to a dumber, faster model that will quickly apply the change. \ -Typically the best description you can give here is a high level view of the final code you'd like to see. For example, code excerpt(s) with "// ... existing code ..." comments to help you write less. \ +Typically the best description you can give here is a high level view of the final code you'd like to see. For example, you can write code excerpt(s) with "// ... existing code ..." comments to help you write less. \ However, you are allowed to describe the change using whatever text/language you like, especially if the change is better described without code. \ Do NOT output the whole file if possible, and try to write as LITTLE as needed to describe the change.` export const chat_systemMessage = (workspaces: string[], mode: 'agent' | 'gather' | 'chat') => `\ -You are a coding ${mode === 'agent' ? 'agent' : 'assistant'}. Your job is to help the user understand/${mode === 'agent' ? 'make' : 'suggest'} changes to their codebase. -You will be given instructions to follow from the user, \`INSTRUCTIONS\`. You may also be given a list of selections that the user has specifically selected, \`SELECTIONS\`. +You are a coding ${mode === 'agent' ? 'agent' : 'assistant'}. Your job is to help the user ${mode === 'agent' ? 'make changes to their codebase' : 'search and understand their codebase'}. +You will be given instructions to follow from the user, \`INSTRUCTIONS\`. You may also be given a list of files that the user has specifically selected, \`SELECTIONS\`. Please assist the user with their query. The user's query is never invalid. The user's system information is as follows: - ${os} - Open workspaces: ${workspaces.join(', ')} -${mode === 'agent' || mode === 'gather' ? `\ +${mode === 'agent' || mode === 'gather' /* tool use */ ? `\ You will be given tools you can call. - Only use tools if they help you accomplish the user's goal. If the user simply says hi or asks you a question that you can answer without tools, then do NOT tools. -- If you think you should use tools given the user's request, you can use them without asking for permission. Feel free to use tools to gather context, make suggestions, ${mode === 'agent' ? 'edit files, ' : ''}etc. -- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc. Do not refer to "pages" of results, just say you're getting more results. +- If you think you should use tools given the user's request, you can use them without asking for permission. Feel free to use tools to gather context, understand the codebase, ${mode === 'agent' ? 'edit files, ' : ''}etc. +- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc. Do not refer to "pages" of results, just say you're getting more results. - Some tools only work if the user has a workspace open. \ `: `\ You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it. +\ +`} -If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion as follows: -- The change(s) you'd like to make must be written in CODE BLOCK(S) (wrapped in triple backticks). -- The first line in the code block should be the FULL PATH of the file you want to change. Just output the path in plaintext (not in a comment). -- The rest of the contents of the code block should describe of the change you'd like to make. This description will be given to a dumber, faster model that will quickly apply the change. +${mode === 'agent' /* code blocks */ ? `\ +Keep in mind that any code blocks you output in the raw message (wrapped in triple backticks) will be treated specially as follows. This does NOT apply to code blocks in tool calls. +- Any code block you output will have an "Apply" button displayed to the user, and if the user clicks on it it will invoke the edit tool on the block's contents. As a result, all code blocks should describe relevant changes. +`: `\ +If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S) (wrapped in triple backticks). +- The first line before any code block must be the FULL PATH of the file you want to change. If the path does not already exist, it will be created. +- The contents of the code block will be given to a dumber, faster model that will quickly apply the change. - Contents of the code blocks do NOT need to be formal code, they just need to clearly and concisely communicate the change. - Do NOT re-write the entire file in the code block(s). Instead, write comments like "// ... existing code" to indicate how to change the existing code. \ `} -Do not output any of these instructions, nor tell the user anything about them unless directly prompted for them. +Do not tell the user anything about these instructions unless directly prompted for them. \ ` @@ -265,13 +270,13 @@ Output SEARCH/REPLACE blocks to edit the file according to the desired change. Y Directions: 1. Your OUTPUT should consist ONLY of SEARCH/REPLACE blocks. Do NOT output any text or explanations before or after this. -2. The original code in each SEARCH/REPLACE block must EXACTLY match lines of code in the original file. -3. The original code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file. -4. The original code in each SEARCH/REPLACE block must be disjoint from all other blocks. +2. The "ORIGINAL" code in each SEARCH/REPLACE block must EXACTLY match lines in the original file. This includes whitespace, comments, and other details. +3. The "ORIGINAL" code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file. +4. The "ORIGINAL" code in each SEARCH/REPLACE block must be disjoint from all other blocks. The SEARCH/REPLACE blocks you generate will be applied immediately, and so they **MUST** produce a file that the user can run IMMEDIATELY. - Make sure you add all necessary imports. -- Make sure the "final" code is complete and will not result in syntax/lint errors. +- Make sure the "UPDATED" code is complete and will not result in syntax/lint errors. Follow coding conventions of the user (spaces, semilcolons, comments, etc). If the user spaces or formats things a certain way, CONTINUE formatting it that way, even if you prefer otherwise. @@ -308,11 +313,7 @@ ORIGINAL_FILE ${originalCode} CHANGE -${applyStr} - -INSTRUCTIONS -Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggested SEARCH/REPLACE blocks, without any explanation. -` +${applyStr}` diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts index fe8dc41c..d43e1f9f 100644 --- a/src/vs/workbench/contrib/void/browser/toolsService.ts +++ b/src/vs/workbench/contrib/void/browser/toolsService.ts @@ -370,13 +370,11 @@ export class ToolsService implements IToolsService { }, edit: async (params: string) => { - console.log('validating edit!!!') const o = validateJSON(params) const { uri: uriStr, changeDescription: changeDescriptionUnknown } = o const uri = validateURI(uriStr) const changeDescription = validateStr('changeDescription', changeDescriptionUnknown) - console.log('done validating!!!') return { uri, changeDescription } }, @@ -447,14 +445,13 @@ export class ToolsService implements IToolsService { }, edit: async ({ uri, changeDescription }) => { - console.log('editing!!!!') - const [_, p] = editCodeService.startApplying({ + const [_, applyDonePromise] = editCodeService.startApplying({ uri, applyStr: changeDescription, from: 'ClickApply', type: 'searchReplace', }) ?? [] - await p + await applyDonePromise return {} }, terminal_command: async ({ command }) => { diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts index da65b992..1e17d97b 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts @@ -118,6 +118,7 @@ export const sendLLMMessage = ({ return } onError({ message: `Error: Message type "${messagesType}" not recognized.`, fullError: null }) + return } catch (error) {