feat(ui): gray out thinking depth button on unsupported models

This commit is contained in:
matevip 2026-04-24 18:16:23 +08:00
parent c249dbcb17
commit 5c98d1120e
5 changed files with 49 additions and 5 deletions

View File

@ -136,14 +136,16 @@
<el-icon><Paperclip /></el-icon>
</button>
<!-- 深度思考开关 -->
<!-- 深度思考开关 (RFC-049 PR-1-UI: 不支持 reasoning_effort 的模型灰态) -->
<button
type="button"
class="action-btn thinking-btn"
:class="{ active: thinkingEnabled }"
:disabled="disabled"
@click="emit('toggle-thinking')"
:title="thinkingEnabled ? t('chat.thinkingOn') : t('chat.thinkingOff')"
:class="{ active: thinkingEnabled && thinkingSupported, unsupported: !thinkingSupported }"
:disabled="disabled || !thinkingSupported"
@click="thinkingSupported && emit('toggle-thinking')"
:title="!thinkingSupported
? t('chat.thinkingUnsupported')
: (thinkingEnabled ? t('chat.thinkingOn') : t('chat.thinkingOff'))"
>
<el-icon><MagicStick /></el-icon>
</button>
@ -239,6 +241,11 @@ interface Props {
enableTalkMode?: boolean
/** 深度思考开关状态 */
thinkingEnabled?: boolean
/**
* RFC-049 PR-1-UI: 当前 runtime 模型是否支持 reasoning_effortfalse 时按钮灰掉
* 不响应点击tooltip 提示当前模型不支持深度思考默认 true 以保持向后兼容
*/
thinkingSupported?: boolean
}
const props = withDefaults(defineProps<Props>(), {
@ -257,6 +264,7 @@ const props = withDefaults(defineProps<Props>(), {
queueSize: 0,
enableTalkMode: false,
thinkingEnabled: false,
thinkingSupported: true,
})
const emit = defineEmits<{
@ -598,6 +606,11 @@ defineExpose({
border-radius: 50%;
background: var(--el-color-primary, #409eff);
}
/* RFC-049 PR-1-UI: model doesn't support reasoning_effort — stronger grayed state */
.thinking-btn.unsupported {
opacity: 0.35;
cursor: not-allowed;
}
.thinking-btn:hover:not(:disabled) {
color: var(--el-color-primary, #409eff);
background: var(--el-color-primary-light-9, rgba(64, 158, 255, 0.08));

View File

@ -165,6 +165,7 @@ export default {
messagePlaceholder: 'Type a message... (Enter to send, Shift+Enter for new line)',
thinkingOn: 'Deep thinking enabled',
thinkingOff: 'Click to enable deep thinking',
thinkingUnsupported: 'Current model does not support deep thinking',
subtitle: 'Your intelligent AI assistant powered by Spring AI Alibaba',
// Queue related
queuedSending: 'Sending queued message...',

View File

@ -165,6 +165,7 @@ export default {
messagePlaceholder: '输入消息... (Enter 发送, Shift+Enter 换行)',
thinkingOn: '深度思考已开启',
thinkingOff: '点击开启深度思考',
thinkingUnsupported: '当前模型不支持深度思考',
subtitle: '基于 Spring AI Alibaba 的智能 AI 助手',
// 排队相关
queuedSending: '正在发送排队消息...',

View File

@ -586,6 +586,14 @@ export interface ProviderModelInfo {
probeOk?: boolean
/** Short error message when probeOk=false */
probeError?: string
/**
* RFC-049 PR-1-UI: whether the model's `ModelFamily` accepts the
* `reasoning_effort` parameter. Derived server-side from the model name;
* true only for the OpenAI reasoning family (gpt-5, o1, o3, o4 variants).
* Used by the thinking-depth selector to gray itself out on non-reasoning
* models.
*/
supportsReasoningEffort?: boolean
}
export interface ProviderInfo {

View File

@ -252,6 +252,7 @@
@deny="handleDeny"
:enable-talk-mode="!!selectedAgentId"
:thinking-enabled="thinkingEnabled"
:thinking-supported="currentModelSupportsReasoningEffort"
@toggle-thinking="thinkingEnabled = !thinkingEnabled"
@talk="showTalkMode = true"
/>
@ -635,6 +636,26 @@ const currentRuntimeModel = computed(() => {
return currentAgent.value?.modelName || 'default'
})
/**
* RFC-049 PR-1-UI: whether the active runtime model supports `reasoning_effort`.
* Drives the enable/disable state of the thinking-depth toggle in ChatInput:
* chat-type models that don't support thinking must not honor the "deep thinking"
* selection (product contract UI reflects the backend gate).
*
* Source of truth: ProviderModelInfo.supportsReasoningEffort (set by backend via
* ModelFamily.detect(modelName) in ModelInfoDTO).
*/
const currentModelSupportsReasoningEffort = computed<boolean>(() => {
const providerId = activeModels.value?.activeLlm?.providerId
const modelName = activeModels.value?.activeLlm?.model
if (!providerId || !modelName) return false
const provider = providers.value.find((p) => p.id === providerId)
if (!provider) return false
const all = [...(provider.models || []), ...(provider.extraModels || [])]
const hit = all.find((m) => m.id === modelName || m.name === modelName)
return Boolean(hit?.supportsReasoningEffort)
})
const userInitial = computed(() => (localStorage.getItem('username') || 'U').charAt(0).toUpperCase())
const activeModelValue = computed(() => {