fix(ui): gate thinking toggle on supportsThinking (broad), not supportsReasoningEffort

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

View File

@ -23,21 +23,42 @@ public class ModelInfoDTO {
private String probeError;
/**
* RFC-049 PR-1-UI: whether this model's {@code ModelFamily} accepts the
* {@code reasoning_effort} parameter. Derived from the model name on the
* server side so the frontend can gray out the "thinking depth" selector
* for chat-type models that don't support thinking a product contract,
* not a runtime toggle.
*
* <p>{@code true} only for the OpenAI reasoning family (gpt-5*, o1*, o3*, o4*).
* RFC-049 PR-1-UI (narrow): whether this model's {@code ModelFamily} accepts the
* OpenAI {@code reasoning_effort} parameter specifically. True <em>only</em> for
* the OpenAI reasoning family (gpt-5, o1, o3, o4 variants). Retained for callers
* that need to know parameter-level compatibility; the UI "deep thinking" toggle
* should use {@link #supportsThinking} instead.
*/
private boolean supportsReasoningEffort;
/**
* RFC-049 PR-1-UI (broad): whether this model supports <em>any</em> form of
* deep thinking either OpenAI-style via {@code reasoning_effort}, provider-
* native (Kimi K2.x, DeepSeek-Reasoner, qwen-thinking), or Anthropic extended
* thinking (Claude family). This is what the UI "deep thinking" toggle reads.
*
* <p>Derived from the model name so every construction site stays consistent.
*/
private boolean supportsThinking;
public ModelInfoDTO(String id, String name) {
this.id = id;
this.name = name;
// RFC-049 PR-1-UI: derived from id (the model name) so every construction
// site populates the capability consistently.
this.supportsReasoningEffort = ModelFamily.detect(id).supportsReasoningEffort();
this.supportsThinking = computeSupportsThinking(id);
}
/**
* Thinking capability at the product level (what the UI toggle should reflect):
* any model family that has a thinking mode, regardless of how it is triggered
* (parameter vs. model-native vs. Anthropic extended thinking).
*/
private static boolean computeSupportsThinking(String modelName) {
if (modelName == null || modelName.isBlank()) return false;
ModelFamily family = ModelFamily.detect(modelName);
if (family.isThinking()) return true; // OPENAI_REASONING / KIMI_THINKING / DEEPSEEK_REASONER / GENERIC_THINKING
// Anthropic Claude supports extended thinking via AnthropicChatOptions.thinking;
// ModelFamily doesn't model non-OpenAI-compatible providers so match by name.
return modelName.toLowerCase().contains("claude");
}
}

View File

@ -587,13 +587,17 @@ export interface ProviderModelInfo {
/** 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.
* RFC-049 PR-1-UI (narrow): whether the model accepts the OpenAI
* `reasoning_effort` parameter. True only for OpenAI reasoning family.
*/
supportsReasoningEffort?: boolean
/**
* RFC-049 PR-1-UI (broad): whether the model supports any form of deep
* thinking (OpenAI reasoning_effort, Kimi/DeepSeek native thinking,
* Anthropic extended thinking). This is the field the UI "thinking depth"
* toggle should gate on.
*/
supportsThinking?: boolean
}
export interface ProviderInfo {

View File

@ -252,7 +252,7 @@
@deny="handleDeny"
:enable-talk-mode="!!selectedAgentId"
:thinking-enabled="thinkingEnabled"
:thinking-supported="currentModelSupportsReasoningEffort"
:thinking-supported="currentModelSupportsThinking"
@toggle-thinking="thinkingEnabled = !thinkingEnabled"
@talk="showTalkMode = true"
/>
@ -637,15 +637,17 @@ const currentRuntimeModel = computed(() => {
})
/**
* 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).
* RFC-049 PR-1-UI: whether the active runtime model supports <em>any</em> form
* of deep thinking (OpenAI reasoning_effort / Kimi native / DeepSeek-Reasoner
* native / Anthropic extended thinking). Drives the enable/disable state of
* the thinking-depth toggle in ChatInput.
*
* Source of truth: ProviderModelInfo.supportsReasoningEffort (set by backend via
* ModelFamily.detect(modelName) in ModelInfoDTO).
* Reads the broad capability (`supportsThinking`) from ProviderModelInfo,
* populated server-side in ModelInfoDTO. The narrow `supportsReasoningEffort`
* only covers OpenAI gpt-5/o1/o3/o4 and would wrongly gray out Kimi K2.x,
* DeepSeek-Reasoner, and Claude all of which legitimately support thinking.
*/
const currentModelSupportsReasoningEffort = computed<boolean>(() => {
const currentModelSupportsThinking = computed<boolean>(() => {
const providerId = activeModels.value?.activeLlm?.providerId
const modelName = activeModels.value?.activeLlm?.model
if (!providerId || !modelName) return false
@ -653,7 +655,7 @@ const currentModelSupportsReasoningEffort = computed<boolean>(() => {
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)
return Boolean(hit?.supportsThinking)
})
const userInitial = computed(() => (localStorage.getItem('username') || 'U').charAt(0).toUpperCase())