From 398a8a1934a5511924ffdb0a4742d49378ca687f Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 5 May 2025 00:50:33 -0700 Subject: [PATCH] misc + model overrides --- .voidrules | 4 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 1 + .../contrib/void/browser/sidebarActions.ts | 2 +- .../contrib/void/common/modelCapabilities.ts | 111 ++++++++++-------- .../void/common/voidSettingsService.ts | 20 ++-- .../contrib/void/common/voidSettingsTypes.ts | 4 +- .../llmMessage/sendLLMMessage.impl.ts | 2 +- 7 files changed, 77 insertions(+), 67 deletions(-) diff --git a/.voidrules b/.voidrules index 08826ef2..8cded830 100644 --- a/.voidrules +++ b/.voidrules @@ -3,4 +3,6 @@ This is a fork of the VSCode repo called Void. Most code we care about lives in src/vs/workbench/contrib/void. You may often need to explore the full repo to find relevant parts of code. -Look for services, and built-in functions that you might need to use to solve the problem. \ No newline at end of file +Look for services, and built-in functions that you might need to use to solve the problem. + +NEVER lazily cast to 'any' in typescript. Find the correct type to apply and use it. 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 92b7d20e..7f11ff15 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 @@ -728,6 +728,7 @@ const ToolHeaderWrapper = ({
{/* left */}
{ const commandService = accessor.get(ICommandService) await commandService.executeCommand(VOID_OPEN_SIDEBAR_ACTION_ID) - await commandService.executeCommand(VOID_ADD_SELECTION_TO_SIDEBAR_ACTION_ID) + // await commandService.executeCommand(VOID_ADD_SELECTION_TO_SIDEBAR_ACTION_ID) } }) diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 455b9e78..8668f830 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -140,42 +140,46 @@ export const defaultModelsOfProvider = { export type VoidStaticModelInfo = { // not stateful - contextWindow: number; // input tokens - reservedOutputTokenSpace: number | null; // output tokens, defaults to 4092 - cost: { // <-- UNUSED + cost: { // just informative, not used in sending / receiving input: number; output: number; cache_read?: number; cache_write?: number; } - downloadable: false | { + downloadable: false | { // just informative, not used in sending / receiving sizeGb: number | 'not-known' } + contextWindow: number; // input tokens + reservedOutputTokenSpace: number | null; // reserve this much space in the context window for output, defaults to 4092 if null + supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated'; // separated = anthropic where "system" is a special paramete specialToolFormat?: 'openai-style' | 'anthropic-style' | 'gemini-style', // null defaults to XML supportsFIM: boolean; + // reasoning options if supports reasoning reasoningCapabilities: false | { - readonly supportsReasoning: true; - // reasoning options if supports reasoning + readonly supportsReasoning: true; // this must be true for clarity 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 reasoningReservedOutputTokenSpace?: number; // overrides normal reservedOutputTokenSpace - readonly reasoningBudgetSlider?: { type: 'slider'; min: number; max: number; default: number }; + readonly reasoningBudgetSlider?: + | undefined + | { type: 'number_slider'; min: number; max: number; default: number } // anthropic only supports this + | { type: 'string_slider'; values: string[]; default: string } // openai-compatible only supports this // options related specifically to model output - // you are allowed to not include openSourceThinkTags if it's not open source (no such cases as of writing) - // if it's open source, put the think tags here so we parse them out in e.g. ollama + // if it's open source, put the think tags here and we'll parse them out in e.g. ollama readonly openSourceThinkTags?: [string, string]; }; } -export type ModelOverrideOptions = Partial> +> + @@ -343,9 +347,12 @@ const extensiveModelFallback: VoidStaticProviderInfo['modelOptionsFallback'] = ( const lower = modelName.toLowerCase() - const toFallback = (opts: Omit): VoidStaticModelInfo & { modelName: string } => { + const toFallback = },>(obj: T, recognizedModelName: string & keyof T) + : VoidStaticModelInfo & { modelName: string } => { + + const opts = obj[recognizedModelName] return { - modelName, + modelName: recognizedModelName, ...opts, supportsSystemMessage: opts.supportsSystemMessage ? 'system-role' : false, cost: { input: 0, output: 0 }, @@ -353,58 +360,58 @@ const extensiveModelFallback: VoidStaticProviderInfo['modelOptionsFallback'] = ( ...fallbackKnownValues } } - if (lower.includes('gemini') && (lower.includes('2.5') || lower.includes('2-5'))) return toFallback(geminiModelOptions['gemini-2.5-pro-exp-03-25']) + if (lower.includes('gemini') && (lower.includes('2.5') || lower.includes('2-5'))) return toFallback(geminiModelOptions, 'gemini-2.5-pro-exp-03-25') - if (lower.includes('claude-3-5') || lower.includes('claude-3.5')) return toFallback(anthropicModelOptions['claude-3-5-sonnet-20241022']) - if (lower.includes('claude')) return toFallback(anthropicModelOptions['claude-3-7-sonnet-20250219']) + if (lower.includes('claude-3-5') || lower.includes('claude-3.5')) return toFallback(anthropicModelOptions, 'claude-3-5-sonnet-20241022') + if (lower.includes('claude')) return toFallback(anthropicModelOptions, 'claude-3-7-sonnet-20250219') - if (lower.includes('grok')) return toFallback(xAIModelOptions['grok-2']) + if (lower.includes('grok')) return toFallback(xAIModelOptions, 'grok-2') - if (lower.includes('deepseek-r1') || lower.includes('deepseek-reasoner')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.deepseekR1 }) - if (lower.includes('deepseek') && lower.includes('v2')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.deepseekCoderV2 }) - if (lower.includes('deepseek')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.deepseekCoderV3 }) + if (lower.includes('deepseek-r1') || lower.includes('deepseek-reasoner')) return toFallback(openSourceModelOptions_assumingOAICompat, 'deepseekR1') + if (lower.includes('deepseek') && lower.includes('v2')) return toFallback(openSourceModelOptions_assumingOAICompat, 'deepseekCoderV2') + if (lower.includes('deepseek')) return toFallback(openSourceModelOptions_assumingOAICompat, 'deepseekCoderV3') - if (lower.includes('llama3')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.llama3, }) - if (lower.includes('llama3.1')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['llama3.1'], }) - if (lower.includes('llama3.2')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['llama3.2'], }) - if (lower.includes('llama3.3')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['llama3.3'], }) - if (lower.includes('llama') || lower.includes('scout')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['llama4-scout'] }) - if (lower.includes('llama') || lower.includes('maverick')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['llama4-scout'] }) - if (lower.includes('llama')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['llama4-scout'] }) + if (lower.includes('llama3')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama3') + if (lower.includes('llama3.1')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama3.1') + if (lower.includes('llama3.2')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama3.2') + if (lower.includes('llama3.3')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama3.3') + if (lower.includes('llama') || lower.includes('scout')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama4-scout') + if (lower.includes('llama') || lower.includes('maverick')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama4-scout') + if (lower.includes('llama')) return toFallback(openSourceModelOptions_assumingOAICompat, 'llama4-scout') - if (lower.includes('qwen') && lower.includes('2.5') && lower.includes('coder')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['qwen2.5coder'] }) - if (lower.includes('qwen') && lower.includes('3')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['qwen3'] }) - if (lower.includes('qwen')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['qwen3'] }) - if (lower.includes('qwq')) { return toFallback({ ...openSourceModelOptions_assumingOAICompat.qwq, }) } - if (lower.includes('phi4')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.phi4, }) - if (lower.includes('codestral')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.codestral }) + if (lower.includes('qwen') && lower.includes('2.5') && lower.includes('coder')) return toFallback(openSourceModelOptions_assumingOAICompat, 'qwen2.5coder') + if (lower.includes('qwen') && lower.includes('3')) return toFallback(openSourceModelOptions_assumingOAICompat, 'qwen3') + if (lower.includes('qwen')) return toFallback(openSourceModelOptions_assumingOAICompat, 'qwen3') + if (lower.includes('qwq')) { return toFallback(openSourceModelOptions_assumingOAICompat, 'qwq') } + if (lower.includes('phi4')) return toFallback(openSourceModelOptions_assumingOAICompat, 'phi4') + if (lower.includes('codestral')) return toFallback(openSourceModelOptions_assumingOAICompat, 'codestral') - if (lower.includes('gemma')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.gemma, }) + if (lower.includes('gemma')) return toFallback(openSourceModelOptions_assumingOAICompat, 'gemma') - if (lower.includes('starcoder2')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.starcoder2, }) + if (lower.includes('starcoder2')) return toFallback(openSourceModelOptions_assumingOAICompat, 'starcoder2') - if (lower.includes('openhands')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['openhands-lm-32b'], }) // max output unclear + if (lower.includes('openhands')) return toFallback(openSourceModelOptions_assumingOAICompat, 'openhands-lm-32b') // max output uncler - if (lower.includes('quasar') || lower.includes('quaser')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['quasar'] }) + if (lower.includes('quasar') || lower.includes('quaser')) return toFallback(openSourceModelOptions_assumingOAICompat, 'quasar') - if (lower.includes('gpt') && lower.includes('mini') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions['gpt-4.1-mini']) - if (lower.includes('gpt') && lower.includes('nano') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions['gpt-4.1-nano']) - if (lower.includes('gpt') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions['gpt-4.1']) + if (lower.includes('gpt') && lower.includes('mini') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions, 'gpt-4.1-mini') + if (lower.includes('gpt') && lower.includes('nano') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions, 'gpt-4.1-nano') + if (lower.includes('gpt') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions, 'gpt-4.1') - if (lower.includes('4o') && lower.includes('mini')) return toFallback(openAIModelOptions['gpt-4o-mini']) - if (lower.includes('4o')) return toFallback(openAIModelOptions['gpt-4o']) + if (lower.includes('4o') && lower.includes('mini')) return toFallback(openAIModelOptions, 'gpt-4o-mini') + if (lower.includes('4o')) return toFallback(openAIModelOptions, 'gpt-4o') - if (lower.includes('o1') && lower.includes('mini')) return toFallback(openAIModelOptions['o1-mini']) - if (lower.includes('o1')) return toFallback(openAIModelOptions['o1']) - if (lower.includes('o3') && lower.includes('mini')) return toFallback(openAIModelOptions['o3-mini']) - if (lower.includes('o3')) return toFallback(openAIModelOptions['o3']) - if (lower.includes('o4') && lower.includes('mini')) return toFallback(openAIModelOptions['o4-mini']) + if (lower.includes('o1') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o1-mini') + if (lower.includes('o1')) return toFallback(openAIModelOptions, 'o1') + if (lower.includes('o3') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o3-mini') + if (lower.includes('o3')) return toFallback(openAIModelOptions, 'o3') + if (lower.includes('o4') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o4-mini') if (Object.keys(openSourceModelOptions_assumingOAICompat).map(k => k.toLowerCase()).includes(lower)) - return toFallback(openSourceModelOptions_assumingOAICompat[lower as keyof typeof openSourceModelOptions_assumingOAICompat]) + return toFallback(openSourceModelOptions_assumingOAICompat, lower as keyof typeof openSourceModelOptions_assumingOAICompat) - return toFallback(defaultModelOptions) + return null } @@ -427,7 +434,7 @@ const anthropicModelOptions = { canTurnOffReasoning: true, canIOReasoning: true, reasoningReservedOutputTokenSpace: 64_000, // can bump it to 128_000 with beta mode output-128k-2025-02-19 - reasoningBudgetSlider: { type: 'slider', min: 1024, max: 32_000, default: 1024 }, // they recommend batching if max > 32_000 + reasoningBudgetSlider: { type: 'number_slider', min: 1024, max: 32_000, default: 1024 }, // they recommend batching if max > 32_000 }, }, @@ -1039,7 +1046,7 @@ const openRouterModelOptions_assumingOpenAICompat = { canTurnOffReasoning: false, canIOReasoning: true, reasoningReservedOutputTokenSpace: 64_000, - reasoningBudgetSlider: { type: 'slider', min: 1024, max: 32_000, default: 1024 }, // they recommend batching if max > 32_000 + reasoningBudgetSlider: { type: 'number_slider', min: 1024, max: 32_000, default: 1024 }, // they recommend batching if max > 32_000 }, }, 'anthropic/claude-3.7-sonnet': { @@ -1224,7 +1231,7 @@ export const getSendableReasoningInfo = ( if (!isReasoningEnabled) return null // check for reasoning budget - const reasoningBudget = reasoningBudgetSlider?.type === 'slider' ? modelSelectionOptions?.reasoningBudget ?? reasoningBudgetSlider?.default : undefined + const reasoningBudget = reasoningBudgetSlider?.type === 'number_slider' ? modelSelectionOptions?.reasoningBudget ?? reasoningBudgetSlider?.default : undefined if (reasoningBudget) { return { type: 'budgetEnabled', isReasoningEnabled: isReasoningEnabled, reasoningBudget: reasoningBudget } } diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index f8081b9c..91847fec 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -11,7 +11,7 @@ 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, ModelOverrideOptions } from './modelCapabilities.js'; +import { defaultProviderSettings, getModelCapabilities, ModelOverrides } 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, OverridesOfModel, defaultOverridesOfModel } from './voidSettingsTypes.js'; @@ -62,7 +62,9 @@ export interface IVoidSettingsService { setModelSelectionOfFeature: SetModelSelectionOfFeatureFn; setOptionsOfModelSelection: SetOptionsOfModelSelection; setGlobalSetting: SetGlobalSettingFn; - setOverridesOfModel(providerName: ProviderName, modelName: string, overrides: ModelOverrideOptions): Promise; + + // setting to undefined CLEARS it, unlike others: + setOverridesOfModel(providerName: ProviderName, modelName: string, overrides: Partial | undefined): Promise; dangerousSetState(newState: VoidSettingsState): Promise; resetState(): Promise; @@ -438,19 +440,17 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { this._onDidChangeState.fire() } - setOverridesOfModel = async (providerName: ProviderName, modelName: string, overrides: ModelOverrideOptions) => { - const currentProviderSettings = this.state.overridesOfModel[providerName] || {}; - + setOverridesOfModel = async (providerName: ProviderName, modelName: string, overrides: Partial | undefined) => { const newState: VoidSettingsState = { ...this.state, overridesOfModel: { ...this.state.overridesOfModel, [providerName]: { - ...currentProviderSettings, - [modelName]: { - ...currentProviderSettings[modelName], + ...this.state.overridesOfModel[providerName], + [modelName]: overrides === undefined ? undefined : { + ...this.state.overridesOfModel[providerName][modelName], ...overrides - } + }, } } }; @@ -459,7 +459,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { await this._storeState(); this._onDidChangeState.fire(); - this._metricsService.capture('Update Model Settings', { providerName, modelName, overrides }); + this._metricsService.capture('Update Model Overrides', { providerName, modelName, overrides }); } diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 43fc9a24..187660dd 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -4,7 +4,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { defaultModelsOfProvider, defaultProviderSettings, ModelOverrideOptions } from './modelCapabilities.js'; +import { defaultModelsOfProvider, defaultProviderSettings, ModelOverrides } from './modelCapabilities.js'; import { ToolApprovalType } from './toolsServiceTypes.js'; import { VoidSettingsState } from './voidSettingsService.js' @@ -482,7 +482,7 @@ export type OptionsOfModelSelection = { export type OverridesOfModel = { [providerName in ProviderName]: { - [modelName: string]: ModelOverrideOptions | undefined + [modelName: string]: Partial | undefined } } diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 4cad7d20..feb18b1b 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -241,7 +241,7 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE const { providerReasoningIOSettings } = getProviderCapabilities(providerName) // reasoning - const { canIOReasoning, openSourceThinkTags, } = reasoningCapabilities || {} + const { canIOReasoning, openSourceThinkTags } = reasoningCapabilities || {} const reasoningInfo = getSendableReasoningInfo('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) // user's modelName_ here const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}