mirror of
https://github.com/voideditor/void
synced 2026-05-24 01:48:25 +00:00
add model overrides and headers JSON!
This commit is contained in:
parent
4fb0d7d727
commit
5090e31a89
11 changed files with 647 additions and 89 deletions
159
package-lock.json
generated
159
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -722,7 +722,7 @@ const ToolHeaderWrapper = ({
|
|||
return (<div className=''>
|
||||
<div className={`w-full border border-void-border-3 rounded px-2 py-1 bg-void-bg-3 overflow-hidden ${className}`}>
|
||||
{/* header */}
|
||||
<div className={`select-none flex items-center ml-4 min-h-[24px]`}>
|
||||
<div className={`select-none flex items-center min-h-[24px]`}>
|
||||
<div className={`flex items-center w-full gap-x-2 overflow-hidden justify-between ${isRejected ? 'line-through' : ''}`}>
|
||||
{/* left */}
|
||||
<div className={`
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, Voi
|
|||
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
|
||||
import { VoidButtonBgDarken, VoidCustomDropdownBox, VoidInputBox2, VoidSimpleInputBox, VoidSwitch } from '../util/inputs.js'
|
||||
import { useAccessor, useIsDark, useRefreshModelListener, useRefreshModelState, useSettingsState } from '../util/services.js'
|
||||
import { X, RefreshCw, Loader2, Check, Asterisk } from 'lucide-react'
|
||||
import { X, RefreshCw, Loader2, Check, Asterisk, Settings as SettingsIcon } from 'lucide-react'
|
||||
import { URI } from '../../../../../../../base/common/uri.js'
|
||||
import { env } from '../../../../../../../base/common/process.js'
|
||||
import { ModelDropdown } from './ModelDropdown.js'
|
||||
|
|
@ -302,13 +302,265 @@ export const AddModelInputBox = ({ providerName: permanentProviderName, classNam
|
|||
}
|
||||
|
||||
|
||||
export const ModelDump = () => {
|
||||
// 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 (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick={onClose}>
|
||||
<div className="bg-void-bg-1 rounded-md p-4 max-w-md w-full shadow-xl overflow-y-auto max-h-[90vh]" onClick={e => e.stopPropagation()}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-medium">Override defaults for {modelName} ({displayInfoOfProviderName(providerName).title})</h3>
|
||||
<button onClick={onClose} className="text-void-fg-3 hover:text-void-fg-1">
|
||||
<X className="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
{/* Model-specific settings */}
|
||||
<div className="border border-void-border-2 rounded-sm p-3">
|
||||
|
||||
{/* Context window */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-void-fg-2">Context window (tokens)</span>
|
||||
<VoidSimpleInputBox
|
||||
value={formValues.contextWindow}
|
||||
onChangeValue={(value) => updateField('contextWindow', value)}
|
||||
placeholder={(modelCapabilities.contextWindow || defaultModelOptions.contextWindow) + ''}
|
||||
compact={true}
|
||||
className="max-w-24"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Maximum output tokens */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-void-fg-2">Maximum output tokens</span>
|
||||
<VoidSimpleInputBox
|
||||
value={formValues.maxOutputTokens}
|
||||
onChangeValue={(value) => updateField('maxOutputTokens', value)}
|
||||
placeholder={(modelCapabilities.maxOutputTokens || defaultModelOptions.maxOutputTokens) + ''}
|
||||
compact={true}
|
||||
className="max-w-24"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Supports Tools */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-void-fg-2">Supports tools</span>
|
||||
<VoidCustomDropdownBox
|
||||
options={['', 'openai-style']}
|
||||
selectedOption={formValues.supportsTools}
|
||||
onChangeOption={(value) => 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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Supports System Message */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-void-fg-2">Supports system message</span>
|
||||
<VoidCustomDropdownBox
|
||||
options={['', 'system-role', 'developer-role', false]}
|
||||
selectedOption={formValues.supportsSystemMessage}
|
||||
onChangeOption={(value) => 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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Supports FIM */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-void-fg-2">Supports fill-in-the-middle (autocomplete)</span>
|
||||
<VoidCustomDropdownBox
|
||||
options={[null, true, false]}
|
||||
selectedOption={formValues.supportsFIM}
|
||||
onChangeOption={(value) => 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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Supports Reasoning */}
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<span className="text-void-fg-2">Supports reasoning</span>
|
||||
<VoidCustomDropdownBox
|
||||
options={[null, true, false]}
|
||||
selectedOption={formValues.reasoningCapabilities}
|
||||
onChangeOption={(value) => 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"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<VoidButtonBgDarken onClick={onClose} className="px-3 py-1">
|
||||
Cancel
|
||||
</VoidButtonBgDarken>
|
||||
<VoidButtonBgDarken onClick={handleSave} className="px-3 py-1 bg-[#0e70c0] text-white">
|
||||
Save
|
||||
</VoidButtonBgDarken>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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' ?
|
||||
<Asterisk size={14} className="inline-block align-text-top brightness-115 stroke-[2] text-[#0e70c0]" data-tooltip-id='void-tooltip' data-tooltip-place='right' data-tooltip-content='Detected locally' />
|
||||
: type === 'default' ? undefined
|
||||
: <Asterisk size={14} className="inline-block align-text-top brightness-115 stroke-[2] text-[#0e70c0]" data-tooltip-id='void-tooltip' data-tooltip-place='right' data-tooltip-content='Custom model' />
|
||||
: type === 'custom' ?
|
||||
<Asterisk size={14} className="inline-block align-text-top brightness-115 stroke-[2] text-[#0e70c0]" data-tooltip-id='void-tooltip' data-tooltip-place='right' data-tooltip-content='Custom model' />
|
||||
: undefined
|
||||
|
||||
|
||||
|
||||
return <div key={`${modelName}${providerName}`}
|
||||
|
|
@ -367,6 +621,27 @@ export const ModelDump = () => {
|
|||
|
||||
{/* <span className='opacity-50 truncate'>{type === 'autodetected' ? '(detected locally)' : type === 'default' ? '' : '(custom model)'}</span> */}
|
||||
|
||||
{/* Settings button - only for custom or locally detected models */}
|
||||
{(type === 'autodetected' || type === 'custom') && (
|
||||
<div className="w-5 flex items-center justify-center">
|
||||
<button
|
||||
onClick={() => {
|
||||
setOpenSettingsModel({
|
||||
modelName,
|
||||
providerName,
|
||||
type
|
||||
})
|
||||
}}
|
||||
data-tooltip-id='void-tooltip'
|
||||
data-tooltip-place='right'
|
||||
data-tooltip-content="Model Settings"
|
||||
>
|
||||
<SettingsIcon size={14} className="text-void-fg-3" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
<VoidSwitch
|
||||
value={value}
|
||||
onChange={() => { settingsStateService.toggleModelHidden(providerName, modelName) }}
|
||||
|
|
@ -384,6 +659,13 @@ export const ModelDump = () => {
|
|||
</div>
|
||||
</div>
|
||||
})}
|
||||
|
||||
{/* Model Settings Dialog */}
|
||||
<ModelSettingsDialog
|
||||
isOpen={openSettingsModel !== null}
|
||||
onClose={() => setOpenSettingsModel(null)}
|
||||
modelInfo={openSettingsModel}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string, VoidStaticModelInfo>
|
||||
// const googleVertexSettings: VoidStaticProviderInfo = {
|
||||
// modelOptions: googleVertexModelOptions,
|
||||
// modelOptionsFallback: (modelName) => { return null }
|
||||
// }
|
||||
const googleVertexModelOptions = {
|
||||
} as const satisfies Record<string, VoidStaticModelInfo>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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<void>;
|
||||
|
||||
dangerousSetState(newState: VoidSettingsState): Promise<void>;
|
||||
resetState(): Promise<void>;
|
||||
|
|
@ -182,6 +184,7 @@ const _validatedModelState = (state: Omit<VoidSettingsState, '_modelOptions'>):
|
|||
...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 });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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<CustomSettingName, undefined> = {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<string, string | null | undefined> | 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,
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
|
|
|
|||
Loading…
Reference in a new issue