diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index ea435ed4..dab3e62e 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -16,6 +16,7 @@ import { IModelService } from '../../../../editor/common/services/model.js'; import { chat_userMessage, chat_systemMessage } from './prompt/prompts.js'; import { InternalToolInfo, IToolsService, ToolFns, ToolName, voidTools } from '../common/toolsService.js'; import { toLLMChatMessage } from '../common/llmMessageTypes.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; // one of the square items that indicates a selection in a chat bubble (NOT a file, a Selection of text) export type CodeSelection = { @@ -161,6 +162,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { @IModelService private readonly _modelService: IModelService, @ILLMMessageService private readonly _llmMessageService: ILLMMessageService, @IToolsService private readonly _toolsService: IToolsService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, ) { super() @@ -312,7 +314,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { useProviderFor: 'Ctrl+L', logging: { loggingName: `Agent` }, messages: [ - { role: 'system', content: chat_systemMessage }, + { role: 'system', content: chat_systemMessage(this._workspaceContextService.getWorkspace().folders.map(f => f.uri.fsPath)) }, ...this.getCurrentThread().messages.map(m => (toLLMChatMessage(m))), ], diff --git a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts index 949e1cba..88e9144d 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts @@ -15,13 +15,14 @@ import { os } from '../helpers/systemInfo.js'; // this is just for ease of readability export const tripleTick = ['```', '```'] -export const chat_systemMessage = `\ +export const chat_systemMessage = (workspaces: string[]) => `\ You are a coding assistant. You are given a list of instructions to follow \`INSTRUCTIONS\`, and optionally a list of relevant files \`FILES\`, and selections inside of files \`SELECTIONS\`. Please respond to the user's query. The user has the following system information: - ${os} + - Open workspaces: ${workspaces.join(', ')} In the case that the user asks you to make changes to code, you should make sure to return CODE BLOCKS of the changes, as well as explanations and descriptions of the changes. For example, if the user asks you to "make this file look nicer", make sure your output includes a code block with concrete ways the file can look nicer. diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index ded3eaff..f8184db1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -97,7 +97,7 @@ export const CodeSpan = ({ children, className }: { children: React.ReactNode, c } -const RenderToken = ({ token, nested = false, noSpace = false, chatMessageLocation: chatLocation, tokenIdx }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatMessageLocation?: ChatMessageLocation, tokenIdx: string }): JSX.Element => { +const RenderToken = ({ token, nested = false, noSpace = false, chatMessageLocation, tokenIdx }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatMessageLocation?: ChatMessageLocation, tokenIdx: string }): JSX.Element => { // deal with built-in tokens first (assume marked token) @@ -111,16 +111,17 @@ const RenderToken = ({ token, nested = false, noSpace = false, chatMessageLocati if (t.type === "code") { const isCodeblockClosed = t.raw?.startsWith('```') && t.raw?.endsWith('```'); - const applyBoxId = getApplyBoxId({ - threadId: chatLocation!.threadId, - messageIdx: chatLocation!.messageIdx, + // this should never be + const applyBoxId = chatMessageLocation ? getApplyBoxId({ + threadId: chatMessageLocation.threadId, + messageIdx: chatMessageLocation.messageIdx, tokenIdx: tokenIdx, - }) + }) : null return } + buttonsOnHover={applyBoxId && } /> } @@ -195,7 +196,7 @@ const RenderToken = ({ token, nested = false, noSpace = false, chatMessageLocati )} - + ))} diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index eac87692..8fd4aa79 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -175,6 +175,9 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // A HACK BECAUSE WE ADDED MISTRAL (did not exist before, comes before readS) ...{ mistral: defaultSettingsOfProvider.mistral }, + // A HACK BECAUSE WE ADDED XAI (did not exist before, comes before readS) + ...{ mistral: defaultSettingsOfProvider.xAI }, + ...readS.settingsOfProvider, // A HACK BECAUSE WE ADDED NEW GEMINI MODELS (existed before, comes after readS) diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 0bbcfcde..abef16d8 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -11,17 +11,15 @@ import { VoidSettingsState } from './voidSettingsService.js' // developer info used in sendLLMMessage export type DeveloperInfoAtModel = { // USED: - - // TODO!!! think tokens - deepseek - - // TODO!!!! - // UNUSED (coming soon): - recognizedModelName: RecognizedModelName, // used to show user if model was auto-recognized + supportsSystemMessage: 'developer' | boolean, // if null, we will just do a string of system message. this is independent from separateSystemMessage, which takes priority and is passed directly in each provider's implementation. supportsTools: boolean, // we will just do a string of tool use if it doesn't support - supportsSystemMessageRole: 'developer' | 'system' | false, // if null, we will just do a string of system message. this is independent from separateSystemMessage, which takes priority and is passed directly in each provider's implementation. - supportsAutocompleteFIM: boolean, // we will just do a description of FIM if it doens't support <|fim_hole|> - supportsStreaming: boolean, // (o1 does NOT) we will just dump the final result if doesn't support it - maxTokens: number, // required + + // UNUSED (coming soon): + // TODO!!! think tokens - deepseek + _recognizedModelName: RecognizedModelName, // used to show user if model was auto-recognized + _supportsStreaming: boolean, // we will just dump the final result if doesn't support it + _supportsAutocompleteFIM: boolean, // we will just do a description of FIM if it doens't support <|fim_hole|> + _maxTokens: number, // required } export type DeveloperInfoAtProvider = { @@ -49,6 +47,7 @@ export const recognizedModels = [ 'Anthropic Claude', 'Llama 3.x', 'Deepseek Chat', // deepseek coder v2 is now merged into chat (V3) https://api-docs.deepseek.com/updates#deepseek-coder--deepseek-chat-upgraded-to-deepseek-v25-model + 'xAI Grok', // 'xAI Grok', // 'Google Gemini, Gemma', // 'Microsoft Phi4', @@ -59,7 +58,7 @@ export const recognizedModels = [ 'Mistral Codestral', // thinking - 'OpenAI o1, o3', + 'OpenAI o1', 'Deepseek R1', // general @@ -85,11 +84,13 @@ export function recognizedModelOfModelName(modelName: string): RecognizedModelNa if (lower.includes('mistral')) return 'Mistral Codestral'; if (/\bo1\b/.test(lower) || /\bo3\b/.test(lower)) // o1, o3 - return 'OpenAI o1, o3'; + return 'OpenAI o1'; if (lower.includes('deepseek-r1') || lower.includes('deepseek-reasoner')) return 'Deepseek R1'; if (lower.includes('deepseek')) return 'Deepseek Chat' + if (lower.includes('grok')) + return 'xAI Grok' return ''; } @@ -98,18 +99,14 @@ export function recognizedModelOfModelName(modelName: string): RecognizedModelNa const developerInfoAtProvider: { [providerName in ProviderName]: DeveloperInfoAtProvider } = { 'anthropic': { overrideSettingsForAllModels: { - supportsSystemMessageRole: 'system', + supportsSystemMessage: true, supportsTools: true, - supportsAutocompleteFIM: false, - supportsStreaming: true, + _supportsAutocompleteFIM: false, + _supportsStreaming: true, } }, 'deepseek': { overrideSettingsForAllModels: { - supportsSystemMessageRole: false, - supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: true, } }, 'ollama': { @@ -126,6 +123,8 @@ const developerInfoAtProvider: { [providerName in ProviderName]: DeveloperInfoAt }, 'groq': { }, + 'xAI': { + }, } export const developerInfoOfProviderName = (providerName: ProviderName): Partial => { return developerInfoAtProvider[providerName] ?? {} @@ -135,83 +134,93 @@ export const developerInfoOfProviderName = (providerName: ProviderName): Partial // providerName is optional, but gives some extra fallbacks if provided -const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelName]: Omit } = { +const developerInfoOfRecognizedModelName: { [recognizedModel in RecognizedModelName]: Omit } = { 'OpenAI 4o': { - supportsSystemMessageRole: 'system', + supportsSystemMessage: true, supportsTools: true, - supportsAutocompleteFIM: false, - supportsStreaming: true, - maxTokens: 4096, + _supportsAutocompleteFIM: false, + _supportsStreaming: true, + _maxTokens: 4096, }, 'Anthropic Claude': { - supportsSystemMessageRole: 'system', + supportsSystemMessage: true, supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, }, 'Llama 3.x': { - supportsSystemMessageRole: false, - supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + supportsSystemMessage: true, + supportsTools: true, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, + }, + + 'xAI Grok': { + supportsSystemMessage: true, + supportsTools: true, + _supportsAutocompleteFIM: false, + _supportsStreaming: true, + _maxTokens: 4096, + }, 'Deepseek Chat': { - supportsSystemMessageRole: false, + supportsSystemMessage: true, supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, }, 'Alibaba Qwen2.5 Coder Instruct': { - supportsSystemMessageRole: false, - supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + supportsSystemMessage: true, + supportsTools: true, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, }, 'Mistral Codestral': { - supportsSystemMessageRole: false, - supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + supportsSystemMessage: true, + supportsTools: true, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, }, - 'OpenAI o1, o3': { - supportsSystemMessageRole: false, + 'OpenAI o1': { + supportsSystemMessage: 'developer', supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + _supportsAutocompleteFIM: false, + _supportsStreaming: true, + _maxTokens: 4096, }, 'Deepseek R1': { - supportsSystemMessageRole: false, + supportsSystemMessage: false, supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, }, + '': { - supportsSystemMessageRole: false, + supportsSystemMessage: false, supportsTools: false, - supportsAutocompleteFIM: false, - supportsStreaming: false, - maxTokens: 4096, + _supportsAutocompleteFIM: false, + _supportsStreaming: false, + _maxTokens: 4096, }, } export const developerInfoOfModelName = (modelName: string, overrides?: Partial): DeveloperInfoAtModel => { const recognizedModelName = recognizedModelOfModelName(modelName) return { - recognizedModelName: recognizedModelName, + _recognizedModelName: recognizedModelName, ...developerInfoOfRecognizedModelName[recognizedModelName], ...overrides } @@ -323,6 +332,10 @@ export const defaultMistralModels = modelInfoOfDefaultModelNames([ "mistral-small-latest", ]) +export const defaultXAIModels = modelInfoOfDefaultModelNames([ + 'grok-2-latest', + 'grok-3-latest', +]) // export const parseMaxTokensStr = (maxTokensStr: string) => { // // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN // const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr) @@ -378,6 +391,9 @@ export const defaultProviderSettings = { }, mistral: { apiKey: '' + }, + xAI: { + apiKey: '' } } as const @@ -446,7 +462,6 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn else if (providerName === 'ollama') { return { title: 'Ollama', - } } else if (providerName === 'openAICompatible') { @@ -469,6 +484,12 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn title: 'Mistral API', } } + else if (providerName === 'xAI') { + return { + title: 'xAI API', + } + } + throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`) } @@ -493,7 +514,8 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'groq' ? 'gsk_key...' : providerName === 'mistral' ? 'key...' : providerName === 'openAICompatible' ? 'sk-key...' : - '', + providerName === 'xAI' ? 'xai-key...' : + '', subTextMd: providerName === 'anthropic' ? 'Get your [API Key here](https://console.anthropic.com/settings/keys).' : providerName === 'openAI' ? 'Get your [API Key here](https://platform.openai.com/api-keys).' : @@ -502,8 +524,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'gemini' ? 'Get your [API Key here](https://aistudio.google.com/apikey).' : providerName === 'groq' ? 'Get your [API Key here](https://console.groq.com/keys).' : providerName === 'mistral' ? 'Get your [API Key here](https://console.mistral.ai/api-keys/).' : - providerName === 'openAICompatible' ? undefined : - '', + providerName === 'xAI' ? 'Get your [API Key here](https://console.x.ai).' : + providerName === 'openAICompatible' ? undefined : + '', } } else if (settingName === 'endpoint') { @@ -574,6 +597,9 @@ export const voidInitModelOptions = { }, mistral: { models: defaultMistralModels, + }, + xAI: { + models: defaultXAIModels, } } @@ -610,6 +636,12 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...voidInitModelOptions.mistral, _didFillInProviderSettings: undefined, }, + xAI: { + ...defaultCustomSettings, + ...defaultProviderSettings.xAI, + ...voidInitModelOptions.xAI, + _didFillInProviderSettings: undefined, + }, groq: { // aggregator ...defaultCustomSettings, ...defaultProviderSettings.groq, diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/openai.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/openai.ts index 49dd0bfd..4b9ab724 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/openai.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/openai.ts @@ -91,9 +91,15 @@ const newOpenAI = ({ settingsOfProvider, providerName }: NewParams) => { baseURL: 'https://api.groq.com/openai/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true, }) } + else if (providerName === 'xAI') { + const thisConfig = settingsOfProvider[providerName] + return new OpenAI({ + baseURL: 'https://api.x.ai/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true, + }) + } else { console.error(`sendOpenAICompatibleMsg: invalid providerName: ${providerName}`) - throw new Error(`providerName was invalid: ${providerName}`) + throw new Error(`Void providerName was invalid: ${providerName}`) } } 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 8bde8459..eba90468 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -21,7 +21,7 @@ export const addSystemMessageAndToolSupport = (modelName: string, providerName: const messages = deepClone(messages_).map(m => ({ ...m, content: m.content.trim(), })) const { overrideSettingsForAllModels } = developerInfoOfProviderName(providerName) - const { supportsSystemMessageRole: supportsSystemMessage, supportsTools } = developerInfoOfModelName(modelName, overrideSettingsForAllModels) + const { supportsSystemMessage, supportsTools } = developerInfoOfModelName(modelName, overrideSettingsForAllModels) // 1. SYSTEM MESSAGE // find system messages and concatenate them @@ -52,7 +52,7 @@ export const addSystemMessageAndToolSupport = (modelName: string, providerName: if (separateSystemMessage) separateSystemMessageStr = systemMessageStr else { - newMessages.unshift({ role: supportsSystemMessage, content: systemMessageStr }) // add new first message + newMessages.unshift({ role: supportsSystemMessage === 'developer' ? 'developer' : 'system', content: systemMessageStr }) // add new first message } } // if does not support system message 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 980cf5b9..7d83cff2 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts @@ -103,6 +103,7 @@ export const sendLLMMessage = ({ case 'ollama': case 'groq': case 'gemini': + case 'xAI': if (messagesType === 'FIMMessage') onFinalMessage({ fullText: 'TODO - OpenAI FIM', toolCalls: [] }) else /* */ sendOpenAIChat({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName, aiInstructions, tools }); break;