diff --git a/src/renderer/src/components/ThinkingPanel/ThinkingSelect.tsx b/src/renderer/src/components/ThinkingPanel/ThinkingSelect.tsx
index 307dd4d4f4..ca92e85891 100644
--- a/src/renderer/src/components/ThinkingPanel/ThinkingSelect.tsx
+++ b/src/renderer/src/components/ThinkingPanel/ThinkingSelect.tsx
@@ -1,16 +1,17 @@
import { isSupportedReasoningEffortGrokModel } from '@renderer/config/models'
import { Assistant, Model } from '@renderer/types'
-import { Select } from 'antd'
-import { useMemo, useState } from 'react'
+import { List } from 'antd'
+import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
import { ReasoningEffortOptions } from './index'
interface ThinkingSelectProps {
model: Model
assistant: Assistant
- value?: ReasoningEffortOptions | null
- onChange?: (value: ReasoningEffortOptions) => void
+ value: ReasoningEffortOptions
+ onChange: (value: ReasoningEffortOptions) => void
}
interface OptionType {
@@ -18,9 +19,8 @@ interface OptionType {
value: ReasoningEffortOptions
}
-export default function ThinkingSelect({ model, assistant, value, onChange }: ThinkingSelectProps) {
+export default function ThinkingSelect({ model, value, onChange }: ThinkingSelectProps) {
const { t } = useTranslation()
- const [open, setOpen] = useState(false)
const baseOptions = useMemo(
() =>
@@ -41,24 +41,41 @@ export default function ThinkingSelect({ model, assistant, value, onChange }: Th
[model, baseOptions]
)
- const currentValue = value ?? assistant.settings?.reasoning_effort ?? null
-
- const handleChange = (newValue: ReasoningEffortOptions) => {
- onChange?.(newValue)
- setOpen(false)
- }
-
return (
- <>
-
- >
+ (
+ onChange(option.value)}>
+ {option.label}
+
+ )}
+ />
)
}
+
+const ReasoningEffortLabel = styled.div`
+ font-size: 16px;
+ font-family: Ubuntu;
+`
+
+const StyledListItem = styled(List.Item)<{ $isSelected: boolean }>`
+ cursor: pointer;
+ padding: 8px 16px;
+ margin: 4px 0;
+ font-family: Ubuntu;
+ border-radius: var(--list-item-border-radius);
+ font-size: 16px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ transition: all 0.3s;
+ background-color: ${(props) => (props.$isSelected ? 'var(--color-background-soft)' : 'transparent')};
+
+ .ant-list-item {
+ border: none !important;
+ }
+
+ &:hover {
+ background-color: var(--color-background-soft);
+ }
+`
diff --git a/src/renderer/src/components/ThinkingPanel/ThinkingSlider.tsx b/src/renderer/src/components/ThinkingPanel/ThinkingSlider.tsx
index d49ee28bc1..43195383ce 100644
--- a/src/renderer/src/components/ThinkingPanel/ThinkingSlider.tsx
+++ b/src/renderer/src/components/ThinkingPanel/ThinkingSlider.tsx
@@ -1,7 +1,9 @@
import { InfoCircleOutlined } from '@ant-design/icons'
import { Model } from '@renderer/types'
-import { Col, InputNumber, Radio, Row, Slider, Space, Tooltip } from 'antd'
+import { Button, InputNumber, Slider, Tooltip } from 'antd'
import { useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
import { isSupportedThinkingTokenGeminiModel } from '../../config/models'
@@ -14,11 +16,9 @@ interface ThinkingSliderProps {
}
export default function ThinkingSlider({ model, value, min, max, onChange }: ThinkingSliderProps) {
- // 使用null表示"Default"模式,使用数字表示"Custom"模式
const [mode, setMode] = useState<'default' | 'custom'>(value === null ? 'default' : 'custom')
const [customValue, setCustomValue] = useState(value === null ? 0 : value)
-
- // 当外部value变化时更新内部状态
+ const { t } = useTranslation()
useEffect(() => {
if (value === null) {
setMode('default')
@@ -28,18 +28,15 @@ export default function ThinkingSlider({ model, value, min, max, onChange }: Thi
}
}, [value])
- // 处理模式切换
- const handleModeChange = (e: any) => {
- const newMode = e.target.value
+ const handleModeChange = (newMode: 'default' | 'custom') => {
setMode(newMode)
if (newMode === 'default') {
- onChange(null) // 传递null表示使用默认行为
+ onChange(null)
} else {
- onChange(customValue) // 传递当前自定义值
+ onChange(customValue)
}
}
- // 处理自定义值变化
const handleCustomValueChange = (newValue: number | null) => {
if (newValue !== null) {
setCustomValue(newValue)
@@ -48,45 +45,128 @@ export default function ThinkingSlider({ model, value, min, max, onChange }: Thi
}
return (
-
+
{isSupportedThinkingTokenGeminiModel(model) && (
-
- Default (Model's default behavior)
- Custom
-
+
+
+ handleModeChange('default')}>
+ {t('chat.input.thinking.mode.default')}
+
+
+
+ handleModeChange('custom')}>
+ {t('chat.input.thinking.mode.custom')}
+
+
+
)}
{mode === 'custom' && (
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+ 0
+ {max.toLocaleString()}
+
+
+
+
+ handleCustomValueChange(Number(value))}
+ controls={false}
+ />
+
+
+
+
+
)}
-
+
)
}
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ width: 100%;
+ min-width: 320px;
+ padding: 4px;
+`
+
+const ButtonGroup = styled.div`
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+ margin-bottom: 4px;
+`
+
+const ModeButton = styled(Button)`
+ min-width: 90px;
+ height: 28px;
+ border-radius: 14px;
+ padding: 0 16px;
+ font-size: 13px;
+
+ &:hover {
+ background-color: var(--color-background-soft);
+ }
+
+ &.ant-btn-primary {
+ background-color: var(--color-primary);
+ border-color: var(--color-primary);
+
+ &:hover {
+ background-color: var(--color-primary);
+ opacity: 0.9;
+ }
+ }
+`
+
+const CustomControls = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 12px;
+`
+
+const SliderContainer = styled.div`
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ min-width: 180px;
+`
+
+const SliderMarks = styled.div`
+ display: flex;
+ justify-content: space-between;
+ color: var(--color-text-2);
+ font-size: 12px;
+`
+
+const InputContainer = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 8px;
+`
+
+const StyledInputNumber = styled(InputNumber)`
+ width: 70px;
+
+ .ant-input-number-input {
+ height: 28px;
+ text-align: center;
+ font-size: 13px;
+ padding: 0 8px;
+ }
+`
diff --git a/src/renderer/src/components/ThinkingPanel/index.tsx b/src/renderer/src/components/ThinkingPanel/index.tsx
index 4a9f8f6d68..ca59e8266e 100644
--- a/src/renderer/src/components/ThinkingPanel/index.tsx
+++ b/src/renderer/src/components/ThinkingPanel/index.tsx
@@ -14,19 +14,17 @@ import ThinkingSlider from './ThinkingSlider'
const THINKING_TOKEN_MAP: Record = {
// Gemini models
- '^gemini-.*$': { min: 0, max: 24576 },
+ 'gemini-.*$': { min: 0, max: 24576 },
// Qwen models
- '^qwen-plus-.*$': { min: 0, max: 38912 },
- '^qwen-turbo-.*$': { min: 0, max: 38912 },
- '^qwq-.*$': { min: 0, max: 32768 },
- '^qvq-.*$': { min: 0, max: 16384 },
- '^qwen3-0\\.6b$': { min: 0, max: 30720 },
- '^qwen3-1\\.7b$': { min: 0, max: 30720 },
- '^qwen3-.*$': { min: 0, max: 38912 },
+ 'qwen-plus-.*$': { min: 0, max: 38912 },
+ 'qwen-turbo-.*$': { min: 0, max: 38912 },
+ 'qwen3-0\\.6b$': { min: 0, max: 30720 },
+ 'qwen3-1\\.7b$': { min: 0, max: 30720 },
+ 'qwen3-.*$': { min: 0, max: 38912 },
// Claude models
- '^claude-3.*sonnet$': { min: 0, max: 64000 }
+ 'claude-3[.-]7.*sonnet.*$': { min: 0, max: 64000 }
}
export type ReasoningEffortOptions = 'low' | 'medium' | 'high'
@@ -67,10 +65,9 @@ export default function ThinkingPanel({ model, assistant }: ThinkingPanelProps)
return currentThinkingBudget > maxTokens
}, [currentThinkingBudget, maxTokens])
- // 使用useEffect显示错误消息
useEffect(() => {
if (isBudgetExceedingMax && isSupportedThinkingTokenClaudeModel(model)) {
- window.message.error(t('chat.input.thinking_budget_exceeds_max'))
+ window.message.error(t('chat.input.thinking.budget_exceeds_max'))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isBudgetExceedingMax, model])
@@ -109,13 +106,11 @@ export default function ThinkingPanel({ model, assistant }: ThinkingPanelProps)
}
if (isSupportedReasoningEffort) {
- const currentReasoningEffort = assistant.settings?.reasoning_effort
-
return (
)
diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts
index a45f263353..1f080b6861 100644
--- a/src/renderer/src/config/models.ts
+++ b/src/renderer/src/config/models.ts
@@ -210,6 +210,7 @@ export const FUNCTION_CALLING_MODELS = [
'o1(?:-[\\w-]+)?',
'claude',
'qwen',
+ 'qwen3',
'hunyuan',
'deepseek',
'glm-4(?:-[\\w-]+)?',
@@ -2298,7 +2299,7 @@ export function isQwenReasoningModel(model?: Model): boolean {
return true
}
- if (model.id.includes('qwq')) {
+ if (model.id.includes('qwq') || model.id.includes('qvq')) {
return true
}
@@ -2312,8 +2313,6 @@ export function isSupportedThinkingTokenQwenModel(model?: Model): boolean {
return (
model.id.includes('qwen3') ||
- model.id.includes('qwq') ||
- model.id.includes('qvq') ||
[
'qwen-plus-latest',
'qwen-plus-0428',
diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index 5367da7419..08c1bda2e4 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -56,11 +56,9 @@
"settings.preset_messages": "Preset Messages",
"settings.prompt": "Prompt Settings",
"settings.reasoning_effort": "Reasoning effort",
- "settings.reasoning_effort.high": "high",
- "settings.reasoning_effort.low": "low",
- "settings.reasoning_effort.medium": "medium",
- "settings.reasoning_effort.off": "off",
- "settings.reasoning_effort.tip": "Only supported by OpenAI o-series, Anthropic, and Grok reasoning models",
+ "settings.reasoning_effort.high": "Think harder",
+ "settings.reasoning_effort.low": "Think less",
+ "settings.reasoning_effort.medium": "Think normally",
"settings.more": "Assistant Settings"
},
"auth": {
@@ -185,7 +183,6 @@
"settings.top_p": "Top-P",
"settings.top_p.tip": "Default value is 1, the smaller the value, the less variety in the answers, the easier to understand, the larger the value, the larger the range of the AI's vocabulary, the more diverse",
"suggestions.title": "Suggested Questions",
- "thinking": "Thinking",
"topics.auto_rename": "Auto Rename",
"topics.clear.title": "Clear Messages",
"topics.copy.image": "Copy as image",
@@ -250,7 +247,15 @@
"input.upload.upload_from_local": "Upload local file...",
"input.web_search.builtin": "Model Built-in",
"input.web_search.builtin.enabled_content": "Use the built-in web search function of the model",
- "input.web_search.builtin.disabled_content": "The current model does not support web search"
+ "input.web_search.builtin.disabled_content": "The current model does not support web search",
+ "input.thinking": "Thinking",
+ "input.thinking.mode.default": "Default",
+ "input.thinking.mode.default.tip": "The model will automatically determine the number of tokens to think",
+ "input.thinking.mode.custom": "Custom",
+ "input.thinking.mode.custom.tip": "The maximum number of tokens the model can think. Need to consider the context limit of the model, otherwise an error will be reported",
+ "input.thinking.mode.tokens.tip": "Set the number of thinking tokens to use.",
+ "thinking": "Thinking",
+ "input.thinking.budget_exceeds_max": "Thinking budget exceeds the maximum token number"
},
"code_block": {
"collapse": "Collapse",
diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json
index 8c2f431b7a..2280357eb3 100644
--- a/src/renderer/src/i18n/locales/ja-jp.json
+++ b/src/renderer/src/i18n/locales/ja-jp.json
@@ -56,11 +56,9 @@
"settings.preset_messages": "プリセットメッセージ",
"settings.prompt": "プロンプト設定",
"settings.reasoning_effort": "思考連鎖の長さ",
- "settings.reasoning_effort.high": "長い",
- "settings.reasoning_effort.low": "短い",
- "settings.reasoning_effort.medium": "中程度",
- "settings.reasoning_effort.off": "オフ",
- "settings.reasoning_effort.tip": "OpenAI o-series、Anthropic、および Grok の推論モデルのみサポート",
+ "settings.reasoning_effort.high": "最大限の思考",
+ "settings.reasoning_effort.low": "少しの思考",
+ "settings.reasoning_effort.medium": "普通の思考",
"settings.more": "アシスタント設定"
},
"auth": {
@@ -250,7 +248,14 @@
"input.upload.upload_from_local": "ローカルファイルをアップロード...",
"input.web_search.builtin": "モデル内蔵",
"input.web_search.builtin.enabled_content": "モデル内蔵のウェブ検索機能を使用",
- "input.web_search.builtin.disabled_content": "現在のモデルはウェブ検索をサポートしていません"
+ "input.web_search.builtin.disabled_content": "現在のモデルはウェブ検索をサポートしていません",
+ "input.thinking": "思考",
+ "input.thinking.mode.default": "デフォルト",
+ "input.thinking.mode.custom": "カスタム",
+ "input.thinking.mode.custom.tip": "モデルが最大で思考できるトークン数。モデルのコンテキスト制限を考慮する必要があります。そうしないとエラーが発生します",
+ "input.thinking.mode.default.tip": "モデルが自動的に思考のトークン数を決定します",
+ "input.thinking.mode.tokens.tip": "思考のトークン数を設定します",
+ "input.thinking.budget_exceeds_max": "思考予算が最大トークン数を超えました"
},
"code_block": {
"collapse": "折りたたむ",
@@ -1507,7 +1512,7 @@
"title": "プライバシー設定",
"enable_privacy_mode": "匿名エラーレポートとデータ統計の送信"
},
- "input.show_translate_confirm": "[to be translated]:显示翻译确认对话框"
+ "input.show_translate_confirm": "翻訳確認ダイアログを表示"
},
"translate": {
"any.language": "任意の言語",
diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json
index 28b3d623e3..24274355dd 100644
--- a/src/renderer/src/i18n/locales/ru-ru.json
+++ b/src/renderer/src/i18n/locales/ru-ru.json
@@ -55,12 +55,9 @@
"settings.model": "Настройки модели",
"settings.preset_messages": "Предустановленные сообщения",
"settings.prompt": "Настройки промптов",
- "settings.reasoning_effort": "Длина цепочки рассуждений",
- "settings.reasoning_effort.high": "Длинная",
- "settings.reasoning_effort.low": "Короткая",
- "settings.reasoning_effort.medium": "Средняя",
- "settings.reasoning_effort.off": "Выключено",
- "settings.reasoning_effort.tip": "Поддерживается только моделями рассуждений OpenAI o-series, Anthropic и Grok",
+ "settings.reasoning_effort.high": "Стараюсь думать",
+ "settings.reasoning_effort.low": "Меньше думать",
+ "settings.reasoning_effort.medium": "Среднее",
"settings.more": "Настройки ассистента"
},
"auth": {
@@ -250,7 +247,14 @@
"input.upload.upload_from_local": "Загрузить локальный файл...",
"input.web_search.builtin": "Модель встроена",
"input.web_search.builtin.enabled_content": "Используйте встроенную функцию веб-поиска модели",
- "input.web_search.builtin.disabled_content": "Текущая модель не поддерживает веб-поиск"
+ "input.web_search.builtin.disabled_content": "Текущая модель не поддерживает веб-поиск",
+ "input.thinking": "Мыслим",
+ "input.thinking.mode.default": "По умолчанию",
+ "input.thinking.mode.default.tip": "Модель автоматически определяет количество токенов для размышления",
+ "input.thinking.mode.custom": "Пользовательский",
+ "input.thinking.mode.custom.tip": "Модель может максимально размышлять количество токенов. Необходимо учитывать ограничение контекста модели, иначе будет ошибка",
+ "input.thinking.mode.tokens.tip": "Установите количество токенов для размышления",
+ "input.thinking.budget_exceeds_max": "Бюджет размышления превышает максимальное количество токенов"
},
"code_block": {
"collapse": "Свернуть",
@@ -1507,7 +1511,7 @@
"title": "Настройки приватности",
"enable_privacy_mode": "Анонимная отправка отчетов об ошибках и статистики"
},
- "input.show_translate_confirm": "[to be translated]:显示翻译确认对话框"
+ "input.show_translate_confirm": "Показать диалоговое окно подтверждения перевода"
},
"translate": {
"any.language": "Любой язык",
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index a87493263d..fd8444b717 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -56,11 +56,9 @@
"settings.preset_messages": "预设消息",
"settings.prompt": "提示词设置",
"settings.reasoning_effort": "思维链长度",
- "settings.reasoning_effort.high": "长",
- "settings.reasoning_effort.low": "短",
- "settings.reasoning_effort.medium": "中",
- "settings.reasoning_effort.off": "关",
- "settings.reasoning_effort.tip": "仅支持 OpenAI o-series、Anthropic、Grok 推理模型",
+ "settings.reasoning_effort.low": "稍微思考",
+ "settings.reasoning_effort.medium": "正常思考",
+ "settings.reasoning_effort.high": "尽力思考",
"settings.more": "助手设置"
},
"auth": {
@@ -133,6 +131,13 @@
"input.translating": "翻译中...",
"input.send": "发送",
"input.settings": "设置",
+ "input.thinking": "思考",
+ "input.thinking.mode.default": "默认",
+ "input.thinking.mode.default.tip": "模型会自动确定思考的 token 数",
+ "input.thinking.mode.custom": "自定义",
+ "input.thinking.mode.custom.tip": "模型最多可以思考的 token 数。需要考虑模型的上下文限制,否则会报错",
+ "input.thinking.mode.tokens.tip": "设置思考的 token 数",
+ "input.thinking.budget_exceeds_max": "思考预算超过最大 token 数",
"input.topics": " 话题 ",
"input.translate": "翻译成{{target_language}}",
"input.upload": "上传图片或文档",
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index 105e699ae8..d110b3c32d 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -56,11 +56,9 @@
"settings.preset_messages": "預設訊息",
"settings.prompt": "提示詞設定",
"settings.reasoning_effort": "思維鏈長度",
- "settings.reasoning_effort.high": "長",
- "settings.reasoning_effort.low": "短",
- "settings.reasoning_effort.medium": "中",
- "settings.reasoning_effort.off": "關",
- "settings.reasoning_effort.tip": "僅支援 OpenAI o-series、Anthropic 和 Grok 推理模型",
+ "settings.reasoning_effort.high": "盡力思考",
+ "settings.reasoning_effort.low": "稍微思考",
+ "settings.reasoning_effort.medium": "正常思考",
"settings.more": "助手設定"
},
"auth": {
@@ -250,7 +248,14 @@
"input.upload.upload_from_local": "上傳本地文件...",
"input.web_search.builtin": "模型內置",
"input.web_search.builtin.enabled_content": "使用模型內置的網路搜尋功能",
- "input.web_search.builtin.disabled_content": "當前模型不支持網路搜尋功能"
+ "input.web_search.builtin.disabled_content": "當前模型不支持網路搜尋功能",
+ "input.thinking": "思考",
+ "input.thinking.mode.default": "預設",
+ "input.thinking.mode.default.tip": "模型會自動確定思考的 token 數",
+ "input.thinking.mode.custom": "自定義",
+ "input.thinking.mode.custom.tip": "模型最多可以思考的 token 數。需要考慮模型的上下文限制,否則會報錯",
+ "input.thinking.mode.tokens.tip": "設置思考的 token 數",
+ "input.thinking.budget_exceeds_max": "思考預算超過最大 token 數"
},
"code_block": {
"collapse": "折疊",
diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx
index 3a2554caf6..c296ebcb15 100644
--- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx
+++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx
@@ -799,13 +799,17 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) =
const newEnableThinking = !assistant.enableThinking
updateAssistant({ ...assistant, enableThinking: newEnableThinking })
- if (newEnableThinking && (isSupportedThinkingToken || isSupportedReasoningEffort)) {
- setThinkingPanelVisible(true)
- } else {
+ if (!newEnableThinking) {
setThinkingPanelVisible(false)
}
}
+ const onTogglePanel = () => {
+ if (isSupportedThinkingToken || isSupportedReasoningEffort) {
+ setThinkingPanelVisible((prev) => !prev)
+ }
+ }
+
useEffect(() => {
if (!isWebSearchModel(model) && assistant.enableWebSearch) {
updateAssistant({ ...assistant, enableWebSearch: false })
@@ -952,6 +956,8 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) =
assistant={assistant}
ToolbarButton={ToolbarButton}
onToggleThinking={onToggleThinking}
+ onTogglePanel={onTogglePanel}
+ showPanel={thinkingPanelVisible}
/>
@@ -1139,7 +1145,8 @@ const ToolbarButton = styled(Button)`
&.active {
background-color: var(--color-primary) !important;
.anticon,
- .iconfont {
+ .iconfont,
+ .chevron-icon {
color: var(--color-white-soft);
}
&:hover {
@@ -1155,31 +1162,18 @@ const ThinkingPanelContainer = styled.div`
transform: translateX(-50%);
background-color: var(--color-background);
border: 0.5px solid var(--color-border);
- border-radius: 8px 8px 0 0;
+ border-radius: 15px;
padding: 10px;
margin-bottom: 5px;
z-index: 10;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
min-width: 250px;
-
- /* Add a small arrow pointing down to the ThinkingButton */
- &::after {
- content: '';
- position: absolute;
- bottom: -5px;
- left: 50%;
- transform: translateX(-50%);
- width: 0;
- height: 0;
- border-left: 5px solid transparent;
- border-right: 5px solid transparent;
- border-top: 5px solid var(--color-border);
- }
`
const ThinkingButtonContainer = styled.div`
position: relative;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
`
export default Inputbar
diff --git a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx
index 6c2b9689b6..935e290dc0 100644
--- a/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx
+++ b/src/renderer/src/pages/home/Inputbar/ThinkingButton.tsx
@@ -1,31 +1,72 @@
-import { isReasoningModel } from '@renderer/config/models'
+import {
+ isReasoningModel,
+ isSupportedReasoningEffortModel,
+ isSupportedThinkingTokenModel
+} from '@renderer/config/models'
import { Assistant, Model } from '@renderer/types'
import { Tooltip } from 'antd'
-import { Brain } from 'lucide-react'
+import { Atom, ChevronDown, ChevronUp } from 'lucide-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
+import styled from 'styled-components'
interface Props {
model: Model
assistant: Assistant
ToolbarButton: any
onToggleThinking: () => void
+ onTogglePanel: () => void
+ showPanel: boolean
}
-const ThinkingButton: FC = ({ model, assistant, ToolbarButton, onToggleThinking }) => {
+const ThinkingButton: FC = ({ model, assistant, ToolbarButton, onToggleThinking, onTogglePanel, showPanel }) => {
const { t } = useTranslation()
if (!isReasoningModel(model)) {
return null
}
+ const isSupportedThinkingToken = isSupportedThinkingTokenModel(model)
+ const isSupportedReasoningEffort = isSupportedReasoningEffortModel(model)
return (
-
-
-
+
+
+
+
+
+ {showPanel ? (
+
+ ) : (
+
+ )}
+
+
)
}
+const ButtonContainer = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 0px;
+`
+
+const ChevronButton = styled.button`
+ background: none;
+ border: none;
+ padding: 2px;
+ cursor: pointer;
+ color: var(--color-icon);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-left: -8px;
+ transition: all 0.3s;
+
+ &:hover {
+ color: var(--color-text-1);
+ }
+`
+
export default ThinkingButton
diff --git a/src/renderer/src/providers/AiProvider/AnthropicProvider.ts b/src/renderer/src/providers/AiProvider/AnthropicProvider.ts
index 560de81225..5facff4a28 100644
--- a/src/renderer/src/providers/AiProvider/AnthropicProvider.ts
+++ b/src/renderer/src/providers/AiProvider/AnthropicProvider.ts
@@ -23,8 +23,6 @@ import OpenAI from 'openai'
import { CompletionsParams } from '.'
import BaseProvider from './BaseProvider'
-type ReasoningEffort = 'high' | 'medium' | 'low'
-
interface ReasoningConfig {
type: 'enabled' | 'disabled'
budget_tokens?: number
@@ -124,32 +122,17 @@ export default class AnthropicProvider extends BaseProvider {
* @param model - The model
* @returns The reasoning effort
*/
- private getReasoningEffort(assistant: Assistant, model: Model): ReasoningConfig | undefined {
+ private getBudgetToken(assistant: Assistant, model: Model): ReasoningConfig | undefined {
if (!isReasoningModel(model)) {
return undefined
}
- const effortRatios: Record = {
- high: 0.8,
- medium: 0.5,
- low: 0.2
- }
-
- const effort = assistant?.settings?.reasoning_effort as ReasoningEffort
- const effortRatio = effortRatios[effort]
-
- if (!effortRatio) {
- return undefined
- }
-
- const isClaude37Sonnet = model.id.includes('claude-3-7-sonnet') || model.id.includes('claude-3.7-sonnet')
-
- if (!isClaude37Sonnet) {
- return undefined
- }
-
const maxTokens = assistant?.settings?.maxTokens || DEFAULT_MAX_TOKENS
- const budgetTokens = Math.trunc(Math.max(Math.min(maxTokens * effortRatio, 32000), 1024))
+ const budgetTokens = assistant?.settings?.thinking_budget || maxTokens
+
+ if (budgetTokens > maxTokens) {
+ return undefined
+ }
return {
type: 'enabled',
@@ -200,7 +183,7 @@ export default class AnthropicProvider extends BaseProvider {
top_p: this.getTopP(assistant, model),
system: systemPrompt,
// @ts-ignore thinking
- thinking: this.getReasoningEffort(assistant, model),
+ thinking: this.getBudgetToken(assistant, model),
...this.getCustomParameters(assistant)
}
diff --git a/src/renderer/src/providers/AiProvider/OpenAIProvider.ts b/src/renderer/src/providers/AiProvider/OpenAIProvider.ts
index 87711d8dcf..88e3990c7b 100644
--- a/src/renderer/src/providers/AiProvider/OpenAIProvider.ts
+++ b/src/renderer/src/providers/AiProvider/OpenAIProvider.ts
@@ -1,10 +1,10 @@
import {
getOpenAIWebSearchParams,
- isGrokReasoningModel,
isHunyuanSearchModel,
isOpenAIWebSearch,
isReasoningModel,
isSupportedModel,
+ isSupportedReasoningEffortGrokModel,
isSupportedReasoningEffortModel,
isSupportedReasoningEffortOpenAIModel,
isSupportedThinkingTokenClaudeModel,
@@ -264,11 +264,15 @@ export default class OpenAIProvider extends BaseProvider {
if (model.provider === 'openrouter') {
if (isSupportedReasoningEffortModel(model)) {
return {
- reasoning_effort: assistant?.settings?.reasoning_effort
+ reasoning: {
+ effort: assistant?.settings?.reasoning_effort
+ }
}
} else if (isSupportedThinkingTokenModel(model)) {
return {
- max_tokens: assistant?.settings?.thinking_budget
+ reasoning: {
+ max_tokens: assistant?.settings?.thinking_budget
+ }
}
}
}
@@ -286,23 +290,39 @@ export default class OpenAIProvider extends BaseProvider {
}
}
- if (isGrokReasoningModel(model)) {
- return {
- reasoning_effort: assistant?.settings?.reasoning_effort
+ if (isSupportedReasoningEffortGrokModel(model)) {
+ if (enableThinking) {
+ return {
+ reasoning_effort: assistant?.settings?.reasoning_effort
+ }
+ } else {
+ return {}
}
}
if (isSupportedReasoningEffortOpenAIModel(model)) {
- return {
- reasoning_effort: assistant?.settings?.reasoning_effort
+ if (enableThinking) {
+ return {
+ reasoning_effort: assistant?.settings?.reasoning_effort
+ }
+ } else {
+ return {}
}
}
if (isSupportedThinkingTokenClaudeModel(model)) {
- return {
- thinking: {
- type: 'enabled',
- budget_tokens: assistant?.settings?.thinking_budget
+ if (enableThinking) {
+ return {
+ thinking: {
+ type: 'enabled',
+ budget_tokens: assistant?.settings?.thinking_budget
+ }
+ }
+ } else {
+ return {
+ thinking: {
+ type: 'disabled'
+ }
}
}
}