diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 7f11ff15..592ad006 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -159,23 +159,26 @@ const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) => const { modelName, providerName } = modelSelection const { reasoningCapabilities } = getModelCapabilities(providerName, modelName, overridesOfModel) - const { canTurnOffReasoning, reasoningBudgetSlider } = reasoningCapabilities || {} + const { canTurnOffReasoning, reasoningSlider: reasoningBudgetSlider } = reasoningCapabilities || {} const modelSelectionOptions = voidSettingsState.optionsOfModelSelection[featureName][providerName]?.[modelName] 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
- // {isReasoningEnabled ? 'Thinking' : 'Thinking'} - // { } } - // /> - //
+ + if (canTurnOffReasoning && !reasoningBudgetSlider) { // if it's just a on/off toggle without a power slider + return
+ Thinking + { + const isOff = canTurnOffReasoning && !newVal + voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !isOff }) + }} + /> +
} - if (reasoningBudgetSlider?.type === 'slider') { // if it's a slider + if (reasoningBudgetSlider?.type === 'budget_slider') { // if it's a slider const { min: min_, max, default: defaultVal } = reasoningBudgetSlider const nSteps = 8 // only used in calculating stepSize, stepSize is what actually matters @@ -186,7 +189,6 @@ const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) => const value = isReasoningEnabled ? voidSettingsState.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]?.reasoningBudget ?? defaultVal : valueIfOff - return
Thinking step={stepSize} value={value} onChange={(newVal) => { - const disabled = newVal === min && canTurnOffReasoning - voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !disabled, reasoningBudget: newVal }) + const isOff = canTurnOffReasoning && newVal === min + voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !isOff, reasoningBudget: newVal }) }} /> {isReasoningEnabled ? `${value} tokens` : 'Thinking disabled'}
} + if (reasoningBudgetSlider?.type === 'effort_slider') { + + const { values, default: defaultVal } = reasoningBudgetSlider + + const min = canTurnOffReasoning ? -1 : 0 + const max = values.length - 1 + + const valueIfOff = -1 + + const value = isReasoningEnabled ? + values.indexOf(voidSettingsState.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]?.reasoningEffort ?? defaultVal) + : valueIfOff + + return
+ Thinking + { + const isOff = canTurnOffReasoning && newVal === min + voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !isOff, reasoningBudget: newVal }) + }} + /> + {isReasoningEnabled ? `${value}` : 'Thinking disabled'} +
+ } + return null } diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx index bf8a3223..8c968c62 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx @@ -355,7 +355,7 @@ const TableOfModelsForProvider = ({ providerName }: { providerName: ProviderName const { showAsDefault, isDownloaded } = infoOfModelName[modelName] ?? {} - const capabilities = getModelCapabilities(providerName, modelName) + const capabilities = getModelCapabilities(providerName, modelName, undefined) const { downloadable, cost, diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index 20a4e9d0..e1e644ce 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react' +import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; // Added useRef import just in case it was missed, though likely already present import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidStatefulModelInfo, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, nonlocalProviderNames, localProviderNames, GlobalSettingName, featureNames, displayInfoOfFeatureName, isProviderNameDisabled, FeatureName, hasDownloadButtonsOnModelsProviderNames, subTextMdOfProviderName } from '../../../../common/voidSettingsTypes.js' import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' import { VoidButtonBgDarken, VoidCustomDropdownBox, VoidInputBox2, VoidSimpleInputBox, VoidSwitch } from '../util/inputs.js' @@ -18,6 +18,7 @@ import { os } from '../../../../common/helpers/systemInfo.js' import { IconLoading } from '../sidebar-tsx/SidebarChat.js' import { ToolApprovalType, toolApprovalTypes } from '../../../../common/toolsServiceTypes.js' import Severity from '../../../../../../../base/common/severity.js' +import { getModelCapabilities, ModelOverrides } from '../../../../common/modelCapabilities.js'; const ButtonLeftTextRightOption = ({ text, leftButton }: { text: string, leftButton?: React.ReactNode }) => { @@ -183,6 +184,197 @@ const ConfirmButton = ({ children, onConfirm, className }: { children: React.Rea ); }; +// ---------------- Simplified Model Settings Dialog ------------------ +// This new dialog replaces the verbose UI with a single JSON override box. +const SimpleModelSettingsDialog = ({ + isOpen, + onClose, + modelInfo, +}: { + isOpen: boolean; + onClose: () => void; + modelInfo: { modelName: string; providerName: ProviderName; type: 'autodetected' | 'custom' | 'default' } | null; +}) => { + if (!isOpen || !modelInfo) return null; + + const { modelName, providerName, type } = modelInfo; + const accessor = useAccessor(); + const settingsState = useSettingsState(); + const mouseDownInsideModal = useRef(false); // Ref to track mousedown origin + const settingsStateService = accessor.get('IVoidSettingsService'); + + // current overrides and defaults + const defaultModelCapabilities = getModelCapabilities(providerName, modelName, undefined); + const currentOverrides = settingsState.overridesOfModel?.[providerName]?.[modelName] ?? undefined; + const { modelName: recognizedModelName, isUnrecognizedModel } = defaultModelCapabilities + + // keys of ModelOverrides we allow the user to override + const allowedKeys: (string & (keyof ModelOverrides))[] = [ + 'contextWindow', + 'reservedOutputTokenSpace', + 'supportsSystemMessage', + 'specialToolFormat', + 'supportsFIM', + 'reasoningCapabilities', + ]; + + // Create the placeholder with the default values for allowed keys + const partialDefaults: Partial = {}; + for (const k of allowedKeys) { if (defaultModelCapabilities[k]) partialDefaults[k] = defaultModelCapabilities[k] as any; } + const placeholder = JSON.stringify(partialDefaults, null, 2); + + const [overrideEnabled, setOverrideEnabled] = useState(() => !!currentOverrides); + const [jsonText, setJsonText] = useState(() => currentOverrides ? JSON.stringify(currentOverrides, null, 2) : placeholder); + + const [readOnlyHeight, setReadOnlyHeight] = useState(undefined); + const [errorMsg, setErrorMsg] = useState(null); + + // reset when dialog toggles + useEffect(() => { + if (!isOpen) return; + const cur = settingsState.overridesOfModel?.[providerName]?.[modelName]; + setOverrideEnabled(!!cur); + // If there are overrides, show them; otherwise use default values + setJsonText(cur ? JSON.stringify(cur, null, 2) : placeholder); + setErrorMsg(null); + }, [isOpen, providerName, modelName, settingsState.overridesOfModel, placeholder]); + + const onSave = async () => { + + // if disabled override, reset overrides + if (!overrideEnabled) { + await settingsStateService.setOverridesOfModel(providerName, modelName, undefined); + onClose(); + return; + } + + // enabled overrides + // parse json + let parsedInput: Record + if (jsonText.trim()) { + try { + parsedInput = JSON.parse(jsonText); + } catch (e) { + setErrorMsg('Invalid JSON'); + return; + } + } else { + setErrorMsg('Invalid JSON'); + return; + } + + // only keep allowed keys + const cleaned: Partial = {}; + for (const k of allowedKeys) { + if (!(k in parsedInput)) continue + const isEmpty = parsedInput[k] === '' || parsedInput[k] === null || parsedInput[k] === undefined; + if (!isEmpty && (k in partialDefaults)) { + cleaned[k] = parsedInput[k] as any; + } + } + await settingsStateService.setOverridesOfModel(providerName, modelName, cleaned); + onClose(); + }; + + return ( +
{ + mouseDownInsideModal.current = false; + }} + onMouseUp={() => { + if (!mouseDownInsideModal.current) { + onClose(); + } + mouseDownInsideModal.current = false; + }} + > + {/* MODAL */} +
e.stopPropagation()} // Keep stopping propagation for normal clicks inside + onMouseDown={(e) => { + mouseDownInsideModal.current = true; + e.stopPropagation(); + }} + > +
+

+ Change Defaults for {modelName} ({displayInfoOfProviderName(providerName).title}) +

+ +
+ + {/* Display model recognition status */} +
+ {type === 'default' ? `${modelName} comes packaged with Void, so you shouldn't need to change these settings.` + : isUnrecognizedModel + ? `Model not recognized by Void.` + : `Void knows about this model ("${recognizedModelName}")!`} +
+ + + {/* override toggle */} +
+ + Override Model Defaults +
+ + + + {overrideEnabled ? ( + + ) : ( +