Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,22 @@ export class GeminiProvider extends BaseLLMProvider {
return safetySettings.length > 0 ? safetySettings : undefined
}

// 判断模型是否支持 thinkingBudget
private supportsThinkingBudget(modelId: string): boolean {
return (
modelId.includes('gemini-2.5-pro') ||
modelId.includes('gemini-2.5-flash') ||
modelId.includes('gemini-2.5-flash-lite')
)
}

// 获取生成配置,不再创建模型实例
private getGenerationConfig(
temperature?: number,
maxTokens?: number,
modelId?: string,
reasoning?: boolean
reasoning?: boolean,
thinkingBudget?: number
): GenerationConfig {
const generationConfig: GenerationConfig = {
temperature,
Expand All @@ -462,6 +472,11 @@ export class GeminiProvider extends BaseLLMProvider {
generationConfig.thinkingConfig = {
includeThoughts: true
}

// 仅对支持 thinkingBudget 的 Gemini 2.5 系列模型添加 thinkingBudget 参数
if (modelId && this.supportsThinkingBudget(modelId) && thinkingBudget !== undefined) {
generationConfig.thinkingConfig.thinkingBudget = thinkingBudget
}
}

return generationConfig
Expand Down Expand Up @@ -937,7 +952,13 @@ export class GeminiProvider extends BaseLLMProvider {
const requestParams: GenerateContentParameters = {
model: modelId,
contents: formattedParts.contents,
config: this.getGenerationConfig(temperature, maxTokens, modelId, modelConfig.reasoning)
config: this.getGenerationConfig(
temperature,
maxTokens,
modelId,
modelConfig.reasoning,
modelConfig.thinkingBudget
)
}
console.log('requestParams', requestParams)
if (formattedParts.systemInstruction) {
Expand Down
197 changes: 194 additions & 3 deletions src/renderer/src/components/settings/ModelConfigDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,68 @@
</div>
<Switch v-model:checked="config.reasoning" />
</div>

<!-- 思考预算 (仅对支持的 Gemini 模型显示) -->
<div v-if="showThinkingBudget" class="space-y-4">
<div class="flex items-center justify-between">
<div class="space-y-0.5">
<Label>{{ t('settings.model.modelConfig.thinkingBudget.label') }}</Label>
<p class="text-xs text-muted-foreground">
{{ t('settings.model.modelConfig.thinkingBudget.description') }}
</p>
<p class="text-xs text-orange-600">
{{ t('settings.model.modelConfig.thinkingBudget.forceEnabled') }}
</p>
</div>
<!-- Gemini 2.5 系列强制开启,不显示开关 -->
</div>

<!-- 思考预算详细配置 -->
<div class="space-y-3 pl-4 border-l-2 border-muted">
<div class="flex items-center justify-between">
<div class="space-y-0.5">
<Label class="text-sm">{{
t('settings.model.modelConfig.thinkingBudget.dynamic')
}}</Label>
</div>
<Switch
:checked="config.thinkingBudget === -1"
@update:checked="handleDynamicThinkingToggle"
/>
</div>

<!-- 数值输入 -->
<div class="space-y-2">
<Label class="text-sm">{{
t('settings.model.modelConfig.thinkingBudget.valueLabel')
}}</Label>
<Input
v-model.number="config.thinkingBudget"
type="number"
:min="-1"
:max="thinkingBudgetRange.max"
:step="128"
:placeholder="t('settings.model.modelConfig.thinkingBudget.placeholder')"
:class="{ 'border-destructive': thinkingBudgetError }"
:disabled="config.thinkingBudget === -1"
/>
<p class="text-xs text-muted-foreground">
<span v-if="thinkingBudgetError" class="text-red-600 font-medium">
{{ t('settings.model.modelConfig.thinkingBudget.notice')
}}{{ thinkingBudgetError }}。
</span>
<span v-else-if="props.modelId.includes('pro')" class="text-red-600 font-medium">
{{ t('settings.model.modelConfig.thinkingBudget.notice')
}}{{ t('settings.model.modelConfig.thinkingBudget.warnings.proNoDisable') }}。
</span>
{{ t('settings.model.modelConfig.thinkingBudget.dynamicPrefix')
}}{{ getDisableHint() }},{{
t('settings.model.modelConfig.thinkingBudget.range', thinkingBudgetRange)
}}
</p>
</div>
</div>
</div>
</form>
</div>

Expand Down Expand Up @@ -238,8 +300,8 @@ const loadConfig = async () => {
config.value = { ...modelConfig }
} catch (error) {
console.error('Failed to load model config:', error)
// 如果加载失败,则使用默认配置
config.value = {

const defaultConfig: ModelConfig = {
maxTokens: 4096,
contextLength: 8192,
temperature: 0.7,
Expand All @@ -248,6 +310,16 @@ const loadConfig = async () => {
reasoning: false,
type: ModelType.Chat
}

config.value = defaultConfig
}

// Initialize thinking budget if not set
if (props.providerId === 'gemini' && config.value.thinkingBudget === undefined) {
const thinkingConfig = getThinkingBudgetConfig(props.modelId)
if (thinkingConfig) {
config.value.thinkingBudget = thinkingConfig.defaultValue
}
}
}

Expand Down Expand Up @@ -280,7 +352,7 @@ const validateForm = () => {
// 表单是否有效
const isValid = computed(() => {
validateForm()
return Object.keys(errors.value).length === 0
return Object.keys(errors.value).length === 0 && !thinkingBudgetError.value
})

// 保存配置
Expand Down Expand Up @@ -324,6 +396,125 @@ watch(
{ immediate: true }
)

// 根据模型 ID 获取思考预算配置
const getThinkingBudgetConfig = (modelId: string) => {
if (modelId.includes('gemini-2.5-pro')) {
return {
min: 128,
max: 32768,
defaultValue: -1, // 默认动态思维
canDisable: false // 2.5 Pro 无法停用思考
}
}

if (modelId.includes('gemini-2.5-flash-lite')) {
return {
min: 0, // 支持设置为 0(停用思考)
max: 24576,
defaultValue: 0, // 默认不思考
canDisable: true // 可以设置为 0 停用思考
}
}

if (modelId.includes('gemini-2.5-flash')) {
return {
min: 0,
max: 24576,
defaultValue: -1, // 默认动态思维
canDisable: true // 可以设置为 0 停用
}
}

return null // 不支持的模型
}

// 是否显示思考预算配置
const showThinkingBudget = computed(() => {
const isGemini = props.providerId === 'gemini'
const hasReasoning = config.value.reasoning
const modelConfig = getThinkingBudgetConfig(props.modelId)
const isSupported = modelConfig !== null
const result = isGemini && hasReasoning && isSupported

return result
})

// 思考预算范围
const thinkingBudgetRange = computed(() => {
const modelConfig = getThinkingBudgetConfig(props.modelId)
return modelConfig || { min: 128, max: 32768, defaultValue: -1, canDisable: false }
})

// 思考预算验证错误
const thinkingBudgetError = computed(() => {
if (!showThinkingBudget.value) return ''

const value = config.value.thinkingBudget
const range = thinkingBudgetRange.value

if (value === undefined || value === null) return ''

// -1 是有效值(动态思维)
if (value === -1) return ''

// 检查是否可以禁用(设置为 0)
if (value === 0 && !range.canDisable) {
if (props.modelId.includes('pro')) {
return t('settings.model.modelConfig.thinkingBudget.warnings.proCannotDisable')
} else if (props.modelId.includes('flash-lite')) {
return t('settings.model.modelConfig.thinkingBudget.warnings.flashLiteCannotSetZero')
} else {
return t('settings.model.modelConfig.thinkingBudget.warnings.modelCannotDisable')
}
}

if (value < range.min && value !== 0) {
// 对于 Flash-Lite,0 是有效值(停用思考),但其他值不能小于 512
if (props.modelId.includes('flash-lite') && value > 0 && value < 512) {
return t('settings.model.modelConfig.thinkingBudget.warnings.flashLiteMinValue')
}

let hint = ''
if (range.canDisable && range.min === 0) {
hint = t('settings.model.modelConfig.thinkingBudget.hints.withZeroAndDynamic')
} else if (range.canDisable) {
hint = t('settings.model.modelConfig.thinkingBudget.hints.withDynamic')
} else {
hint = t('settings.model.modelConfig.thinkingBudget.hints.withDynamic')
}
return t('settings.model.modelConfig.thinkingBudget.warnings.belowMin', {
min: range.min,
hint
})
}
if (value > range.max) {
return t('settings.model.modelConfig.thinkingBudget.warnings.aboveMax', { max: range.max })
}
return ''
})

// 处理动态思维开关
const handleDynamicThinkingToggle = (enabled: boolean) => {
if (enabled) {
config.value.thinkingBudget = -1 // 动态思维
} else {
// 设置为 1024(Gemini Demo的默认值)
config.value.thinkingBudget = 1024
}
}

// 获取禁用提示文字
const getDisableHint = () => {
const range = thinkingBudgetRange.value
if (props.modelId.includes('flash-lite')) {
return t('settings.model.modelConfig.thinkingBudget.hints.flashLiteDisable')
} else if (range.canDisable) {
return t('settings.model.modelConfig.thinkingBudget.hints.normalDisable')
} else {
return '' // Pro 模型的限制已在红色警告中显示
}
}

onMounted(() => {
if (props.open) {
loadConfig()
Expand Down
27 changes: 27 additions & 0 deletions src/renderer/src/i18n/en-US/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,33 @@
"description": "Does the model support reasoning ability?",
"label": "Reasoning ability"
},
"thinkingBudget": {
"label": "Thinking Budget",
"description": "Limit the length of model thinking",
"dynamic": "Dynamic Thinking",
"range": "Range: {min} - {max}",
"onlySupported": "Only supported in Gemini 2.5 Flash, 2.5 Pro and 2.5 Flash-Lite",
"valueLabel": "Thinking Budget Value",
"placeholder": "Enter thinking budget value",
"forceEnabled": "Gemini 2.5 series models force enable thinking budget",
"dynamicPrefix": "-1 = Dynamic Thinking",
"notice": "Notice: ",
"warnings": {
"proNoDisable": "This model does not support disabling thinking, minimum value 128",
"proCannotDisable": "Gemini 2.5 Pro cannot disable thinking function",
"flashLiteCannotSetZero": "Gemini 2.5 Flash-Lite cannot be set to 0, minimum value is 512",
"modelCannotDisable": "This model cannot disable thinking function",
"flashLiteMinValue": "Thinking budget cannot be less than 512 when set to specific value (or use 0 to disable thinking, -1 to enable dynamic thinking)",
"belowMin": "Thinking budget cannot be less than {min}{hint}",
"aboveMax": "Thinking budget cannot be greater than {max}"
},
"hints": {
"flashLiteDisable": ", 0 = disable thinking, specific value minimum 512",
"normalDisable": ", 0 = disable thinking",
"withZeroAndDynamic": "(or use 0 to disable thinking, -1 to enable dynamic thinking)",
"withDynamic": "(or use -1 to enable dynamic thinking)"
}
},
"resetConfirm": {
"confirm": "Confirm reset",
"message": "Are you sure you want to reset the configuration of this model to the default? \nThis operation is irrevocable.",
Expand Down
27 changes: 27 additions & 0 deletions src/renderer/src/i18n/fa-IR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,33 @@
"description": "آیا مدل از توانایی استدلال پشتیبانی می کند؟",
"label": "توانایی استدلال"
},
"thinkingBudget": {
"label": "بودجه تفکر",
"description": "محدود کردن طول تفکر مدل",
"dynamic": "تفکر پویا",
"range": "محدوده: {min} - {max}",
"onlySupported": "فقط در Gemini 2.5 Flash، 2.5 Pro و 2.5 Flash-Lite پشتیبانی می‌شود",
"valueLabel": "مقدار بودجه تفکر",
"placeholder": "مقدار بودجه تفکر را وارد کنید",
"forceEnabled": "مدل‌های سری Gemini 2.5 بودجه تفکر را به صورت اجباری فعال می‌کنند",
"dynamicPrefix": "-1 = تفکر پویا",
"notice": "توجه: ",
"warnings": {
"proNoDisable": "این مدل از غیرفعال کردن تفکر پشتیبانی نمی‌کند، حداقل مقدار 128",
"proCannotDisable": "Gemini 2.5 Pro نمی‌تواند عملکرد تفکر را غیرفعال کند",
"flashLiteCannotSetZero": "Gemini 2.5 Flash-Lite نمی‌تواند روی 0 تنظیم شود، حداقل مقدار 512 است",
"modelCannotDisable": "این مدل نمی‌تواند عملکرد تفکر را غیرفعال کند",
"flashLiteMinValue": "بودجه تفکر نمی‌تواند کمتر از 512 باشد هنگام تنظیم روی مقدار مشخص (یا از 0 برای غیرفعال کردن تفکر، -1 برای فعال کردن تفکر پویا استفاده کنید)",
"belowMin": "بودجه تفکر نمی‌تواند کمتر از {min} باشد{hint}",
"aboveMax": "بودجه تفکر نمی‌تواند بیشتر از {max} باشد"
},
"hints": {
"flashLiteDisable": "، 0 = غیرفعال کردن تفکر، مقدار مشخص حداقل 512",
"normalDisable": "، 0 = غیرفعال کردن تفکر",
"withZeroAndDynamic": "(یا از 0 برای غیرفعال کردن تفکر، -1 برای فعال کردن تفکر پویا استفاده کنید)",
"withDynamic": "(یا از -1 برای فعال کردن تفکر پویا استفاده کنید)"
}
},
"resetConfirm": {
"confirm": "تأیید مجدد",
"message": "آیا مطمئن هستید که می خواهید پیکربندی این مدل را به صورت پیش فرض تنظیم کنید؟ این عمل غیرقابل برگشت است.",
Expand Down
27 changes: 27 additions & 0 deletions src/renderer/src/i18n/fr-FR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,33 @@
"description": "Le modèle prend-il en charge la capacité de raisonnement?",
"label": "Capacité de raisonnement"
},
"thinkingBudget": {
"label": "Budget de Réflexion",
"description": "Limiter la longueur de réflexion du modèle",
"dynamic": "Réflexion Dynamique",
"range": "Plage: {min} - {max}",
"onlySupported": "Pris en charge uniquement dans Gemini 2.5 Flash, 2.5 Pro et 2.5 Flash-Lite",
"valueLabel": "Valeur du Budget de Réflexion",
"placeholder": "Entrez la valeur du budget de réflexion",
"forceEnabled": "Les modèles de la série Gemini 2.5 activent de force le budget de réflexion",
"dynamicPrefix": "-1 = Réflexion Dynamique",
"notice": "Attention : ",
"warnings": {
"proNoDisable": "Ce modèle ne prend pas en charge la désactivation de la réflexion, valeur minimale 128",
"proCannotDisable": "Gemini 2.5 Pro ne peut pas désactiver la fonction de réflexion",
"flashLiteCannotSetZero": "Gemini 2.5 Flash-Lite ne peut pas être défini à 0, la valeur minimale est 512",
"modelCannotDisable": "Ce modèle ne peut pas désactiver la fonction de réflexion",
"flashLiteMinValue": "Le budget de réflexion ne peut pas être inférieur à 512 lorsqu'il est défini sur une valeur spécifique (ou utilisez 0 pour désactiver la réflexion, -1 pour activer la réflexion dynamique)",
"belowMin": "Le budget de réflexion ne peut pas être inférieur à {min}{hint}",
"aboveMax": "Le budget de réflexion ne peut pas être supérieur à {max}"
},
"hints": {
"flashLiteDisable": ", 0 = désactiver la réflexion, valeur spécifique minimum 512",
"normalDisable": ", 0 = désactiver la réflexion",
"withZeroAndDynamic": "(ou utilisez 0 pour désactiver la réflexion, -1 pour activer la réflexion dynamique)",
"withDynamic": "(ou utilisez -1 pour activer la réflexion dynamique)"
}
},
"resetConfirm": {
"confirm": "Confirmer la réinitialisation",
"message": "Êtes-vous sûr de souhaiter réinitialiser la configuration de ce modèle par défaut? Cette opération est irrévocable.",
Expand Down
Loading