Skip to content

Commit 03625e8

Browse files
authored
💄 style: Optimized Gemini thinkingBudget configuration (lobehub#8224)
1 parent b1c72fd commit 03625e8

File tree

8 files changed

+188
-25
lines changed

8 files changed

+188
-25
lines changed

src/config/aiModels/google.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const googleChatModels: AIChatModelCard[] = [
2020
},
2121
releasedAt: '2025-06-17',
2222
settings: {
23-
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
23+
extendParams: ['thinkingBudget'],
2424
searchImpl: 'params',
2525
searchProvider: 'google',
2626
},
@@ -45,7 +45,7 @@ const googleChatModels: AIChatModelCard[] = [
4545
},
4646
releasedAt: '2025-06-05',
4747
settings: {
48-
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
48+
extendParams: ['thinkingBudget'],
4949
searchImpl: 'params',
5050
searchProvider: 'google',
5151
},
@@ -119,7 +119,7 @@ const googleChatModels: AIChatModelCard[] = [
119119
},
120120
releasedAt: '2025-06-17',
121121
settings: {
122-
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
122+
extendParams: ['thinkingBudget'],
123123
searchImpl: 'params',
124124
searchProvider: 'google',
125125
},
@@ -143,7 +143,7 @@ const googleChatModels: AIChatModelCard[] = [
143143
},
144144
releasedAt: '2025-05-20',
145145
settings: {
146-
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
146+
extendParams: ['thinkingBudget'],
147147
searchImpl: 'params',
148148
searchProvider: 'google',
149149
},
@@ -167,7 +167,7 @@ const googleChatModels: AIChatModelCard[] = [
167167
},
168168
releasedAt: '2025-04-17',
169169
settings: {
170-
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
170+
extendParams: ['thinkingBudget'],
171171
searchImpl: 'params',
172172
searchProvider: 'google',
173173
},
@@ -215,7 +215,7 @@ const googleChatModels: AIChatModelCard[] = [
215215
},
216216
releasedAt: '2025-06-11',
217217
settings: {
218-
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
218+
extendParams: ['thinkingBudget'],
219219
searchImpl: 'params',
220220
searchProvider: 'google',
221221
},

src/features/ChatInput/ActionBar/Model/ControlsForm.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
1313
import ContextCachingSwitch from './ContextCachingSwitch';
1414
import ReasoningEffortSlider from './ReasoningEffortSlider';
1515
import ReasoningTokenSlider from './ReasoningTokenSlider';
16+
import ThinkingBudgetSlider from './ThinkingBudgetSlider';
1617

1718
const ControlsForm = memo(() => {
1819
const { t } = useTranslation('chat');
@@ -93,6 +94,17 @@ const ControlsForm = memo(() => {
9394
paddingBottom: 0,
9495
},
9596
},
97+
{
98+
children: <ThinkingBudgetSlider />,
99+
label: t('extendParams.reasoningBudgetToken.title'),
100+
layout: 'vertical',
101+
minWidth: 500,
102+
name: 'thinkingBudget',
103+
style: {
104+
paddingBottom: 0,
105+
},
106+
tag: 'thinkingBudget',
107+
},
96108
].filter(Boolean) as FormItemProps[];
97109

98110
return (
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { InputNumber } from '@lobehub/ui';
2+
import { Slider } from 'antd';
3+
import { memo, useMemo } from 'react';
4+
import { Flexbox } from 'react-layout-kit';
5+
import useMergeState from 'use-merge-value';
6+
7+
// 定义特殊值映射
8+
const SPECIAL_VALUES = {
9+
AUTO: -1,
10+
OFF: 0,
11+
};
12+
13+
// 定义滑块位置到实际值的映射
14+
const SLIDER_TO_VALUE_MAP = [
15+
SPECIAL_VALUES.AUTO, // 位置 0 -> -1 (Auto)
16+
SPECIAL_VALUES.OFF, // 位置 1 -> 0 (OFF)
17+
128, // 位置 2 -> 128
18+
512, // 位置 3 -> 512
19+
1024, // 位置 4 -> 1024
20+
2048, // 位置 5 -> 2048
21+
4096, // 位置 6 -> 4096
22+
8192, // 位置 7 -> 8192
23+
16_384, // 位置 8 -> 16384
24+
24_576, // 位置 9 -> 24576
25+
32_768, // 位置 10 -> 32768
26+
];
27+
28+
// 从实际值获取滑块位置
29+
const getSliderPosition = (value: number): number => {
30+
const index = SLIDER_TO_VALUE_MAP.indexOf(value);
31+
return index === -1 ? 0 : index;
32+
};
33+
34+
// 从滑块位置获取实际值(修复:0 不再被当作 falsy)
35+
const getValueFromPosition = (position: number): number => {
36+
const v = SLIDER_TO_VALUE_MAP[position];
37+
return v === undefined ? SPECIAL_VALUES.AUTO : v;
38+
};
39+
40+
interface ThinkingBudgetSliderProps {
41+
defaultValue?: number;
42+
onChange?: (value: number) => void;
43+
value?: number;
44+
}
45+
46+
const ThinkingBudgetSlider = memo<ThinkingBudgetSliderProps>(
47+
({ value, onChange, defaultValue }) => {
48+
// 首先确定初始的 budget 值
49+
const initialBudget = value ?? defaultValue ?? SPECIAL_VALUES.AUTO;
50+
51+
const [budget, setBudget] = useMergeState(initialBudget, {
52+
defaultValue,
53+
onChange,
54+
value,
55+
});
56+
57+
const sliderPosition = getSliderPosition(budget);
58+
59+
const updateWithSliderPosition = (position: number) => {
60+
const newValue = getValueFromPosition(position);
61+
setBudget(newValue);
62+
};
63+
64+
const updateWithRealValue = (value: number) => {
65+
setBudget(value);
66+
};
67+
68+
const marks = useMemo(() => {
69+
return {
70+
0: 'Auto',
71+
1: 'OFF',
72+
2: '128',
73+
3: '512',
74+
4: '1K',
75+
5: '2K',
76+
6: '4K',
77+
7: '8K',
78+
8: '16K',
79+
9: '24K',
80+
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
81+
10: '32K',
82+
};
83+
}, []);
84+
85+
return (
86+
<Flexbox align={'center'} gap={12} horizontal paddingInline={'4px 0'}>
87+
<Flexbox flex={1}>
88+
<Slider
89+
marks={marks}
90+
max={10}
91+
min={0}
92+
onChange={updateWithSliderPosition}
93+
step={null}
94+
tooltip={{ open: false }}
95+
value={sliderPosition}
96+
/>
97+
</Flexbox>
98+
<div>
99+
<InputNumber
100+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
101+
formatter={(value, _info) => {
102+
if (value === SPECIAL_VALUES.AUTO) return 'Auto';
103+
if (value === SPECIAL_VALUES.OFF) return 'OFF';
104+
return `${value}`;
105+
}}
106+
max={32_768}
107+
min={-1}
108+
onChange={(e) => {
109+
if (e === null || e === undefined) return;
110+
updateWithRealValue(e as number);
111+
}}
112+
parser={(value) => {
113+
if (typeof value === 'string') {
114+
if (value.toLowerCase() === 'auto') return SPECIAL_VALUES.AUTO;
115+
if (value.toLowerCase() === 'off') return SPECIAL_VALUES.OFF;
116+
return parseInt(value.replaceAll(/[^\d-]/g, ''), 10) || 0;
117+
}
118+
if (typeof value === 'number') {
119+
return value;
120+
}
121+
return SPECIAL_VALUES.AUTO;
122+
}}
123+
step={128}
124+
style={{ width: 80 }}
125+
value={budget}
126+
/>
127+
</div>
128+
</Flexbox>
129+
);
130+
},
131+
);
132+
133+
export default ThinkingBudgetSlider;

src/libs/model-runtime/google/index.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -120,28 +120,37 @@ export class LobeGoogleAI implements LobeRuntimeAI {
120120
async chat(rawPayload: ChatStreamPayload, options?: ChatMethodOptions) {
121121
try {
122122
const payload = this.buildPayload(rawPayload);
123-
const { model, thinking } = payload;
123+
const { model, thinkingBudget } = payload;
124124

125125
const thinkingConfig: GoogleAIThinkingConfig = {
126126
includeThoughts:
127-
thinking?.type === 'enabled' ||
128-
(!thinking && model && (model.includes('-2.5-') || model.includes('thinking')))
127+
!!thinkingBudget ||
128+
(!thinkingBudget && model && (model.includes('-2.5-') || model.includes('thinking')))
129129
? true
130130
: undefined,
131-
thinkingBudget:
132-
thinking?.type === 'enabled'
133-
? (() => {
134-
const budget = thinking.budget_tokens;
135-
if (model.includes('-2.5-flash')) {
136-
return Math.min(budget, 24_576);
137-
} else if (model.includes('-2.5-pro')) {
138-
return Math.max(128, Math.min(budget, 32_768));
139-
}
140-
return Math.min(budget, 24_576);
141-
})()
142-
: thinking?.type === 'disabled'
143-
? model.includes('-2.5-pro') ? 128 : 0
144-
: undefined,
131+
// https://ai.google.dev/gemini-api/docs/thinking#set-budget
132+
thinkingBudget: (() => {
133+
if (thinkingBudget !== undefined && thinkingBudget !== null) {
134+
if (model.includes('-2.5-flash-lite')) {
135+
if (thinkingBudget === 0 || thinkingBudget === -1) {
136+
return thinkingBudget;
137+
}
138+
return Math.max(512, Math.min(thinkingBudget, 24_576));
139+
} else if (model.includes('-2.5-flash')) {
140+
return Math.min(thinkingBudget, 24_576);
141+
} else if (model.includes('-2.5-pro')) {
142+
return Math.max(128, Math.min(thinkingBudget, 32_768));
143+
}
144+
return Math.min(thinkingBudget, 24_576);
145+
}
146+
147+
if (model.includes('-2.5-pro') || model.includes('-2.5-flash')) {
148+
return -1;
149+
} else if (model.includes('-2.5-flash-lite')) {
150+
return 0;
151+
}
152+
return undefined;
153+
})(),
145154
};
146155

147156
const contents = await this.buildGoogleMessages(payload.messages);

src/libs/model-runtime/types/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export interface ChatStreamPayload {
126126
budget_tokens: number;
127127
type: 'enabled' | 'disabled';
128128
};
129+
thinkingBudget?: number;
129130
tool_choice?: string;
130131
tools?: ChatCompletionTool[];
131132
/**

src/services/chat.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,13 @@ class ChatService {
263263
if (modelExtendParams!.includes('reasoningEffort') && chatConfig.reasoningEffort) {
264264
extendParams.reasoning_effort = chatConfig.reasoningEffort;
265265
}
266+
267+
if (
268+
modelExtendParams!.includes('thinkingBudget') &&
269+
chatConfig.thinkingBudget !== undefined
270+
) {
271+
extendParams.thinkingBudget = chatConfig.thinkingBudget;
272+
}
266273
}
267274

268275
return this.getChatCompletion(

src/types/agent/chatConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface LobeAgentChatConfig {
2626
enableReasoningEffort?: boolean;
2727
reasoningBudgetToken?: number;
2828
reasoningEffort?: 'low' | 'medium' | 'high';
29-
29+
thinkingBudget?: number;
3030
/**
3131
* 禁用上下文缓存
3232
*/

src/types/aiModel.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ export type ExtendParamsType =
147147
| 'reasoningBudgetToken'
148148
| 'enableReasoning'
149149
| 'disableContextCaching'
150-
| 'reasoningEffort';
150+
| 'reasoningEffort'
151+
| 'thinkingBudget';
151152

152153
export interface AiModelSettings {
153154
extendParams?: ExtendParamsType[];

0 commit comments

Comments
 (0)