diff --git a/package-lock.json b/package-lock.json index a365be59..7057acfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "cross-spawn": "^7.0.6", "diff": "^7.0.0", "eslint-plugin-react": "^7.37.5", + "google-auth-library": "^9.15.1", "groq-sdk": "^0.20.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", @@ -6023,6 +6024,15 @@ "node": "*" } }, + "node_modules/bignumber.js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -6253,6 +6263,12 @@ "node": ">=0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -8100,6 +8116,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/editorconfig": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz", @@ -9340,8 +9365,7 @@ "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "node_modules/extend-shallow": { "version": "3.0.2", @@ -10308,6 +10332,68 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -10944,6 +11030,32 @@ "node": ">= 0.10" } }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -11016,6 +11128,19 @@ "undici-types": "~5.26.4" } }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -13975,6 +14100,15 @@ "node": ">=6" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -14095,6 +14229,27 @@ "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kerberos": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-2.1.1.tgz", diff --git a/package.json b/package.json index 462cf549..767d6b49 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ "cross-spawn": "^7.0.6", "diff": "^7.0.0", "eslint-plugin-react": "^7.37.5", + "google-auth-library": "^9.15.1", "groq-sdk": "^0.20.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index ae443a56..7c9eb899 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -682,8 +682,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { let shouldRetryLLM = true let nAttempts = 0 while (shouldRetryLLM) { - shouldRetryLLM = false + nAttempts += 1 let resMessageIsDonePromise: (res: { type: 'llmDone', toolCall?: RawToolCallObj } | { type: 'llmError', error?: { message: string; fullError: Error | null; } } | { type: 'llmAborted' }) => void // resolves when user approves this tool use (or if tool doesn't require approval) const messageIsDonePromise = new Promise<{ type: 'llmDone', toolCall?: RawToolCallObj } | { type: 'llmError', error?: { message: string; fullError: Error | null; } } | { type: 'llmAborted' }>((res, rej) => { resMessageIsDonePromise = res }) @@ -737,7 +737,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { else if (llmRes.type === 'llmError') { // error, should retry if (nAttempts < CHAT_RETRIES) { - nAttempts += 1 shouldRetryLLM = true this._setStreamState(threadId, { isRunning: 'idle', interrupt: idleInterruptor }) await timeout(RETRY_DELAY) 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 e35193a9..568c5aac 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 @@ -722,7 +722,7 @@ const ToolHeaderWrapper = ({ return (
{/* header */} -
+
{/* left */}
{ +// Import the getModelCapabilities function to access default values +import { defaultModelOptions, getModelCapabilities, ModelOverrideOptions } from '../../../../common/modelCapabilities.js'; +// Modal dialog to show model settings +const ModelSettingsDialog = ({ + isOpen, + onClose, + modelInfo +}: { + isOpen: boolean, + onClose: () => void, + modelInfo: { modelName: string, providerName: ProviderName, type: string } | null +}) => { + if (!isOpen || !modelInfo) return null; + + const { modelName, providerName } = modelInfo; + const accessor = useAccessor(); + const settingsStateService = accessor.get('IVoidSettingsService'); + const settingsState = useSettingsState(); + + // Get current model capabilities and override settings + const modelCapabilities = getModelCapabilities(providerName, modelName, settingsState.overridesOfModel); + + // Initialize form state for all potential override options + const [formValues, setFormValues] = useState<{ + contextWindow: string; + maxOutputTokens: string; + supportsTools: 'openai-style' | undefined | ''; + supportsSystemMessage: 'system-role' | 'developer-role' | false | ''; + supportsFIM: boolean | null; + reasoningCapabilities: boolean | null; + }>({ + contextWindow: '', + maxOutputTokens: '', + supportsTools: '', + supportsSystemMessage: '', + supportsFIM: null, + reasoningCapabilities: null + }); + + // When dialog opens or model changes, reset form values + useEffect(() => { + if (isOpen && modelInfo) { + + // Get current overrides + const overrides = settingsState.overridesOfModel?.[providerName]?.[modelName] || {}; + + setFormValues({ + contextWindow: (overrides.contextWindow !== undefined) ? overrides.contextWindow?.toString() : '', + maxOutputTokens: (overrides.maxOutputTokens !== undefined) ? overrides.maxOutputTokens?.toString() : '', + supportsTools: overrides.supportsTools !== undefined ? overrides.supportsTools : '', + supportsSystemMessage: overrides.supportsSystemMessage !== undefined ? overrides.supportsSystemMessage : '', + supportsFIM: overrides.supportsFIM !== undefined ? overrides.supportsFIM : null, + reasoningCapabilities: overrides.reasoningCapabilities !== undefined ? + !!overrides.reasoningCapabilities : null + }); + } + }, [isOpen, modelInfo, settingsState.overridesOfModel, providerName, modelName]); + + // Update a single field in the form + const updateField = (field: string, value: any) => { + setFormValues(prev => ({ + ...prev, + [field]: value + })); + }; + + // Handle saving settings + const handleSave = async () => { + const settings: ModelOverrideOptions = {}; + + // Only add fields to the override if they have been changed from defaults + if (formValues.contextWindow) { + const tokens = parseInt(formValues.contextWindow); + if (!isNaN(tokens)) settings.contextWindow = tokens; + } + + if (formValues.maxOutputTokens) { + const tokens = parseInt(formValues.maxOutputTokens); + if (!isNaN(tokens)) settings.maxOutputTokens = tokens; + } + + if (formValues.supportsTools !== '') { + settings.supportsTools = formValues.supportsTools as any; + } + + if (formValues.supportsSystemMessage !== '') { + settings.supportsSystemMessage = formValues.supportsSystemMessage as any; + } + + if (formValues.supportsFIM !== null) { + settings.supportsFIM = formValues.supportsFIM; + } + + if (formValues.reasoningCapabilities !== null) { + if (formValues.reasoningCapabilities) { + settings.reasoningCapabilities = { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true + }; + } else { + settings.reasoningCapabilities = false; + } + } + + await settingsStateService.setOverridesOfModel(providerName, modelName, settings); + onClose(); + }; + + return ( +
+
e.stopPropagation()}> +
+

Override defaults for {modelName} ({displayInfoOfProviderName(providerName).title})

+ +
+ +
+ {/* Model-specific settings */} +
+ + {/* Context window */} +
+ Context window (tokens) + updateField('contextWindow', value)} + placeholder={(modelCapabilities.contextWindow || defaultModelOptions.contextWindow) + ''} + compact={true} + className="max-w-24" + /> +
+ + {/* Maximum output tokens */} +
+ Maximum output tokens + updateField('maxOutputTokens', value)} + placeholder={(modelCapabilities.maxOutputTokens || defaultModelOptions.maxOutputTokens) + ''} + compact={true} + className="max-w-24" + /> +
+ + {/* Supports Tools */} +
+ Supports tools + updateField('supportsTools', value)} + getOptionDisplayName={(opt) => { + if (opt === '') return `Default (${modelCapabilities.specialToolFormat || 'No'})`; + return opt; + }} + getOptionDropdownName={(opt) => { + if (opt === '') return `Default`; + return opt; + }} + getOptionsEqual={(a, b) => a === b} + className="max-w-32 text-xs" + /> +
+ + {/* Supports System Message */} +
+ Supports system message + updateField('supportsSystemMessage', value)} + getOptionDisplayName={(opt) => { + if (opt === '') return `Default (${modelCapabilities.supportsSystemMessage || 'No'})`; + if (opt === false) return 'No' + if (opt === true) return 'Yes' // should never happen + return opt; + }} + getOptionDropdownName={(opt) => { + if (opt === '') return `Default`; + if (opt === false) return 'No' + if (opt === true) return 'Yes' // should never happen + return opt; + }} + getOptionsEqual={(a, b) => a === b} + className="max-w-32 text-xs" + /> +
+ + {/* Supports FIM */} +
+ Supports fill-in-the-middle (autocomplete) + updateField('supportsFIM', value)} + getOptionDisplayName={(opt) => { + if (opt === null) return `Default (${modelCapabilities.supportsFIM ? 'Yes' : 'No'})`; + return opt ? 'Yes' : 'No'; + }} + getOptionDropdownName={(opt) => { + if (opt === null) return 'Default'; + return opt ? 'Yes' : 'No'; + }} + getOptionsEqual={(a, b) => a === b} + className="max-w-32 text-xs" + /> +
+ + {/* Supports Reasoning */} +
+ Supports reasoning + updateField('reasoningCapabilities', value)} + getOptionDisplayName={(opt) => { + if (opt === null) return `Default (${modelCapabilities.reasoningCapabilities ? 'Yes' : 'No'})`; + return opt ? 'Yes' : 'No'; + }} + getOptionDropdownName={(opt) => { + if (opt === null) return 'Default'; + return opt ? 'Yes' : 'No'; + }} + getOptionsEqual={(a, b) => a === b} + className="max-w-32 text-xs" + /> +
+
+
+ +
+ + Cancel + + + Save + +
+
+
+ ); +}; + +export const ModelDump = () => { const accessor = useAccessor() const settingsStateService = accessor.get('IVoidSettingsService') - const settingsState = useSettingsState() + // State to track which model's settings dialog is open + const [openSettingsModel, setOpenSettingsModel] = useState<{ + modelName: string, + providerName: ProviderName, + type: string + } | null>(null); + // a dump of all the enabled providers' models const modelDump: (VoidStatefulModelInfo & { providerName: ProviderName, providerEnabled: boolean })[] = [] for (let providerName of providerNames) { @@ -342,8 +594,10 @@ export const ModelDump = () => { const detailAboutModel = type === 'autodetected' ? - : type === 'default' ? undefined - : + : type === 'custom' ? + + : undefined + return
{ {/* {type === 'autodetected' ? '(detected locally)' : type === 'default' ? '' : '(custom model)'} */} + {/* Settings button - only for custom or locally detected models */} + {(type === 'autodetected' || type === 'custom') && ( +
+ +
+ )} + + { settingsStateService.toggleModelHidden(providerName, modelName) }} @@ -384,6 +659,13 @@ export const ModelDump = () => {
})} + + {/* Model Settings Dialog */} + setOpenSettingsModel(null)} + modelInfo={openSettingsModel} + />
} diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 79039f7b..cc2a4fce 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 { FeatureName, ModelSelectionOptions, ProviderName } from './voidSettingsTypes.js'; +import { FeatureName, ModelSelectionOptions, OverridesOfModel, ProviderName } from './voidSettingsTypes.js'; @@ -31,6 +31,7 @@ export const defaultProviderSettings = { openAICompatible: { endpoint: '', apiKey: '', + headersJSON: '', }, gemini: { apiKey: '', @@ -50,10 +51,10 @@ export const defaultProviderSettings = { liteLLM: { // https://docs.litellm.ai/docs/providers/openai_compatible endpoint: '', }, - // googleVertex: { // google https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-vertex-using-openai-library - // region: 'us-west2', - // project: '', - // }, + googleVertex: { // google https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-vertex-using-openai-library + region: 'us-west2', + project: '', + }, microsoftAzure: { // microsoft Azure Foundry project: '', // really 'resource' apiKey: '', @@ -129,7 +130,7 @@ export const defaultModelsOfProvider = { 'ministral-8b-latest', ], openAICompatible: [], // fallback - // googleVertex: [], + googleVertex: [], microsoftAzure: [], liteLLM: [], @@ -161,7 +162,7 @@ export type VoidStaticModelInfo = { // not stateful // reasoning options if supports reasoning readonly canTurnOffReasoning: boolean; // whether or not the user can disable reasoning mode (false if the model only supports reasoning) readonly canIOReasoning: boolean; // whether or not the model actually outputs reasoning (eg o1 lets us control reasoning but not output it) - readonly reasoningMaxOutputTokens?: number; // overrides normal maxOutputTokens // <-- UNUSED (except anthropic) + readonly reasoningMaxOutputTokens?: number; // overrides normal maxOutputTokens readonly reasoningBudgetSlider?: { type: 'slider'; min: number; max: number; default: number }; // options related specifically to model output @@ -171,6 +172,26 @@ export type VoidStaticModelInfo = { // not stateful }; } + +export type ModelOverrideOptions = Partial<{ + contextWindow: number; // input tokens + maxOutputTokens: number; // output tokens, defaults to 4092 + supportsTools: 'openai-style' | undefined; + supportsSystemMessage: 'system-role' | 'developer-role' | false; + supportsFIM: boolean; + reasoningCapabilities: false | { + readonly supportsReasoning: true; + readonly canTurnOffReasoning: boolean; + readonly canIOReasoning: boolean; + readonly reasoningMaxOutputTokens?: number; + readonly openSourceThinkTags?: [string, string]; + } +}> + + + + + type ProviderReasoningIOSettings = { // include this in payload to get reasoning input?: { includeInPayload?: (reasoningState: SendableReasoningInfo) => null | { [key: string]: any }, }; @@ -189,15 +210,15 @@ type VoidStaticProviderInfo = { // doesn't change (not stateful) -const modelOptionsDefaults: VoidStaticModelInfo = { - contextWindow: 16_000, +export const defaultModelOptions = { + contextWindow: 4_096, maxOutputTokens: 4_096, cost: { input: 0, output: 0 }, downloadable: false, supportsSystemMessage: false, supportsFIM: false, reasoningCapabilities: false, -} +} as const satisfies VoidStaticModelInfo // TODO!!! double check all context sizes below // TODO!!! add openrouter common models @@ -396,7 +417,7 @@ const extensiveModelFallback: VoidStaticProviderInfo['modelOptionsFallback'] = ( if (Object.keys(openSourceModelOptions_assumingOAICompat).map(k => k.toLowerCase()).includes(lower)) return toFallback(openSourceModelOptions_assumingOAICompat[lower as keyof typeof openSourceModelOptions_assumingOAICompat]) - return toFallback(modelOptionsDefaults) + return toFallback(defaultModelOptions) } @@ -485,7 +506,7 @@ const anthropicSettings: VoidStaticProviderInfo = { if (lower.includes('claude-3-opus')) fallbackName = 'claude-3-opus-20240229' if (lower.includes('claude-3-sonnet')) fallbackName = 'claude-3-sonnet-20240229' if (fallbackName) return { modelName: fallbackName, ...anthropicModelOptions[fallbackName] } - return { modelName, ...modelOptionsDefaults, maxOutputTokens: 4_096 } + return { modelName, ...defaultModelOptions, maxOutputTokens: 4_096 } }, } @@ -854,12 +875,12 @@ const groqSettings: VoidStaticProviderInfo = { // ---------------- GOOGLE VERTEX ---------------- -// const googleVertexModelOptions = { -// } as const satisfies Record -// const googleVertexSettings: VoidStaticProviderInfo = { -// modelOptions: googleVertexModelOptions, -// modelOptionsFallback: (modelName) => { return null } -// } +const googleVertexModelOptions = { +} as const satisfies Record +const googleVertexSettings: VoidStaticProviderInfo = { + modelOptions: googleVertexModelOptions, + modelOptionsFallback: (modelName) => { return null } +} // ---------------- MICROSOFT AZURE ---------------- const microsoftAzureModelOptions = { @@ -872,7 +893,7 @@ const microsoftAzureSettings: VoidStaticProviderInfo = { // ---------------- VLLM, OLLAMA, OPENAICOMPAT (self-hosted / local) ---------------- const ollamaModelOptions = { - 'qwen2.5-coder:1.5b': { + 'qwen2.5-coder:7b': { contextWindow: 32_000, maxOutputTokens: null, cost: { input: 0, output: 0 }, @@ -881,6 +902,24 @@ const ollamaModelOptions = { supportsSystemMessage: 'system-role', reasoningCapabilities: false, }, + 'qwen2.5-coder:3b': { + contextWindow: 32_000, + maxOutputTokens: null, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: 1.9 }, + supportsFIM: true, + supportsSystemMessage: 'system-role', + reasoningCapabilities: false, + }, + 'qwen2.5-coder:1.5b': { + contextWindow: 32_000, + maxOutputTokens: null, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: .986 }, + supportsFIM: true, + supportsSystemMessage: 'system-role', + reasoningCapabilities: false, + }, 'llama3.1': { contextWindow: 128_000, maxOutputTokens: null, @@ -1105,7 +1144,7 @@ const modelSettingsOfProvider: { [providerName in ProviderName]: VoidStaticProvi liteLLM: liteLLMSettings, lmStudio: lmStudioSettings, - // googleVertex: googleVertexSettings, + googleVertex: googleVertexSettings, microsoftAzure: microsoftAzureSettings, } as const @@ -1113,22 +1152,33 @@ const modelSettingsOfProvider: { [providerName in ProviderName]: VoidStaticProvi // ---------------- exports ---------------- // returns the capabilities and the adjusted modelName if it was a fallback -export const getModelCapabilities = (providerName: ProviderName, modelName: string): VoidStaticModelInfo & { modelName: string; isUnrecognizedModel: boolean } => { +export const getModelCapabilities = ( + providerName: ProviderName, + modelName: string, + overridesOfModel?: OverridesOfModel +): VoidStaticModelInfo & { modelName: string; isUnrecognizedModel: boolean } => { const lowercaseModelName = modelName.toLowerCase() const { modelOptions, modelOptionsFallback } = modelSettingsOfProvider[providerName] + // Get any override settings for this model + const overrides = overridesOfModel?.[providerName]?.[modelName]; + // search model options object directly first for (const modelName_ in modelOptions) { const lowercaseModelName_ = modelName_.toLowerCase() - if (lowercaseModelName === lowercaseModelName_) - return { modelName, ...modelOptions[modelName], isUnrecognizedModel: false } + if (lowercaseModelName === lowercaseModelName_) { + return { ...modelOptions[modelName], ...overrides, modelName, isUnrecognizedModel: false }; + } } const result = modelOptionsFallback(modelName) - if (result) return { ...result, isUnrecognizedModel: false } - return { modelName, ...modelOptionsDefaults, isUnrecognizedModel: true } + if (result) { + return { ...result, ...overrides, modelName: result.modelName, isUnrecognizedModel: false }; + } + + return { modelName, ...defaultModelOptions, ...overrides, isUnrecognizedModel: true }; } // non-model settings diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index d0e27fcd..14544216 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -112,7 +112,7 @@ ${searchReplaceBlockTemplate} ## Guidelines: -1. You are encouraged to output multiple changes whenever possible. +1. You may output multiple search replace blocks if needed. 2. The ORIGINAL code in each SEARCH/REPLACE block must EXACTLY match lines in the original file. Do not add or remove any whitespace or comments from the original code. diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index ad7c6a7f..462d7899 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -11,9 +11,9 @@ import { registerSingleton, InstantiationType } from '../../../../platform/insta import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IMetricsService } from './metricsService.js'; -import { defaultProviderSettings, getModelCapabilities } from './modelCapabilities.js'; +import { defaultProviderSettings, getModelCapabilities, ModelOverrideOptions } from './modelCapabilities.js'; import { VOID_SETTINGS_STORAGE_KEY } from './storageKeys.js'; -import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode } from './voidSettingsTypes.js'; +import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode, OverridesOfModel, defaultOverridesOfModel } from './voidSettingsTypes.js'; // name is the name in the dropdown @@ -41,6 +41,7 @@ export type VoidSettingsState = { readonly settingsOfProvider: SettingsOfProvider; // optionsOfProvider readonly modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature readonly optionsOfModelSelection: OptionsOfModelSelection; + readonly overridesOfModel: OverridesOfModel; readonly globalSettings: GlobalSettings; readonly _modelOptions: ModelOption[] // computed based on the two above items @@ -61,6 +62,7 @@ export interface IVoidSettingsService { setModelSelectionOfFeature: SetModelSelectionOfFeatureFn; setOptionsOfModelSelection: SetOptionsOfModelSelection; setGlobalSetting: SetGlobalSettingFn; + setOverridesOfModel(providerName: ProviderName, modelName: string, overrides: ModelOverrideOptions): Promise; dangerousSetState(newState: VoidSettingsState): Promise; resetState(): Promise; @@ -182,6 +184,7 @@ const _validatedModelState = (state: Omit): ...state, settingsOfProvider: newSettingsOfProvider, modelSelectionOfFeature: newModelSelectionOfFeature, + overridesOfModel: state.overridesOfModel, _modelOptions: newModelOptions, } satisfies VoidSettingsState @@ -198,6 +201,7 @@ const defaultState = () => { modelSelectionOfFeature: { 'Chat': null, 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null }, globalSettings: deepClone(defaultGlobalSettings), optionsOfModelSelection: { 'Chat': {}, 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {} }, + overridesOfModel: deepClone(defaultOverridesOfModel), _modelOptions: [], // computed later } return d @@ -267,9 +271,11 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // the stored data structure might be outdated, so we need to update it here try { readS = { + ...defaultState(), ...readS, - ...defaultSettingsOfProvider, - ...readS.settingsOfProvider, + // no idea why this was here, seems like a bug + // ...defaultSettingsOfProvider, + // ...readS.settingsOfProvider, } for (const providerName of providerNames) { @@ -314,7 +320,8 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { return defaultState() const stateStr = await this._encryptionService.decrypt(encryptedState) - return JSON.parse(stateStr) + const state = JSON.parse(stateStr) + return state } @@ -339,12 +346,14 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } const newGlobalSettings = this.state.globalSettings + const newOverridesOfModel = this.state.overridesOfModel const newState = { modelSelectionOfFeature: newModelSelectionOfFeature, optionsOfModelSelection: newOptionsOfModelSelection, settingsOfProvider: newSettingsOfProvider, globalSettings: newGlobalSettings, + overridesOfModel: newOverridesOfModel, } this.state = _validatedModelState(newState) @@ -422,6 +431,30 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { this._onDidChangeState.fire() } + setOverridesOfModel = async (providerName: ProviderName, modelName: string, overrides: ModelOverrideOptions) => { + const currentProviderSettings = this.state.overridesOfModel[providerName] || {}; + + const newState: VoidSettingsState = { + ...this.state, + overridesOfModel: { + ...this.state.overridesOfModel, + [providerName]: { + ...currentProviderSettings, + [modelName]: { + ...currentProviderSettings[modelName], + ...overrides + } + } + } + }; + + this.state = _validatedModelState(newState); + await this._storeState(); + this._onDidChangeState.fire(); + + this._metricsService.capture('Update Model Settings', { providerName, modelName, overrides }); + } + diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 7c9e854b..43fc9a24 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -4,7 +4,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { defaultModelsOfProvider, defaultProviderSettings } from './modelCapabilities.js'; +import { defaultModelsOfProvider, defaultProviderSettings, ModelOverrideOptions } from './modelCapabilities.js'; import { ToolApprovalType } from './toolsServiceTypes.js'; import { VoidSettingsState } from './voidSettingsService.js' @@ -97,9 +97,9 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn else if (providerName === 'mistral') { return { title: 'Mistral', } } - // else if (providerName === 'googleVertex') { - // return { title: 'Google Vertex AI', } - // } + else if (providerName === 'googleVertex') { + return { title: 'Google Vertex AI', } + } else if (providerName === 'microsoftAzure') { return { title: 'Microsoft Azure OpenAI', } } @@ -118,7 +118,7 @@ export const subTextMdOfProviderName = (providerName: ProviderName): string => { if (providerName === 'xAI') return 'Get your [API Key here](https://console.x.ai).' if (providerName === 'mistral') return 'Get your [API Key here](https://console.mistral.ai/api-keys).' if (providerName === 'openAICompatible') return `Use any provider that's OpenAI-compatible (use this for llama.cpp and more).` - // if (providerName === 'googleVertex') return 'You must authenticate before using Vertex with Void. Read more about endpoints [here](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-vertex-using-openai-library), and regions [here](https://cloud.google.com/vertex-ai/docs/general/locations#available-regions).' + if (providerName === 'googleVertex') return 'You must authenticate before using Vertex with Void. Read more about endpoints [here](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-vertex-using-openai-library), and regions [here](https://cloud.google.com/vertex-ai/docs/general/locations#available-regions).' if (providerName === 'microsoftAzure') return 'Read more about endpoints [here](https://learn.microsoft.com/en-us/rest/api/aifoundry/model-inference/get-chat-completions/get-chat-completions?view=rest-aifoundry-model-inference-2024-05-01-preview&tabs=HTTP), and get your API key [here](https://learn.microsoft.com/en-us/azure/search/search-security-api-keys?tabs=rest-use%2Cportal-find%2Cportal-query#find-existing-keys).' if (providerName === 'ollama') return 'If you would like to change this endpoint, please read more about [Endpoints here](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-expose-ollama-on-my-network).' if (providerName === 'vLLM') return 'If you would like to change this endpoint, please read more about [Endpoints here](https://docs.vllm.ai/en/latest/getting_started/quickstart.html#openai-compatible-server).' @@ -149,9 +149,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'openAICompatible' ? 'sk-key...' : providerName === 'xAI' ? 'xai-key...' : providerName === 'mistral' ? 'api-key...' : - // providerName === 'googleVertex' ? 'AIzaSy...' : - providerName === 'microsoftAzure' ? 'key-...' : - '', + providerName === 'googleVertex' ? 'AIzaSy...' : + providerName === 'microsoftAzure' ? 'key-...' : + '', isPasswordField: true, } @@ -162,10 +162,10 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'vLLM' ? 'Endpoint' : providerName === 'lmStudio' ? 'Endpoint' : providerName === 'openAICompatible' ? 'baseURL' : // (do not include /chat/completions) - // providerName === 'googleVertex' ? 'baseURL' : - providerName === 'microsoftAzure' ? 'baseURL' : - providerName === 'liteLLM' ? 'baseURL' : - '(never)', + providerName === 'googleVertex' ? 'baseURL' : + providerName === 'microsoftAzure' ? 'baseURL' : + providerName === 'liteLLM' ? 'baseURL' : + '(never)', placeholder: providerName === 'ollama' ? defaultProviderSettings.ollama.endpoint : providerName === 'vLLM' ? defaultProviderSettings.vLLM.endpoint @@ -177,14 +177,17 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName } } - // else if (settingName === 'region') { - // // vertex only - // return { - // title: 'Region', - // placeholder: providerName === 'googleVertex' ? defaultProviderSettings.googleVertex.region - // : '' - // } - // } + else if (settingName === 'headersJSON') { + return { title: 'Custom Headers', placeholder: '{ "X-Request-Id": "..." }' } + } + else if (settingName === 'region') { + // vertex only + return { + title: 'Region', + placeholder: providerName === 'googleVertex' ? defaultProviderSettings.googleVertex.region + : '' + } + } else if (settingName === 'azureApiVersion') { // azure only return { @@ -196,11 +199,11 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName else if (settingName === 'project') { return { title: providerName === 'microsoftAzure' ? 'Resource' - // : providerName === 'googleVertex' ? 'Project' - : '', + : providerName === 'googleVertex' ? 'Project' + : '', placeholder: providerName === 'microsoftAzure' ? 'my-resource' - // : providerName === 'googleVertex' ? 'my-project' - : '' + : providerName === 'googleVertex' ? 'my-project' + : '' } @@ -228,9 +231,10 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName const defaultCustomSettings: Record = { apiKey: undefined, endpoint: undefined, - // region: undefined, // googleVertex + region: undefined, // googleVertex project: undefined, azureApiVersion: undefined, + headersJSON: undefined, } @@ -324,12 +328,12 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.vLLM), _didFillInProviderSettings: undefined, }, - // googleVertex: { // aggregator (serves models from multiple providers) - // ...defaultCustomSettings, - // ...defaultProviderSettings.googleVertex, - // ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.googleVertex), - // _didFillInProviderSettings: undefined, - // }, + googleVertex: { // aggregator (serves models from multiple providers) + ...defaultCustomSettings, + ...defaultProviderSettings.googleVertex, + ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.googleVertex), + _didFillInProviderSettings: undefined, + }, microsoftAzure: { // aggregator (serves models from multiple providers) ...defaultCustomSettings, ...defaultProviderSettings.microsoftAzure, @@ -467,8 +471,22 @@ export type ModelSelectionOptions = { export type OptionsOfModelSelection = { [featureName in FeatureName]: Partial<{ [providerName in ProviderName]: { - [modelName: string]: - ModelSelectionOptions | undefined + [modelName: string]: ModelSelectionOptions | undefined } }> } + + + + + +export type OverridesOfModel = { + [providerName in ProviderName]: { + [modelName: string]: ModelOverrideOptions | undefined + } +} + + +const overridesOfModel = {} as OverridesOfModel +for (const providerName of providerNames) { overridesOfModel[providerName] = {} } +export const defaultOverridesOfModel = overridesOfModel 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 6eb0ad7a..95b68e45 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 OpenAI, { ClientOptions } from 'openai'; import { MistralCore } from '@mistralai/mistralai/core.js'; import { fimComplete } from '@mistralai/mistralai/funcs/fimComplete.js'; import { GoogleGenerativeAI, Tool as GeminiTool, SchemaType, FunctionDeclaration, FunctionDeclarationSchemaProperty } from '@google/generative-ai'; -// import { GoogleAuth } from 'google-auth-library' +import { GoogleAuth } from 'google-auth-library' /* eslint-enable */ import { AnthropicLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js'; @@ -21,6 +21,14 @@ import { extractReasoningWrapper, extractXMLToolsWrapper } from './extractGramma import { availableTools, InternalToolInfo, isAToolName, ToolParamName, voidTools } from '../../common/prompt/prompts.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; +const getGoogleApiKey = async () => { + // module‑level singleton + const auth = new GoogleAuth({ scopes: `https://www.googleapis.com/auth/cloud-platform` }); + const key = await auth.getAccessToken() + if (!key) throw new Error(`Google API failed to generate a key.`) + return key +} + @@ -50,6 +58,15 @@ const invalidApiKeyMessage = (providerName: ProviderName) => `Invalid ${displayI +const parseHeadersJSON = (s: string | undefined): Record | undefined => { + if (!s) return undefined + try { + return JSON.parse(s) + } catch (e) { + throw new Error(`Error parsing OpenAI-Compatible headers: ${s} is not a valid JSON.`) + } +} + const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includeInPayload }: { settingsOfProvider: SettingsOfProvider, providerName: ProviderName, includeInPayload?: { [s: string]: any } }) => { const commonPayloadOpts: ClientOptions = { dangerouslyAllowBrowser: true, @@ -87,12 +104,13 @@ const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includ ...commonPayloadOpts, }) } - // else if (providerName === 'googleVertex') { - // // https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-vertex-using-openai-library - // const thisConfig = settingsOfProvider[providerName] - // const baseURL = `https://${thisConfig.region}-aiplatform.googleapis.com/v1/projects/${thisConfig.project}/locations/${thisConfig.region}/endpoints/${'openapi'}` - // return new OpenAI({ baseURL: baseURL, apiKey: apiKey, ...commonPayloadOpts }) - // } + else if (providerName === 'googleVertex') { + // https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/call-vertex-using-openai-library + const thisConfig = settingsOfProvider[providerName] + const baseURL = `https://${thisConfig.region}-aiplatform.googleapis.com/v1/projects/${thisConfig.project}/locations/${thisConfig.region}/endpoints/${'openapi'}` + const apiKey = await getGoogleApiKey() + return new OpenAI({ baseURL: baseURL, apiKey: apiKey, ...commonPayloadOpts }) + } else if (providerName === 'microsoftAzure') { // https://learn.microsoft.com/en-us/rest/api/aifoundry/model-inference/get-chat-completions/get-chat-completions?view=rest-aifoundry-model-inference-2024-05-01-preview&tabs=HTTP const thisConfig = settingsOfProvider[providerName] @@ -106,7 +124,8 @@ const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includ } else if (providerName === 'openAICompatible') { const thisConfig = settingsOfProvider[providerName] - return new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, ...commonPayloadOpts }) + const headers = parseHeadersJSON(thisConfig.headersJSON) + return new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, defaultHeaders: headers, ...commonPayloadOpts }) } else if (providerName === 'groq') { const thisConfig = settingsOfProvider[providerName] @@ -843,20 +862,21 @@ export const sendLLMMessageToProviderImplementation = { }, lmStudio: { + // lmStudio has no suffix parameter in /completions, so sendFIM might not work sendChat: (params) => _sendOpenAICompatibleChat(params), - sendFIM: null, // lmStudio has no suffix parameter in /completions + sendFIM: (params) => _sendOpenAICompatibleFIM(params), list: (params) => _openaiCompatibleList(params), }, liteLLM: { + sendChat: (params) => _sendOpenAICompatibleChat(params), + sendFIM: (params) => _sendOpenAICompatibleFIM(params), + list: null, + }, + googleVertex: { sendChat: (params) => _sendOpenAICompatibleChat(params), sendFIM: null, list: null, }, - // googleVertex: { - // sendChat: (params) => _sendOpenAICompatibleChat(params), - // sendFIM: null, - // list: null, - // }, microsoftAzure: { sendChat: (params) => _sendOpenAICompatibleChat(params), sendFIM: null, 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 e1c18f50..7c92d9da 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.ts @@ -114,7 +114,7 @@ export const sendLLMMessage = async ({ await sendFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName, _setAborter, providerName, separateSystemMessage }) return } - onError({ message: `Error: This provider does not support Autocomplete yet.`, fullError: null }) + onError({ message: `Error running Autocomplete with ${providerName} - ${modelName}.`, fullError: null }) return } onError({ message: `Error: Message type "${messagesType}" not recognized.`, fullError: null })