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,