add model overrides

This commit is contained in:
Andrew Pareles 2025-05-04 19:32:28 -07:00
parent d841468f3b
commit d826052444
12 changed files with 388 additions and 182 deletions

View file

@ -790,6 +790,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
console.log('starting autocomplete...', predictionType)
const featureName: FeatureName = 'Autocomplete'
const overridesOfModel = this._settingsService.state.overridesOfModel
const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName]
const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined
@ -807,6 +808,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
}),
modelSelection,
modelSelectionOptions,
overridesOfModel,
logging: { loggingName: 'Autocomplete' },
onText: () => { }, // unused in FIMMessage
// onText: async ({ fullText, newText }) => {

View file

@ -641,6 +641,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
// above just defines helpers, below starts the actual function
const { chatMode } = this._settingsService.state.globalSettings // should not change as we loop even if user changes it, so it goes here
const { overridesOfModel } = this._settingsService.state
let nMessagesSent = 0
let shouldSendAnotherMessage = true
@ -694,6 +695,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
messages: messages,
modelSelection,
modelSelectionOptions,
overridesOfModel,
logging: { loggingName: `Chat - ${chatMode}`, loggingExtras: { threadId, nMessagesSent, chatMode } },
separateSystemMessage: separateSystemMessage,
onText: ({ fullText, fullReasoning, toolCall }) => {

View file

@ -607,20 +607,23 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
prepareLLMSimpleMessages: IConvertToLLMMessageService['prepareLLMSimpleMessages'] = ({ simpleMessages, systemMessage, modelSelection, featureName }) => {
if (modelSelection === null) return { messages: [], separateSystemMessage: undefined }
const { overridesOfModel } = this.voidSettingsService.state
const { providerName, modelName } = modelSelection
const {
specialToolFormat,
contextWindow,
supportsSystemMessage,
} = getModelCapabilities(providerName, modelName)
} = getModelCapabilities(providerName, modelName, overridesOfModel)
const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]
// Get combined AI instructions
const aiInstructions = this._getCombinedAIInstructions();
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions)
const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled })
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel)
const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled, overridesOfModel })
const { messages, separateSystemMessage } = prepareMessages({
messages: simpleMessages,
@ -637,12 +640,15 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
}
prepareLLMChatMessages: IConvertToLLMMessageService['prepareLLMChatMessages'] = async ({ chatMessages, chatMode, modelSelection }) => {
if (modelSelection === null) return { messages: [], separateSystemMessage: undefined }
const { overridesOfModel } = this.voidSettingsService.state
const { providerName, modelName } = modelSelection
const {
specialToolFormat,
contextWindow,
supportsSystemMessage,
} = getModelCapabilities(providerName, modelName)
} = getModelCapabilities(providerName, modelName, overridesOfModel)
const systemMessage = await this._generateChatMessagesSystemMessage(chatMode, specialToolFormat)
const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection['Chat'][modelSelection.providerName]?.[modelSelection.modelName]
@ -650,8 +656,8 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
// Get combined AI instructions
const aiInstructions = this._getCombinedAIInstructions();
const isReasoningEnabled = getIsReasoningEnabledState('Chat', providerName, modelName, modelSelectionOptions)
const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled })
const isReasoningEnabled = getIsReasoningEnabledState('Chat', providerName, modelName, modelSelectionOptions, overridesOfModel)
const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled, overridesOfModel })
const llmMessages = this._chatMessagesToSimpleMessages(chatMessages)
const { messages, separateSystemMessage } = prepareMessages({

View file

@ -106,37 +106,42 @@ const removeWhitespaceExceptNewlines = (str: string): string => {
// finds block.orig in fileContents and return its range in file
// startingAtLine is 1-indexed and inclusive
const findTextInCode = (text: string, fileContents: string, canFallbackToRemoveWhitespace: boolean, opts: { startingAtLine?: number, returnType: 'lines' | 'indices' }) => {
// returns 1-indexed lines
const findTextInCode = (text: string, fileContents: string, canFallbackToRemoveWhitespace: boolean, opts: { startingAtLine?: number, returnType: 'lines' }) => {
const startLineIdx = (fileContents: string) => opts?.startingAtLine !== undefined ?
const returnAns = (fileContents: string, idx: number) => {
const startLine = numLinesOfStr(fileContents.substring(0, idx + 1))
const numLines = numLinesOfStr(text)
const endLine = startLine + numLines - 1
return [startLine, endLine] as const
}
const startingAtLineIdx = (fileContents: string) => opts?.startingAtLine !== undefined ?
fileContents.split('\n').slice(0, opts.startingAtLine).join('\n').length // num characters in all lines before startingAtLine
: 0
// idx = starting index in fileContents
let idx = fileContents.indexOf(text, startLineIdx(fileContents))
let idx = fileContents.indexOf(text, startingAtLineIdx(fileContents))
// if idx was found
if (idx !== -1) {
return returnAns(fileContents, idx)
}
if (!canFallbackToRemoveWhitespace)
return 'Not found' as const
// try to find it ignoring all whitespace this time
if (idx === -1 && canFallbackToRemoveWhitespace) {
text = removeWhitespaceExceptNewlines(text)
fileContents = removeWhitespaceExceptNewlines(fileContents)
idx = fileContents.indexOf(text, startLineIdx(fileContents));
}
text = removeWhitespaceExceptNewlines(text)
fileContents = removeWhitespaceExceptNewlines(fileContents)
idx = fileContents.indexOf(text, startingAtLineIdx(fileContents));
if (idx === -1) return 'Not found' as const
const lastIdx = fileContents.lastIndexOf(text)
if (lastIdx !== idx) return 'Not unique' as const
if (opts.returnType === 'lines') {
const startLine = fileContents.substring(0, idx).split('\n').length
const numLines = numLinesOfStr(text)
const endLine = startLine + numLines - 1
return [startLine, endLine] as const
}
else if (opts.returnType === 'indices') {
return [idx, idx + text.length] as const
}
else throw new Error(`findTextInCode: Invalid returnType ${opts.returnType}`)
return returnAns(fileContents, idx)
}
@ -1331,6 +1336,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
const { from, } = opts
const featureName: FeatureName = opts.from === 'ClickApply' ? 'Apply' : 'Ctrl+K'
const overridesOfModel = this._settingsService.state.overridesOfModel
const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName]
const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined
@ -1482,6 +1488,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
messages,
modelSelection,
modelSelectionOptions,
overridesOfModel,
separateSystemMessage,
chatMode: null, // not chat
onText: (params) => {
@ -1556,17 +1563,30 @@ class EditCodeService extends Disposable implements IEditCodeService {
}
private _errContentOfInvalidStr = (str: 'Not found' | 'Not unique' | 'Has overlap', blockOrig: string) => {
/**
* Generates a human-readable error message for an invalid ORIGINAL search block.
*/
private _errContentOfInvalidStr = (
str: 'Not found' | 'Not unique' | 'Has overlap',
blockOrig: string,
): string => {
const problematicCode = `${tripleTick[0]}\n${JSON.stringify(blockOrig)}\n${tripleTick[1]}`
const descStr = str === `Not found` ?
`The edit was not applied. The text in ORIGINAL must EXACTLY match lines of code in the file, but there was no match for:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code matches a code excerpt exactly.`
: str === `Not unique` ?
`The edit was not applied. The text in ORIGINAL must be unique in the file being edited, but the following ORIGINAL code appears multiple times in the file:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code is unique.`
: str === 'Has overlap' ?
`The edit was not applied. The text in the ORIGINAL blocks must not overlap, but the following ORIGINAL code had overlap with another ORIGINAL string:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code blocks do not overlap.`
: ``
// use a switch for better readability / exhaustiveness check
let descStr: string
switch (str) {
case 'Not found':
descStr = `The edit was not applied. The text in ORIGINAL must EXACTLY match lines of code in the file, but there was no match for:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code matches a code excerpt exactly.`
break
case 'Not unique':
descStr = `The edit was not applied. The text in ORIGINAL must be unique in the file being edited, but the following ORIGINAL code appears multiple times in the file:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code is unique.`
break
case 'Has overlap':
descStr = `The edit was not applied. The text in the ORIGINAL blocks must not overlap, but the following ORIGINAL code had overlap with another ORIGINAL string:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code blocks do not overlap.`
break
default:
descStr = ''
}
return descStr
}
@ -1578,22 +1598,34 @@ class EditCodeService extends Disposable implements IEditCodeService {
const { model } = this._voidModelService.getModel(uri)
if (!model) throw new Error(`Error applying Search/Replace blocks: File does not exist.`)
const modelStr = model.getValue(EndOfLinePreference.LF)
// .split('\n').map(l => '\t' + l).join('\n') // for testing purposes only, remember to remove this
const modelStrLines = modelStr.split('\n')
const replacements: { origStart: number; origEnd: number; block: ExtractedSearchReplaceBlock }[] = []
for (const b of blocks) {
const res = findTextInCode(b.orig, modelStr, true, { returnType: 'indices' })
const res = findTextInCode(b.orig, modelStr, true, { returnType: 'lines' })
if (typeof res === 'string')
throw new Error(this._errContentOfInvalidStr(res, b.orig))
const [i, _] = res
let [startLine, endLine] = res
startLine -= 1 // 0-index
endLine -= 1
replacements.push({
origStart: i,
origEnd: i + b.orig.length - 1, // INCLUSIVE
block: b,
})
// including newline before start
const contentBeforeStart = startLine !== 0 ?
modelStrLines.slice(0, startLine).join('\n') + '\n'
: ''
// including endline at end
const contentUpToEnd = modelStrLines.slice(0, endLine + 1).join('\n')
const origStart = contentBeforeStart.length;
const origEnd = contentUpToEnd.length;
replacements.push({ origStart, origEnd, block: b });
}
// sort in increasing order
replacements.sort((a, b) => a.origStart - b.origStart)
@ -1610,17 +1642,18 @@ class EditCodeService extends Disposable implements IEditCodeService {
const { origStart, origEnd, block } = replacements[i]
newCode = newCode.slice(0, origStart) + block.final + newCode.slice(origEnd + 1, Infinity)
}
console.log('REPLACEMENTS', replacements, newCode)
this._writeURIText(uri, newCode,
'wholeFileRange',
{ shouldRealignDiffAreas: true }
)
}
private _initializeSearchAndReplaceStream(opts: StartApplyingOpts & { from: 'ClickApply' }): [DiffZone, Promise<void>] | undefined {
const { from, applyStr, } = opts
const featureName: FeatureName = 'Apply'
const overridesOfModel = this._settingsService.state.overridesOfModel
const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName]
const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined
@ -1900,6 +1933,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
messages,
modelSelection,
modelSelectionOptions,
overridesOfModel,
separateSystemMessage,
chatMode: null, // not chat
onText: (params) => {

View file

@ -153,14 +153,16 @@ const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) =>
const voidSettingsState = useSettingsState()
const modelSelection = voidSettingsState.modelSelectionOfFeature[featureName]
const overridesOfModel = voidSettingsState.overridesOfModel
if (!modelSelection) return null
const { modelName, providerName } = modelSelection
const { reasoningCapabilities } = getModelCapabilities(providerName, modelName)
const { reasoningCapabilities } = getModelCapabilities(providerName, modelName, overridesOfModel)
const { canTurnOffReasoning, reasoningBudgetSlider } = reasoningCapabilities || {}
const modelSelectionOptions = voidSettingsState.optionsOfModelSelection[featureName][providerName]?.[modelName]
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions)
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel)
if (canTurnOffReasoning && !reasoningBudgetSlider) { // if it's just a on/off toggle without a power slider (no models right now)
return null // unused right now
// return <div className='flex items-center gap-x-2'>

View file

@ -56,7 +56,7 @@ const MemoizedModelDropdown = ({ featureName, className }: { featureName: Featur
useEffect(() => {
const oldOptions = oldOptionsRef.current
const newOptions = settingsState._modelOptions.filter((o) => filter(o.selection, { chatMode: settingsState.globalSettings.chatMode }))
const newOptions = settingsState._modelOptions.filter((o) => filter(o.selection, { chatMode: settingsState.globalSettings.chatMode, overridesOfModel: settingsState.overridesOfModel }))
if (!optionsEqual(oldOptions, newOptions)) {
setMemoizedOptions(newOptions)

View file

@ -303,7 +303,7 @@ export const AddModelInputBox = ({ providerName: permanentProviderName, classNam
// Import the getModelCapabilities function to access default values
import { defaultModelOptions, getModelCapabilities, ModelOverrideOptions } from '../../../../common/modelCapabilities.js';
import { getModelCapabilities, ModelOverrideOptions } from '../../../../common/modelCapabilities.js';
// Modal dialog to show model settings
const ModelSettingsDialog = ({
@ -324,45 +324,68 @@ const ModelSettingsDialog = ({
// Get current model capabilities and override settings
const modelCapabilities = getModelCapabilities(providerName, modelName, settingsState.overridesOfModel);
const defaultModelCapabilities = getModelCapabilities(providerName, modelName, undefined)
// 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 | '';
specialToolFormat: 'openai-style' | 'gemini-style' | 'anthropic-style' | undefined | '';
supportsSystemMessage: 'system-role' | 'developer-role' | 'separated' | false | '';
supportsFIM: boolean | null;
reasoningCapabilities: boolean | null;
canTurnOffReasoning: boolean;
reasoningMaxOutputTokens: string;
openSourceThinkTags: [string, string] | null;
}>({
// start form as default values
contextWindow: '',
maxOutputTokens: '',
supportsTools: '',
specialToolFormat: '',
supportsSystemMessage: '',
supportsFIM: null,
reasoningCapabilities: null
reasoningCapabilities: null,
canTurnOffReasoning: false,
reasoningMaxOutputTokens: '',
openSourceThinkTags: null,
});
// When dialog opens or model changes, reset form values
useEffect(() => {
if (isOpen && modelInfo) {
// Get current overrides
const overrides = settingsState.overridesOfModel?.[providerName]?.[modelName] || {};
// Extract reasoning capabilities if available (use any to avoid TS union narrowing issues)
const reasoningCapabilities: any = typeof overrides.reasoningCapabilities === 'object' ?
overrides.reasoningCapabilities : overrides.reasoningCapabilities ? { supportsReasoning: true, canIOReasoning: true } : false;
// Extract the think tags if they exist
let thinkTags: [string, string] | null = null;
if (typeof reasoningCapabilities === 'object' && reasoningCapabilities.openSourceThinkTags) {
thinkTags = reasoningCapabilities.openSourceThinkTags as [string, string];
}
// Only set values that are explicitly overridden, otherwise leave them empty
// to indicate default values should be used
setFormValues({
contextWindow: (overrides.contextWindow !== undefined) ? overrides.contextWindow?.toString() : '',
maxOutputTokens: (overrides.maxOutputTokens !== undefined) ? overrides.maxOutputTokens?.toString() : '',
supportsTools: overrides.supportsTools !== undefined ? overrides.supportsTools : '',
contextWindow: overrides.contextWindow !== undefined ? String(overrides.contextWindow) : '',
maxOutputTokens: overrides.maxOutputTokens !== undefined ? String(overrides.maxOutputTokens) : '',
specialToolFormat: overrides.specialToolFormat !== undefined ? overrides.specialToolFormat : '',
supportsSystemMessage: overrides.supportsSystemMessage !== undefined ? overrides.supportsSystemMessage : '',
supportsFIM: overrides.supportsFIM !== undefined ? overrides.supportsFIM : null,
reasoningCapabilities: overrides.reasoningCapabilities !== undefined ?
!!overrides.reasoningCapabilities : null
!!overrides.reasoningCapabilities : null,
canTurnOffReasoning: typeof reasoningCapabilities === 'object' ? !!reasoningCapabilities.canTurnOffReasoning : false,
reasoningMaxOutputTokens: typeof reasoningCapabilities === 'object' && reasoningCapabilities.reasoningMaxOutputTokens ?
String(reasoningCapabilities.reasoningMaxOutputTokens) : '',
openSourceThinkTags: thinkTags,
});
}
}, [isOpen, modelInfo, settingsState.overridesOfModel, providerName, modelName]);
// Update a single field in the form
const updateField = (field: string, value: any) => {
const updateField = (field: keyof typeof formValues, value: any) => {
setFormValues(prev => ({
...prev,
[field]: value
@ -371,52 +394,78 @@ const ModelSettingsDialog = ({
// Handle saving settings
const handleSave = async () => {
const settings: ModelOverrideOptions = {};
// Get current overrides to see what needs to be updated/removed
const currentOverrides = settingsState.overridesOfModel?.[providerName]?.[modelName] || {};
const newSettings: ModelOverrideOptions = {};
// Only add fields to the override if they have been changed from defaults
if (formValues.contextWindow) {
// Handle numeric fields - empty strings should remove the override
if (formValues.contextWindow.trim() === '') {
newSettings.contextWindow = defaultModelCapabilities.contextWindow;
} else if (formValues.contextWindow) {
const tokens = parseInt(formValues.contextWindow);
if (!isNaN(tokens)) settings.contextWindow = tokens;
if (!isNaN(tokens)) newSettings.contextWindow = tokens;
}
if (formValues.maxOutputTokens) {
if (formValues.maxOutputTokens.trim() === '') {
newSettings.maxOutputTokens = defaultModelCapabilities.maxOutputTokens;
} else if (formValues.maxOutputTokens) {
const tokens = parseInt(formValues.maxOutputTokens);
if (!isNaN(tokens)) settings.maxOutputTokens = tokens;
if (!isNaN(tokens)) newSettings.maxOutputTokens = tokens;
}
if (formValues.supportsTools !== '') {
settings.supportsTools = formValues.supportsTools as any;
// Handle dropdown fields
if (formValues.specialToolFormat === '') {
newSettings.specialToolFormat = defaultModelCapabilities.specialToolFormat
} else {
newSettings.specialToolFormat = formValues.specialToolFormat
}
if (formValues.supportsSystemMessage !== '') {
settings.supportsSystemMessage = formValues.supportsSystemMessage as any;
if (formValues.supportsSystemMessage === '') {
newSettings.supportsSystemMessage = defaultModelCapabilities.supportsSystemMessage;
} else {
newSettings.supportsSystemMessage = formValues.supportsSystemMessage as any;
}
if (formValues.supportsFIM !== null) {
settings.supportsFIM = formValues.supportsFIM;
if (formValues.supportsFIM === null) {
newSettings.supportsFIM = defaultModelCapabilities.supportsFIM
} else {
newSettings.supportsFIM = formValues.supportsFIM;
}
if (formValues.reasoningCapabilities !== null) {
if (formValues.reasoningCapabilities) {
settings.reasoningCapabilities = {
supportsReasoning: true,
canTurnOffReasoning: true,
canIOReasoning: true
};
} else {
settings.reasoningCapabilities = false;
if (formValues.reasoningCapabilities === null) {
newSettings.reasoningCapabilities = defaultModelCapabilities.reasoningCapabilities;
} else if (formValues.reasoningCapabilities) {
const reasoningSettings: any = {
supportsReasoning: true,
canIOReasoning: true,
canTurnOffReasoning: formValues.canTurnOffReasoning
};
// Only add these if they have values
if (formValues.reasoningMaxOutputTokens) {
reasoningSettings.reasoningMaxOutputTokens = parseInt(formValues.reasoningMaxOutputTokens);
}
if (formValues.openSourceThinkTags) {
reasoningSettings.openSourceThinkTags = formValues.openSourceThinkTags;
}
newSettings.reasoningCapabilities = reasoningSettings;
} else {
newSettings.reasoningCapabilities = false;
}
await settingsStateService.setOverridesOfModel(providerName, modelName, settings);
await settingsStateService.setOverridesOfModel(providerName, modelName, newSettings);
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>
<h3 className="text-lg font-medium">Change 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>
@ -429,25 +478,51 @@ const ModelSettingsDialog = ({
{/* 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 className="flex items-center gap-2">
<VoidSwitch
size="xxs"
value={formValues.contextWindow !== ''}
onChange={(enabled) => {
updateField('contextWindow', enabled ? String(defaultModelCapabilities.contextWindow) : '');
}}
/>
{formValues.contextWindow === '' ? (
<span className="text-void-fg-3 text-xs w-24 text-right">Default ({defaultModelCapabilities.contextWindow})</span>
) : (
<VoidSimpleInputBox
value={formValues.contextWindow}
onChangeValue={(value) => updateField('contextWindow', value)}
placeholder={String(defaultModelCapabilities.contextWindow)}
compact={true}
className="max-w-24"
/>
)}
</div>
</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 className="flex items-center gap-2">
<VoidSwitch
size="xxs"
value={formValues.maxOutputTokens !== ''}
onChange={(enabled) => {
updateField('maxOutputTokens', enabled ? String(defaultModelCapabilities.maxOutputTokens) : '');
}}
/>
{formValues.maxOutputTokens === '' ? (
<span className="text-void-fg-3 text-xs w-24 text-right">Default ({defaultModelCapabilities.maxOutputTokens})</span>
) : (
<VoidSimpleInputBox
value={formValues.maxOutputTokens}
onChangeValue={(value) => updateField('maxOutputTokens', value)}
placeholder={String(defaultModelCapabilities.maxOutputTokens)}
compact={true}
className="max-w-24"
/>
)}
</div>
</div>
{/* Supports Tools */}
@ -455,10 +530,10 @@ const ModelSettingsDialog = ({
<span className="text-void-fg-2">Supports tools</span>
<VoidCustomDropdownBox
options={['', 'openai-style']}
selectedOption={formValues.supportsTools}
onChangeOption={(value) => updateField('supportsTools', value)}
selectedOption={formValues.specialToolFormat}
onChangeOption={(value) => updateField('specialToolFormat', value)}
getOptionDisplayName={(opt) => {
if (opt === '') return `Default (${modelCapabilities.specialToolFormat || 'No'})`;
if (opt === '') return `Default (${defaultModelCapabilities.specialToolFormat || 'No'})`;
return opt;
}}
getOptionDropdownName={(opt) => {
@ -478,7 +553,7 @@ const ModelSettingsDialog = ({
selectedOption={formValues.supportsSystemMessage}
onChangeOption={(value) => updateField('supportsSystemMessage', value)}
getOptionDisplayName={(opt) => {
if (opt === '') return `Default (${modelCapabilities.supportsSystemMessage || 'No'})`;
if (opt === '') return `Default (${defaultModelCapabilities.supportsSystemMessage || 'No'})`;
if (opt === false) return 'No'
if (opt === true) return 'Yes' // should never happen
return opt;
@ -502,7 +577,7 @@ const ModelSettingsDialog = ({
selectedOption={formValues.supportsFIM}
onChangeOption={(value) => updateField('supportsFIM', value)}
getOptionDisplayName={(opt) => {
if (opt === null) return `Default (${modelCapabilities.supportsFIM ? 'Yes' : 'No'})`;
if (opt === null) return `Default (${defaultModelCapabilities.supportsFIM ? 'Yes' : 'No'})`;
return opt ? 'Yes' : 'No';
}}
getOptionDropdownName={(opt) => {
@ -522,7 +597,7 @@ const ModelSettingsDialog = ({
selectedOption={formValues.reasoningCapabilities}
onChangeOption={(value) => updateField('reasoningCapabilities', value)}
getOptionDisplayName={(opt) => {
if (opt === null) return `Default (${modelCapabilities.reasoningCapabilities ? 'Yes' : 'No'})`;
if (opt === null) return `Default (${defaultModelCapabilities.reasoningCapabilities ? 'Yes' : 'No'})`;
return opt ? 'Yes' : 'No';
}}
getOptionDropdownName={(opt) => {
@ -533,6 +608,100 @@ const ModelSettingsDialog = ({
className="max-w-32 text-xs"
/>
</div>
{/* Additional reasoning options - only show when reasoning is enabled */}
{formValues.reasoningCapabilities && (
<>
{/* Can Turn Off Reasoning */}
<div className="flex items-center justify-between py-1 pl-6">
<span className="text-void-fg-2">Allow turning off reasoning</span>
<VoidCustomDropdownBox
options={[true, false]}
selectedOption={formValues.canTurnOffReasoning}
onChangeOption={(value) => updateField('canTurnOffReasoning', value)}
getOptionDisplayName={(opt) => opt ? 'Yes' : 'No'}
getOptionDropdownName={(opt) => opt ? 'Yes' : 'No'}
getOptionsEqual={(a, b) => a === b}
className="max-w-32 text-xs"
/>
</div>
{/* Reasoning Max Output Tokens - only shown if canTurnOffReasoning is true */}
{formValues.canTurnOffReasoning && (
<div className="flex items-center justify-between py-1 pl-6">
<span className="text-void-fg-2">Max output tokens when reasoning</span>
<div className="flex items-center gap-2">
<VoidSwitch
size="xxs"
value={formValues.reasoningMaxOutputTokens !== ''}
onChange={(enabled) => {
// Use a reasonable default value when enabling
const defaultValue = defaultModelCapabilities.maxOutputTokens || 500;
updateField('reasoningMaxOutputTokens', enabled ? String(defaultValue) : '');
}}
/>
{formValues.reasoningMaxOutputTokens === '' ? (
<span className="text-void-fg-3 text-xs w-24 text-right">Default</span>
) : (
<VoidSimpleInputBox
value={formValues.reasoningMaxOutputTokens}
onChangeValue={(value) => updateField('reasoningMaxOutputTokens', value)}
placeholder="Default"
compact={true}
className="max-w-24"
/>
)}
</div>
</div>
)}
{/* Open Source Think Tags Toggle + Input Fields */}
<div className="flex items-center justify-between py-1 pl-6">
<span className="text-void-fg-2">Open source think tags</span>
<div className="flex items-center gap-2">
<VoidSwitch
size="xxs"
value={formValues.openSourceThinkTags !== null}
onChange={(enabled) => {
if (enabled) {
// Enable with default values
updateField('openSourceThinkTags', ['<think>', '</think>']);
} else {
// Disable
updateField('openSourceThinkTags', null);
}
}}
/>
{formValues.openSourceThinkTags !== null && (
<div className="flex gap-1 items-center">
<VoidSimpleInputBox
value={formValues.openSourceThinkTags ? formValues.openSourceThinkTags[0] : ''}
onChangeValue={(value) => {
const currentTags = formValues.openSourceThinkTags || ['', ''];
updateField('openSourceThinkTags', [value, currentTags[1]]);
}}
placeholder="<think>"
compact={true}
className="max-w-16"
/>
<span className="text-void-fg-3">...</span>
<VoidSimpleInputBox
value={formValues.openSourceThinkTags ? formValues.openSourceThinkTags[1] : ''}
onChangeValue={(value) => {
const currentTags = formValues.openSourceThinkTags || ['', ''];
updateField('openSourceThinkTags', [currentTags[0], value]);
}}
placeholder="</think>"
compact={true}
className="max-w-16"
/>
</div>
)}
</div>
</div>
</>
)}
</div>
</div>
@ -587,8 +756,8 @@ export const ModelDump = () => {
const tooltipName = (
disabled ? `Add ${providerTitle} to enable`
: value === true ? 'Enabled'
: 'Disabled'
: value === true ? 'Show in Dropdown'
: 'Hide from Dropdown'
)
@ -601,50 +770,39 @@ export const ModelDump = () => {
return <div key={`${modelName}${providerName}`}
className={`flex items-center justify-between gap-4 hover:bg-black/10 dark:hover:bg-gray-300/10 py-1 px-3 rounded-sm overflow-hidden cursor-default truncate
className={`flex items-center justify-between gap-4 hover:bg-black/10 dark:hover:bg-gray-300/10 py-1 px-3 rounded-sm overflow-hidden cursor-default truncate group
`}
>
{/* left part is width:full */}
<div className={`flex-grow flex items-center gap-4`}>
<div className={`flex flex-grow items-center gap-4`}>
<span className='w-full max-w-32'>{isNewProviderName ? providerTitle : ''}</span>
<span className='w-fit truncate'>{modelName}{detailAboutModel}</span>
<span className='w-fit truncate'>{modelName}</span>
</div>
{/* right part is anything that fits */}
<div className='flex items-center gap-4'
// data-tooltip-id='void-tooltip'
// data-tooltip-place='top'
// data-tooltip-content={disabled ? `${displayInfoOfProviderName(providerName).title} is disabled`
// : (isHidden ? `'${modelName}' won't appear in dropdowns` : ``)
// }
>
{/* <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>
)}
<div className="flex items-center gap-4 w-fit">
{/* Advanced Settings button - only for custom or locally detected models */}
<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='Advanced Settings'
className="opacity-0 group-hover:opacity-100 transition-opacity"
>
<SettingsIcon size={14} className="text-void-fg-3" />
</button>
</div>
{/* Blue star */}
{detailAboutModel}
{/* Switch */}
<VoidSwitch
value={value}
onChange={() => { settingsStateService.toggleModelHidden(providerName, modelName) }}
onChange={() => { settingsStateService.toggleModelHidden(providerName, modelName); }}
disabled={disabled}
size='sm'
@ -653,8 +811,9 @@ export const ModelDump = () => {
data-tooltip-content={tooltipName}
/>
{/* X button */}
<div className={`w-5 flex items-center justify-center`}>
{type === 'default' || type === 'autodetected' ? null : <button onClick={() => { settingsStateService.deleteModel(providerName, modelName) }}><X className='size-4' /></button>}
{type === 'default' || type === 'autodetected' ? null : <button onClick={() => { settingsStateService.deleteModel(providerName, modelName); }}><X className="size-4" /></button>}
</div>
</div>
</div>
@ -1301,7 +1460,7 @@ export const Settings = () => {
<span
className='hover:brightness-110'
data-tooltip-id='void-tooltip'
data-tooltip-content='We recommend using qwen2.5-coder:1.5b with Ollama.'
data-tooltip-content='We recommend using the largest qwen2.5-coder model you can with Ollama (try qwen2.5-coder:3b).'
data-tooltip-class-name='void-max-w-[20px]'
>
Only works with FIM models.*

View file

@ -173,22 +173,9 @@ 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];
}
}>
export type ModelOverrideOptions = Partial<Pick<VoidStaticModelInfo,
'contextWindow' | 'maxOutputTokens' | 'specialToolFormat' | 'supportsSystemMessage' | 'supportsFIM' | 'reasoningCapabilities'
>>
@ -210,7 +197,7 @@ type VoidStaticProviderInfo = { // doesn't change (not stateful)
export const defaultModelOptions = {
const defaultModelOptions = {
contextWindow: 4_096,
maxOutputTokens: 4_096,
cost: { input: 0, output: 0 },
@ -1155,7 +1142,7 @@ const modelSettingsOfProvider: { [providerName in ProviderName]: VoidStaticProvi
export const getModelCapabilities = (
providerName: ProviderName,
modelName: string,
overridesOfModel?: OverridesOfModel
overridesOfModel: OverridesOfModel | undefined
): VoidStaticModelInfo & { modelName: string; isUnrecognizedModel: boolean } => {
const lowercaseModelName = modelName.toLowerCase()
@ -1201,8 +1188,9 @@ export const getIsReasoningEnabledState = (
providerName: ProviderName,
modelName: string,
modelSelectionOptions: ModelSelectionOptions | undefined,
overridesOfModel: OverridesOfModel | undefined,
) => {
const { supportsReasoning, canTurnOffReasoning } = getModelCapabilities(providerName, modelName).reasoningCapabilities || {}
const { supportsReasoning, canTurnOffReasoning } = getModelCapabilities(providerName, modelName, overridesOfModel).reasoningCapabilities || {}
if (!supportsReasoning) return false
// default to enabled if can't turn off, or if the featureName is Chat.
@ -1213,11 +1201,11 @@ export const getIsReasoningEnabledState = (
}
export const getMaxOutputTokens = (providerName: ProviderName, modelName: string, opts: { isReasoningEnabled: boolean }) => {
export const getMaxOutputTokens = (providerName: ProviderName, modelName: string, opts: { isReasoningEnabled: boolean, overridesOfModel: OverridesOfModel | undefined }) => {
const {
reasoningCapabilities,
maxOutputTokens
} = getModelCapabilities(providerName, modelName)
maxOutputTokens,
} = getModelCapabilities(providerName, modelName, opts.overridesOfModel)
return opts.isReasoningEnabled && reasoningCapabilities ? reasoningCapabilities.reasoningMaxOutputTokens : maxOutputTokens
}
@ -1227,11 +1215,12 @@ export const getSendableReasoningInfo = (
providerName: ProviderName,
modelName: string,
modelSelectionOptions: ModelSelectionOptions | undefined,
overridesOfModel: OverridesOfModel | undefined,
): SendableReasoningInfo => {
const { canIOReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName).reasoningCapabilities || {}
const { canIOReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName, overridesOfModel).reasoningCapabilities || {}
if (!canIOReasoning) return null
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions)
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel)
if (!isReasoningEnabled) return null
// check for reasoning budget

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------*/
import { ToolName, ToolParamName } from './prompt/prompts.js'
import { ChatMode, ModelSelection, ModelSelectionOptions, ProviderName, RefreshableProviderName, SettingsOfProvider } from './voidSettingsTypes.js'
import { ChatMode, ModelSelection, ModelSelectionOptions, OverridesOfModel, ProviderName, RefreshableProviderName, SettingsOfProvider } from './voidSettingsTypes.js'
export const errorDetails = (fullError: Error | null): string | null => {
@ -116,6 +116,7 @@ export type ServiceSendLLMMessageParams = {
logging: { loggingName: string, loggingExtras?: { [k: string]: any } };
modelSelection: ModelSelection | null;
modelSelectionOptions: ModelSelectionOptions | undefined;
overridesOfModel: OverridesOfModel | undefined;
onAbort: OnAbort;
} & SendLLMType;
@ -129,6 +130,7 @@ export type SendLLMMessageParams = {
modelSelection: ModelSelection;
modelSelectionOptions: ModelSelectionOptions | undefined;
overridesOfModel: OverridesOfModel | undefined;
settingsOfProvider: SettingsOfProvider;
} & SendLLMType

View file

@ -96,8 +96,15 @@ const _modelsWithSwappedInNewModels = (options: { existingModels: VoidStatefulMo
}
export const modelFilterOfFeatureName: { [featureName in FeatureName]: { filter: (o: ModelSelection, opts: { chatMode: ChatMode }) => boolean; emptyMessage: null | { message: string, priority: 'always' | 'fallback' } } } = {
'Autocomplete': { filter: (o) => getModelCapabilities(o.providerName, o.modelName).supportsFIM, emptyMessage: { message: 'No models support FIM', priority: 'always' } },
export const modelFilterOfFeatureName: {
[featureName in FeatureName]: {
filter: (
o: ModelSelection,
opts: { chatMode: ChatMode, overridesOfModel: OverridesOfModel }
) => boolean;
emptyMessage: null | { message: string, priority: 'always' | 'fallback' }
} } = {
'Autocomplete': { filter: (o, opts) => getModelCapabilities(o.providerName, o.modelName, opts.overridesOfModel).supportsFIM, emptyMessage: { message: 'No models support FIM', priority: 'always' } },
'Chat': { filter: o => true, emptyMessage: null, },
'Ctrl+K': { filter: o => true, emptyMessage: null, },
'Apply': { filter: o => true, emptyMessage: null, },
@ -165,7 +172,7 @@ const _validatedModelState = (state: Omit<VoidSettingsState, '_modelOptions'>):
for (const featureName of featureNames) {
const { filter } = modelFilterOfFeatureName[featureName]
const filterOpts = { chatMode: state.globalSettings.chatMode }
const filterOpts = { chatMode: state.globalSettings.chatMode, overridesOfModel: state.overridesOfModel }
const modelOptionsForThisFeature = newModelOptions.filter((o) => filter(o.selection, filterOpts))
const modelSelectionAtFeature = newModelSelectionOfFeature[featureName]

View file

@ -15,7 +15,7 @@ import { GoogleAuth } from 'google-auth-library'
/* eslint-enable */
import { AnthropicLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js';
import { ChatMode, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js';
import { ChatMode, displayInfoOfProviderName, ModelSelectionOptions, OverridesOfModel, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js';
import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, defaultProviderSettings, getMaxOutputTokens } from '../../common/modelCapabilities.js';
import { extractReasoningWrapper, extractXMLToolsWrapper } from './extractGrammar.js';
import { availableTools, InternalToolInfo, isAToolName, ToolParamName, voidTools } from '../../common/prompt/prompts.js';
@ -39,6 +39,7 @@ type InternalCommonMessageParams = {
providerName: ProviderName;
settingsOfProvider: SettingsOfProvider;
modelSelectionOptions: ModelSelectionOptions | undefined;
overridesOfModel: OverridesOfModel | undefined;
modelName: string;
_setAborter: (aborter: () => void) => void;
}
@ -144,9 +145,9 @@ const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includ
}
const _sendOpenAICompatibleFIM = async ({ messages: { prefix, suffix, stopTokens }, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, }: SendFIMParams_Internal) => {
const _sendOpenAICompatibleFIM = async ({ messages: { prefix, suffix, stopTokens }, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, overridesOfModel }: SendFIMParams_Internal) => {
const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_)
const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_, overridesOfModel)
if (!supportsFIM) {
if (modelName === modelName_)
onError({ message: `Model ${modelName} does not support FIM.`, fullError: null })
@ -230,18 +231,18 @@ const rawToolCallObjOf = (name: string, toolParamsStr: string, id: string): RawT
// ------------ OPENAI-COMPATIBLE ------------
const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName: modelName_, _setAborter, providerName, chatMode, separateSystemMessage }: SendChatParams_Internal) => {
const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName: modelName_, _setAborter, providerName, chatMode, separateSystemMessage, overridesOfModel }: SendChatParams_Internal) => {
const {
modelName,
specialToolFormat,
reasoningCapabilities,
} = getModelCapabilities(providerName, modelName_)
} = getModelCapabilities(providerName, modelName_, overridesOfModel)
const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
// reasoning
const { canIOReasoning, openSourceThinkTags, } = reasoningCapabilities || {}
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions) // user's modelName_ here
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here
const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
// tools
@ -415,21 +416,21 @@ const anthropicToolToRawToolCallObj = (toolBlock: Anthropic.Messages.ToolUseBloc
}
// ------------ ANTHROPIC ------------
const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName: modelName_, _setAborter, separateSystemMessage, chatMode }: SendChatParams_Internal) => {
const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, overridesOfModel, modelName: modelName_, _setAborter, separateSystemMessage, chatMode }: SendChatParams_Internal) => {
const {
modelName,
specialToolFormat,
} = getModelCapabilities(providerName, modelName_)
} = getModelCapabilities(providerName, modelName_, overridesOfModel)
const thisConfig = settingsOfProvider.anthropic
const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
// reasoning
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions) // user's modelName_ here
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here
const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
// anthropic-specific - max tokens
const maxTokens = getMaxOutputTokens(providerName, modelName_, { isReasoningEnabled: !!reasoningInfo?.isReasoningEnabled })
const maxTokens = getMaxOutputTokens(providerName, modelName_, { isReasoningEnabled: !!reasoningInfo?.isReasoningEnabled, overridesOfModel })
// tools
const potentialTools = chatMode !== null ? anthropicTools(chatMode) : null
@ -539,8 +540,8 @@ const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessag
// ------------ MISTRAL ------------
// https://docs.mistral.ai/api/#tag/fim
const sendMistralFIM = ({ messages, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName }: SendFIMParams_Internal) => {
const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_)
const sendMistralFIM = ({ messages, onFinalMessage, onError, settingsOfProvider, overridesOfModel, modelName: modelName_, _setAborter, providerName }: SendFIMParams_Internal) => {
const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_, overridesOfModel)
if (!supportsFIM) {
if (modelName === modelName_)
onError({ message: `Model ${modelName} does not support FIM.`, fullError: null })
@ -679,6 +680,7 @@ const sendGeminiChat = async ({
onFinalMessage,
onError,
settingsOfProvider,
overridesOfModel,
modelName: modelName_,
_setAborter,
providerName,
@ -694,13 +696,13 @@ const sendGeminiChat = async ({
modelName,
specialToolFormat,
// reasoningCapabilities,
} = getModelCapabilities(providerName, modelName_)
} = getModelCapabilities(providerName, modelName_, overridesOfModel)
const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
// reasoning
// const { canIOReasoning, openSourceThinkTags, } = reasoningCapabilities || {}
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions) // user's modelName_ here
const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here
const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
// tools

View file

@ -20,6 +20,7 @@ export const sendLLMMessage = async ({
settingsOfProvider,
modelSelection,
modelSelectionOptions,
overridesOfModel,
chatMode,
separateSystemMessage,
}: SendLLMMessageParams,
@ -106,12 +107,12 @@ export const sendLLMMessage = async ({
}
const { sendFIM, sendChat } = implementation
if (messagesType === 'chatMessages') {
await sendChat({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName, _setAborter, providerName, separateSystemMessage, chatMode })
await sendChat({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, overridesOfModel, modelName, _setAborter, providerName, separateSystemMessage, chatMode })
return
}
if (messagesType === 'FIMMessage') {
if (sendFIM) {
await sendFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName, _setAborter, providerName, separateSystemMessage })
await sendFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, overridesOfModel, modelName, _setAborter, providerName, separateSystemMessage })
return
}
onError({ message: `Error running Autocomplete with ${providerName} - ${modelName}.`, fullError: null })