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 bac48dda..b6090aba 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
@@ -24,13 +24,11 @@ import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js';
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
import { ChatMode, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
-import { getModelSelectionState, getModelCapabilities } from '../../../../common/modelCapabilities.js';
+import { getModelCapabilities, getIsResoningEnabledState } from '../../../../common/modelCapabilities.js';
import { AlertTriangle, Ban, ChevronRight, Dot, Pencil, X } from 'lucide-react';
import { ChatMessage, StagingSelectionItem, ToolMessage, ToolRequestApproval } from '../../../../common/chatThreadServiceTypes.js';
-import { ResolveReason, ToolCallParams, ToolName, ToolNameWithApproval } from '../../../../common/toolsServiceTypes.js';
+import { ToolCallParams, ToolName, ToolNameWithApproval } from '../../../../common/toolsServiceTypes.js';
import { JumpToFileButton, useApplyButtonHTML } from '../markdown/ApplyBlockHoverButtons.js';
-import { DiffZone } from '../../../editCodeService.js';
-import { ScrollType } from '../../../../../../../editor/common/editorCommon.js';
import { IsRunningType } from '../../../chatThreadService.js';
@@ -160,11 +158,12 @@ const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) =>
if (!modelSelection) return null
const { modelName, providerName } = modelSelection
- const { canToggleReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName).supportsReasoning || {}
+ const { reasoningCapabilities } = getModelCapabilities(providerName, modelName)
+ const { canTurnOffReasoning, reasoningBudgetSlider } = reasoningCapabilities || {}
- const { isReasoningEnabled } = getModelSelectionState(providerName, modelName, voidSettingsState.optionsOfModelSelection[providerName]?.[modelName])
-
- if (canToggleReasoning && !reasoningBudgetSlider) { // if it's just a on/off toggle without a power slider (no models right now)
+ const modelSelectionOptions = voidSettingsState.optionsOfModelSelection[providerName]?.[modelName]
+ const isReasoningEnabled = getIsResoningEnabledState(providerName, modelName, modelSelectionOptions)
+ 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'}
@@ -183,7 +182,7 @@ const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) =>
const nSteps = 8 // only used in calculating stepSize, stepSize is what actually matters
const stepSize = Math.round((max - min_) / nSteps)
- const min = canToggleReasoning ? min_ - stepSize : min_
+ const min = canTurnOffReasoning ? min_ - stepSize : min_
return
Thinking
@@ -195,7 +194,7 @@ const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) =>
step={stepSize}
value={value}
onChange={(newVal) => {
- const disabled = newVal === min && canToggleReasoning
+ const disabled = newVal === min && canTurnOffReasoning
voidSettingsService.setOptionsOfModelSelection(modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !disabled, reasoningBudget: newVal })
}}
/>
diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx
index 36d13173..be9fb5d2 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/void-command-bar-tsx/VoidCommandBar.tsx
@@ -185,10 +185,12 @@ const VoidCommandBar = ({ uri, editor }: { uri: URI | null, editor: ICodeEditor
- File {(currUriIdx ?? 0) + 1} of {sortedCommandBarURIs.length}
+ {`File ${(currUriIdx ?? 0) + 1} of ${sortedCommandBarURIs.length}`}
- Diff {(currDiffIdx ?? 0) + 1} of {sortedDiffIds?.length ?? 0}
+ {sortedDiffIds?.length ?? 0 === 0 ?
+ '(No changes)'
+ : `Diff ${(currDiffIdx ?? 0) + 1} of ${sortedDiffIds?.length ?? 0}`}
diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts
index 95de9604..88a45939 100644
--- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts
+++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts
@@ -40,6 +40,8 @@ export const defaultModelsOfProvider = {
vLLM: [ // autodetected
],
openRouter: [ // https://openrouter.ai/models
+ 'anthropic/claude-3.7-sonnet:thinking',
+ 'anthropic/claude-3.7-sonnet',
'anthropic/claude-3.5-sonnet',
'deepseek/deepseek-r1',
'mistralai/codestral-2501',
@@ -79,10 +81,11 @@ type ModelOptions = {
supportsTools: false | 'anthropic-style' | 'openai-style';
supportsFIM: boolean;
- supportsReasoning: false | {
+ reasoningCapabilities: false | {
+ readonly supportsReasoning: true;
// reasoning options if supports reasoning
- readonly canToggleReasoning: 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
+ 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 reasoningBudgetSlider?: { type: 'slider'; min: number; max: number; default: number };
@@ -95,7 +98,7 @@ type ModelOptions = {
type ProviderReasoningIOSettings = {
// include this in payload to get reasoning
- input?: { includeInPayload?: { [key: string]: any }, };
+ input?: { includeInPayload?: (reasoningState: SendableReasoningInfo) => null | { [key: string]: any }, };
// nameOfFieldInDelta: reasoning output is in response.choices[0].delta[deltaReasoningField]
// needsManualParse: whether we must manually parse out the
tags
output?:
@@ -118,7 +121,7 @@ const modelOptionsDefaults: ModelOptions = {
supportsSystemMessage: false,
supportsTools: false,
supportsFIM: false,
- supportsReasoning: false,
+ reasoningCapabilities: false,
}
@@ -127,70 +130,70 @@ const openSourceModelOptions_assumingOAICompat = {
supportsFIM: false,
supportsSystemMessage: false,
supportsTools: false,
- supportsReasoning: { canToggleReasoning: false, canIOReasoning: true, openSourceThinkTags: ['', ''] },
+ reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: true, openSourceThinkTags: ['', ''] },
},
'deepseekCoderV2': {
supportsFIM: false,
supportsSystemMessage: false, // unstable
supportsTools: false,
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'codestral': {
supportsFIM: true,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
// llama
'llama3': {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'llama3.1': {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'llama3.2': {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'llama3.3': {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
// qwen
'qwen2.5coder': {
supportsFIM: true,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'qwq': {
supportsFIM: false, // no FIM, yes reasoning
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: { canToggleReasoning: false, canIOReasoning: true, openSourceThinkTags: ['', ''] },
+ reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: true, openSourceThinkTags: ['', ''] },
},
// FIM only
'starcoder2': {
supportsFIM: true,
supportsSystemMessage: false,
supportsTools: false,
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'codegemma:2b': {
supportsFIM: true,
supportsSystemMessage: false,
supportsTools: false,
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
} as const satisfies { [s: string]: Partial }
@@ -233,8 +236,9 @@ const anthropicModelOptions = {
supportsFIM: false,
supportsSystemMessage: 'separated',
supportsTools: 'anthropic-style',
- supportsReasoning: {
- canToggleReasoning: true,
+ reasoningCapabilities: {
+ supportsReasoning: true,
+ canTurnOffReasoning: true,
canIOReasoning: true,
reasoningMaxOutputTokens: 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
@@ -247,7 +251,7 @@ const anthropicModelOptions = {
supportsFIM: false,
supportsSystemMessage: 'separated',
supportsTools: 'anthropic-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'claude-3-5-haiku-20241022': {
contextWindow: 200_000,
@@ -256,7 +260,7 @@ const anthropicModelOptions = {
supportsFIM: false,
supportsSystemMessage: 'separated',
supportsTools: 'anthropic-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'claude-3-opus-20240229': {
contextWindow: 200_000,
@@ -265,7 +269,7 @@ const anthropicModelOptions = {
supportsFIM: false,
supportsSystemMessage: 'separated',
supportsTools: 'anthropic-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'claude-3-sonnet-20240229': { // no point of using this, but including this for people who put it in
contextWindow: 200_000, cost: { input: 3.00, output: 15.00 },
@@ -273,11 +277,21 @@ const anthropicModelOptions = {
supportsFIM: false,
supportsSystemMessage: 'separated',
supportsTools: 'anthropic-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
}
} as const satisfies { [s: string]: ModelOptions }
const anthropicSettings: ProviderSettings = {
+ providerReasoningIOSettings: {
+ input: {
+ includeInPayload: (reasoningInfo) => {
+ if (reasoningInfo?.type === 'budgetEnabled') {
+ return { thinking: { type: 'enabled', budget_tokens: reasoningInfo.reasoningBudget } }
+ }
+ return null
+ }
+ },
+ },
modelOptions: anthropicModelOptions,
modelOptionsFallback: (modelName) => {
let fallbackName: keyof typeof anthropicModelOptions | null = null
@@ -288,7 +302,7 @@ const anthropicSettings: ProviderSettings = {
if (modelName.includes('claude-3-sonnet')) fallbackName = 'claude-3-sonnet-20240229'
if (fallbackName) return { modelName: fallbackName, ...anthropicModelOptions[fallbackName] }
return { modelName, ...modelOptionsDefaults, maxOutputTokens: 4_096 }
- }
+ },
}
@@ -301,7 +315,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing
supportsFIM: false,
supportsTools: false,
supportsSystemMessage: 'developer-role',
- supportsReasoning: { canIOReasoning: false, canToggleReasoning: false }, // it doesn't actually output reasoning, but our logic is fine with it
+ reasoningCapabilities: { supportsReasoning: true, canIOReasoning: false, canTurnOffReasoning: false }, // it doesn't actually output reasoning, but our logic is fine with it
},
'o3-mini': {
contextWindow: 200_000,
@@ -310,7 +324,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing
supportsFIM: false,
supportsTools: false,
supportsSystemMessage: 'developer-role',
- supportsReasoning: { canIOReasoning: false, canToggleReasoning: false },
+ reasoningCapabilities: { supportsReasoning: true, canIOReasoning: false, canTurnOffReasoning: false },
},
'gpt-4o': {
contextWindow: 128_000,
@@ -319,7 +333,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing
supportsFIM: false,
supportsTools: 'openai-style',
supportsSystemMessage: 'system-role',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'o1-mini': {
contextWindow: 128_000,
@@ -328,7 +342,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing
supportsFIM: false,
supportsTools: false,
supportsSystemMessage: false, // does not support any system
- supportsReasoning: { canIOReasoning: false, canToggleReasoning: false },
+ reasoningCapabilities: { supportsReasoning: true, canIOReasoning: false, canTurnOffReasoning: false },
},
'gpt-4o-mini': {
contextWindow: 128_000,
@@ -337,7 +351,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing
supportsFIM: false,
supportsTools: 'openai-style',
supportsSystemMessage: 'system-role', // ??
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
} as const satisfies { [s: string]: ModelOptions }
@@ -363,7 +377,7 @@ const xAIModelOptions = {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
} as const satisfies { [s: string]: ModelOptions }
@@ -387,7 +401,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style', // we are assuming OpenAI SDK when calling gemini
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'gemini-2.0-flash-lite-preview-02-05': {
contextWindow: 1_048_576,
@@ -396,7 +410,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'gemini-1.5-flash': {
contextWindow: 1_048_576,
@@ -405,7 +419,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'gemini-1.5-pro': {
contextWindow: 2_097_152,
@@ -414,7 +428,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'gemini-1.5-flash-8b': {
contextWindow: 1_048_576,
@@ -423,7 +437,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
} as const satisfies { [s: string]: ModelOptions }
@@ -469,7 +483,7 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'llama-3.1-8b-instant': {
contextWindow: 128_000,
@@ -478,7 +492,7 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'qwen-2.5-coder-32b': {
contextWindow: 128_000,
@@ -487,7 +501,7 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq
supportsFIM: false, // unfortunately looks like no FIM support on groq
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'qwen-qwq-32b': { // https://huggingface.co/Qwen/QwQ-32B
contextWindow: 128_000,
@@ -496,11 +510,21 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: { canIOReasoning: true, canToggleReasoning: false, openSourceThinkTags: ['', ''] }, // we're using reasoning_format:parsed so really don't need to know openSourceThinkTags
+ reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false, openSourceThinkTags: ['', ''] }, // we're using reasoning_format:parsed so really don't need to know openSourceThinkTags
},
} as const satisfies { [s: string]: ModelOptions }
const groqSettings: ProviderSettings = {
- providerReasoningIOSettings: { input: { includeInPayload: { reasoning_format: 'parsed' } }, output: { nameOfFieldInDelta: 'reasoning' }, }, // Must be set to either parsed or hidden when using tool calling https://console.groq.com/docs/reasoning
+ providerReasoningIOSettings: {
+ input: {
+ includeInPayload: (reasoningInfo) => {
+ if (reasoningInfo?.type === 'budgetEnabled') {
+ return { reasoning_format: 'parsed' }
+ }
+ return null
+ }
+ },
+ output: { nameOfFieldInDelta: 'reasoning' },
+ }, // Must be set to either parsed or hidden when using tool calling https://console.groq.com/docs/reasoning
modelOptions: groqModelOptions,
modelOptionsFallback: (modelName) => { return null }
}
@@ -536,6 +560,21 @@ const openRouterModelOptions_assumingOpenAICompat = {
maxOutputTokens: null,
cost: { input: 0.8, output: 2.4 },
},
+ 'anthropic/claude-3.7-sonnet:thinking': {
+ contextWindow: 200_000,
+ maxOutputTokens: null,
+ cost: { input: 3.00, output: 15.00 },
+ supportsFIM: false,
+ supportsSystemMessage: 'system-role',
+ supportsTools: 'openai-style',
+ reasoningCapabilities: { // same as anthropic, see above
+ supportsReasoning: true,
+ canTurnOffReasoning: false,
+ canIOReasoning: true,
+ reasoningMaxOutputTokens: 64_000,
+ reasoningBudgetSlider: { type: 'slider', min: 1024, max: 32_000, default: 1024 }, // they recommend batching if max > 32_000
+ },
+ },
'anthropic/claude-3.7-sonnet': {
contextWindow: 200_000,
maxOutputTokens: null,
@@ -543,7 +582,7 @@ const openRouterModelOptions_assumingOpenAICompat = {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: { canIOReasoning: true, canToggleReasoning: false }, // TODO!!! false for now
+ reasoningCapabilities: false, // stupidly, openrouter separates thinking from non-thinking
},
'anthropic/claude-3.5-sonnet': {
contextWindow: 200_000,
@@ -552,7 +591,7 @@ const openRouterModelOptions_assumingOpenAICompat = {
supportsFIM: false,
supportsSystemMessage: 'system-role',
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'mistralai/codestral-2501': {
...openSourceModelOptions_assumingOAICompat.codestral,
@@ -560,7 +599,7 @@ const openRouterModelOptions_assumingOpenAICompat = {
maxOutputTokens: null,
cost: { input: 0.3, output: 0.9 },
supportsTools: 'openai-style',
- supportsReasoning: false,
+ reasoningCapabilities: false,
},
'qwen/qwen-2.5-coder-32b-instruct': {
...openSourceModelOptions_assumingOAICompat['qwen2.5coder'],
@@ -581,7 +620,18 @@ const openRouterModelOptions_assumingOpenAICompat = {
const openRouterSettings: ProviderSettings = {
// reasoning: OAICompat + response.choices[0].delta.reasoning : payload should have {include_reasoning: true} https://openrouter.ai/announcements/reasoning-tokens-for-thinking-models
providerReasoningIOSettings: {
- input: { includeInPayload: { include_reasoning: true } },
+ input: {
+ includeInPayload: (reasoningInfo) => {
+ if (reasoningInfo?.type === 'budgetEnabled') {
+ return {
+ reasoning: {
+ max_tokens: reasoningInfo.reasoningBudget
+ }
+ }
+ }
+ return null
+ }
+ },
output: { nameOfFieldInDelta: 'reasoning' },
},
modelOptions: openRouterModelOptions_assumingOpenAICompat,
@@ -632,12 +682,45 @@ export const getProviderCapabilities = (providerName: ProviderName) => {
return { providerReasoningIOSettings }
}
-// state from optionsOfModelSelection
-export const getModelSelectionState = (providerName: ProviderName, modelName: string, modelSelectionOptions: ModelSelectionOptions | undefined): { isReasoningEnabled: boolean, reasoningBudget: number | undefined } => {
- const { canToggleReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName).supportsReasoning || {}
- const defaultEnabledVal = canToggleReasoning ? true : false
+export type SendableReasoningInfo = {
+ type: 'budgetEnabled',
+ isReasoningEnabled: true,
+ reasoningBudget: number,
+} | null
+
+
+
+export const getIsResoningEnabledState = (
+ providerName: ProviderName,
+ modelName: string,
+ modelSelectionOptions: ModelSelectionOptions | undefined,
+) => {
+ const { supportsReasoning } = getModelCapabilities(providerName, modelName).reasoningCapabilities || {}
+ if (!supportsReasoning) return false
+
+ const defaultEnabledVal = true // if can't toggle reasoning, then this must be true. just true as default
const isReasoningEnabled = modelSelectionOptions?.reasoningEnabled ?? defaultEnabledVal
- const reasoningBudget = reasoningBudgetSlider?.type === 'slider' ? modelSelectionOptions?.reasoningBudget ?? reasoningBudgetSlider?.default : undefined
- return { isReasoningEnabled, reasoningBudget }
+ return isReasoningEnabled
+}
+
+
+// used to force reasoning state (complex) into something simple we can just read from when sending a message
+export const getSendableReasoningInfo = (
+ providerName: ProviderName,
+ modelName: string,
+ modelSelectionOptions: ModelSelectionOptions | undefined,
+): SendableReasoningInfo => {
+
+ const { canIOReasoning, reasoningBudgetSlider } = getModelCapabilities(providerName, modelName).reasoningCapabilities || {}
+ if (!canIOReasoning) return null
+ const isReasoningEnabled = getIsResoningEnabledState(providerName, modelName, modelSelectionOptions)
+ if (!isReasoningEnabled) return null
+
+ // check for reasoning budget
+ const reasoningBudget = reasoningBudgetSlider?.type === 'slider' ? modelSelectionOptions?.reasoningBudget ?? reasoningBudgetSlider?.default : undefined
+ if (reasoningBudget) {
+ return { type: 'budgetEnabled', isReasoningEnabled: isReasoningEnabled, reasoningBudget: reasoningBudget }
+ }
+ return null
}
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 a34535bf..2b23c0cb 100644
--- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts
+++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts
@@ -11,7 +11,7 @@ import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '.
import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js';
import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js';
import { prepareFIMMessage, prepareMessages } from './preprocessLLMMessages.js';
-import { getModelSelectionState, getModelCapabilities, getProviderCapabilities } from '../../common/modelCapabilities.js';
+import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities } from '../../common/modelCapabilities.js';
import { InternalToolInfo, ToolName, isAToolName } from '../../common/toolsServiceTypes.js';
@@ -150,32 +150,41 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError
-const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, tools: tools_ }: SendChatParams_Internal) => {
+const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, modelName: modelName_, _setAborter, providerName, aiInstructions, tools: tools_ }: SendChatParams_Internal) => {
const {
modelName,
- supportsReasoning,
supportsSystemMessage,
supportsTools,
- // maxOutputTokens, right now we are ignoring this
+ // maxOutputTokens,
+ reasoningCapabilities,
} = getModelCapabilities(providerName, modelName_)
- const {
- canIOReasoning,
- openSourceThinkTags,
- } = supportsReasoning || {}
-
-
const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
- const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: false })
+ // reasoning
+ const { canIOReasoning, openSourceThinkTags, } = reasoningCapabilities || {}
+ const reasoningInfo = getSendableReasoningInfo(providerName, modelName_, modelSelectionOptions) // user's modelName_ here
+ const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
+
+ // tools
const tools = (supportsTools && ((tools_?.length ?? 0) !== 0)) ? tools_?.map(tool => toOpenAICompatibleTool(tool)) : undefined
-
- const includeInPayload = canIOReasoning ? providerReasoningIOSettings?.input?.includeInPayload || {} : {}
-
const toolsObj = tools ? { tools: tools, tool_choice: 'auto', parallel_tool_calls: false, } as const : {}
- const openai: OpenAI = newOpenAICompatibleSDK({ providerName, settingsOfProvider, includeInPayload })
- const options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { model: modelName, messages: messages, stream: true, ...toolsObj, }
+ // max tokens
+ // const maxTokens = reasoningInfo?.isReasoningEnabled && reasoningCapabilities ? reasoningCapabilities.reasoningMaxOutputTokens : maxOutputTokens
+
+ // instance
+ const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: false })
+ const openai: OpenAI = newOpenAICompatibleSDK({ providerName, settingsOfProvider, includeInPayload })
+ const options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
+ model: modelName,
+ messages: messages,
+ stream: true,
+ // max_completion_tokens: maxTokens,
+ ...toolsObj,
+ }
+
+ // open source models - manually parse think tokens
const { needsManualParse: needsManualReasoningParse, nameOfFieldInDelta: nameOfReasoningFieldInDelta } = providerReasoningIOSettings?.output ?? {}
const manuallyParseReasoning = needsManualReasoningParse && canIOReasoning && openSourceThinkTags
if (manuallyParseReasoning) {
@@ -300,31 +309,32 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM
supportsSystemMessage,
supportsTools,
maxOutputTokens,
- supportsReasoning,
+ reasoningCapabilities,
} = getModelCapabilities(providerName, modelName_)
- const {
- isReasoningEnabled,
- reasoningBudget,
- } = getModelSelectionState(providerName, modelName_, modelSelectionOptions) // user's modelName_ here
-
- const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: true })
const thisConfig = settingsOfProvider.anthropic
- const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
+ const { providerReasoningIOSettings } = getProviderCapabilities(providerName)
+
+ // reasoning
+ const reasoningInfo = getSendableReasoningInfo(providerName, modelName_, modelSelectionOptions) // user's modelName_ here
+ const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}
+
+ // tools
const tools = ((tools_?.length ?? 0) !== 0) ? tools_?.map(tool => toAnthropicTool(tool)) : undefined
-
-
const toolsObj: Partial = tools ? {
tools: tools,
tool_choice: { type: 'auto', disable_parallel_tool_use: true } // one tool at a time
} : {}
+ // anthropic-specific - max tokens
+ const maxTokens = reasoningInfo?.isReasoningEnabled && reasoningCapabilities ? reasoningCapabilities.reasoningMaxOutputTokens : maxOutputTokens
- const enableThinking = supportsReasoning && isReasoningEnabled && reasoningBudget
- const maxTokens = enableThinking ? supportsReasoning.reasoningMaxOutputTokens : maxOutputTokens
- const thinkingObj: Partial = enableThinking ? {
- thinking: { type: 'enabled', budget_tokens: reasoningBudget } // thinking enabled
- } : {}
+ // instance
+ const { messages, separateSystemMessageStr } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: true })
+ const anthropic = new Anthropic({
+ apiKey: thisConfig.apiKey,
+ dangerouslyAllowBrowser: true
+ });
const stream = anthropic.messages.stream({
system: separateSystemMessageStr,
@@ -332,7 +342,7 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM
model: modelName,
max_tokens: maxTokens ?? 4_096, // anthropic requires this
...toolsObj,
- ...thinkingObj,
+ ...includeInPayload,
})
// when receive text