mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-07-23 11:10:55 -05:00
ui: Add a "Default" option for the reasoning selector (#25846)
* ui: add Default reasoning option that defers to the server The webui always injected enable_thinking, overriding the chat template default and the --reasoning flag, breaking models that reason unconditionally (e.g. Gemma 4 E4B) on a fresh client. Default sends nothing so the server decides, Off and effort levels force the value as before. All choices are remembered. Also remove the boolean thinking API from the conversations store and drop ChatFormReasoningEffortSubmenu.svelte (dead code). * ui: close the whole menu tree on reasoning level selection The reasoning levels were raw buttons inside the SubContent, so selecting one only closed the submenu via manual state while the root dropdown stayed open. DropdownMenu.Item closes the full tree on select like the sibling entries and brings native keyboard navigation. * ui: prevent the add menu tooltip from flashing when the dropdown closes
This commit is contained in:
@@ -71,7 +71,9 @@
|
||||
|
||||
<div class="flex items-center gap-1 {className}">
|
||||
<DropdownMenu.Root bind:open={dropdownOpen}>
|
||||
<Tooltip.Root>
|
||||
<!-- ignoreNonKeyboardFocus prevents the tooltip from flashing when the
|
||||
menu closes and focus returns to the trigger -->
|
||||
<Tooltip.Root ignoreNonKeyboardFocus>
|
||||
<Tooltip.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<DropdownMenu.Trigger
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { useReasoningMenu } from '$lib/hooks/use-reasoning-menu.svelte';
|
||||
|
||||
let subOpen = $state(false);
|
||||
|
||||
const reasoning = useReasoningMenu();
|
||||
</script>
|
||||
|
||||
{#if reasoning.modelSupportsThinking}
|
||||
<DropdownMenu.Sub bind:open={subOpen}>
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
{#if reasoning.thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
{:else if reasoning.isOff}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{:else}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span
|
||||
@@ -27,7 +27,7 @@
|
||||
Reasoning
|
||||
|
||||
<span class="capitalize text-muted-foreground">
|
||||
{reasoning.thinkingEnabled ? reasoning.currentEffort : 'off'}
|
||||
{reasoning.currentEffort}
|
||||
</span>
|
||||
</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
@@ -37,14 +37,13 @@
|
||||
>
|
||||
{#each reasoning.levels as level (level.value)}
|
||||
{@const tokenLabel = reasoning.tokenLabel(level)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-3 rounded-md px-2 py-1.75 text-left text-sm transition-colors hover:bg-accent"
|
||||
class:bg-accent={reasoning.isSelected(level)}
|
||||
onclick={() => {
|
||||
reasoning.select(level);
|
||||
subOpen = false;
|
||||
}}
|
||||
<DropdownMenu.Item
|
||||
class="flex w-full cursor-pointer items-center gap-3 rounded-md px-2 py-1.75 text-left text-sm transition-colors hover:bg-accent {reasoning.isSelected(
|
||||
level
|
||||
)
|
||||
? 'bg-accent'
|
||||
: ''}"
|
||||
onclick={() => reasoning.select(level)}
|
||||
>
|
||||
{#if reasoning.isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
@@ -70,7 +69,7 @@
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
|
||||
@@ -116,14 +116,16 @@
|
||||
|
||||
{#if reasoning.thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
{:else if reasoning.isOff}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{:else}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">Reasoning</span>
|
||||
|
||||
<span class="text-xs capitalize text-muted-foreground">
|
||||
{reasoning.thinkingEnabled ? reasoning.currentEffort : 'off'}
|
||||
{reasoning.currentEffort}
|
||||
</span>
|
||||
</Collapsible.Trigger>
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Check, Info, Lightbulb, LightbulbOff } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { ReasoningEffort, MessageRole } from '$lib/enums';
|
||||
import { REASONING_EFFORT_TOKENS } from '$lib/constants/reasoning-effort-tokens';
|
||||
import { REASONING_EFFORT_LEVELS } from '$lib/constants/reasoning-effort';
|
||||
import type { ReasoningEffortLevel } from '$lib/types';
|
||||
import { DIALOG_SUBMENU_CONTENT, ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import {
|
||||
modelsStore,
|
||||
checkModelSupportsThinking,
|
||||
supportsThinking,
|
||||
propsCacheVersion,
|
||||
loadedModelIds
|
||||
} from '$lib/stores/models.svelte';
|
||||
import { chatStore } from '$lib/stores/chat.svelte';
|
||||
import { conversationsStore, activeMessages } from '$lib/stores/conversations.svelte';
|
||||
import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
import type { DatabaseMessage } from '$lib/types/database';
|
||||
|
||||
let thinkingEnabled = $derived(conversationsStore.getThinkingEnabled());
|
||||
let currentEffort = $derived(conversationsStore.getReasoningEffort());
|
||||
let isOff = $derived(!thinkingEnabled);
|
||||
let subOpen = $state(false);
|
||||
|
||||
// Get conversation model from message history
|
||||
let conversationModel = $derived(
|
||||
chatStore.getConversationModel(activeMessages() as DatabaseMessage[])
|
||||
);
|
||||
|
||||
let modelSupportsThinkingFromMessages = $derived.by(() => {
|
||||
const modelId = isRouterMode() ? modelsStore.selectedModelName || conversationModel : null;
|
||||
if (!modelId) return false;
|
||||
|
||||
const messages = conversationsStore.activeMessages;
|
||||
|
||||
return messages.some(
|
||||
(m: DatabaseMessage) =>
|
||||
m.role === MessageRole.ASSISTANT && m.model === modelId && !!m.reasoningContent
|
||||
);
|
||||
});
|
||||
|
||||
let modelSupportsThinking = $derived.by(() => {
|
||||
loadedModelIds();
|
||||
propsCacheVersion();
|
||||
|
||||
if (isRouterMode()) {
|
||||
const modelId = modelsStore.selectedModelName || conversationModel;
|
||||
return checkModelSupportsThinking(modelId ?? '') || modelSupportsThinkingFromMessages;
|
||||
}
|
||||
|
||||
return supportsThinking() || modelSupportsThinkingFromMessages;
|
||||
});
|
||||
|
||||
function isSelected(item: ReasoningEffortLevel): boolean {
|
||||
if (item.isOff) return isOff;
|
||||
|
||||
return thinkingEnabled && currentEffort === item.value;
|
||||
}
|
||||
|
||||
function handleSelection(item: ReasoningEffortLevel) {
|
||||
if (item.isOff) {
|
||||
conversationsStore.setThinkingEnabled(false);
|
||||
} else {
|
||||
conversationsStore.setThinkingEnabled(true);
|
||||
conversationsStore.setReasoningEffort(item.value as ReasoningEffort);
|
||||
}
|
||||
subOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modelSupportsThinking}
|
||||
<DropdownMenu.Sub bind:open={subOpen}>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
{#if thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">Thinking</span>
|
||||
|
||||
{#if thinkingEnabled}
|
||||
<span class="text-xs text-muted-foreground">{currentEffort}</span>
|
||||
{:else}
|
||||
<span class="text-xs text-muted-foreground">off</span>
|
||||
{/if}
|
||||
</DropdownMenu.SubTrigger>
|
||||
|
||||
<DropdownMenu.SubContent class={DIALOG_SUBMENU_CONTENT}>
|
||||
{#each REASONING_EFFORT_LEVELS as level (level.value)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-2"
|
||||
class:bg-accent={isSelected(level)}
|
||||
onclick={() => handleSelection(level)}
|
||||
>
|
||||
<span class="flex-1 text-left">{level.label}</span>
|
||||
|
||||
{#if !level.isOff}
|
||||
<span class="text-[11px] text-muted-foreground opacity-60">
|
||||
{REASONING_EFFORT_TOKENS[level.value] === -1
|
||||
? 'Unlimited'
|
||||
: `Max ${REASONING_EFFORT_TOKENS[level.value].toLocaleString()} tokens`}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if level.hasInfo}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Info class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side="left">
|
||||
<p>Maximum thinking effort with extended context usage</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
{/if}
|
||||
@@ -6,6 +6,7 @@ import type { ReasoningEffortLevel } from '$lib/types';
|
||||
* Keys match the ReasoningEffort enum values for type-safe lookups.
|
||||
*/
|
||||
export const REASONING_EFFORT_LABELS: Record<string, string> = {
|
||||
[ReasoningEffort.DEFAULT]: 'Default',
|
||||
[ReasoningEffort.OFF]: 'Off',
|
||||
[ReasoningEffort.LOW]: 'Low',
|
||||
[ReasoningEffort.MEDIUM]: 'Medium',
|
||||
@@ -14,7 +15,8 @@ export const REASONING_EFFORT_LABELS: Record<string, string> = {
|
||||
};
|
||||
|
||||
export const REASONING_EFFORT_LEVELS: ReasoningEffortLevel[] = [
|
||||
{ value: ReasoningEffort.OFF, label: 'Off', isOff: true },
|
||||
{ value: ReasoningEffort.DEFAULT, label: 'Default' },
|
||||
{ value: ReasoningEffort.OFF, label: 'Off' },
|
||||
{ value: ReasoningEffort.LOW, label: 'Low' },
|
||||
{ value: ReasoningEffort.MEDIUM, label: 'Medium' },
|
||||
{ value: ReasoningEffort.HIGH, label: 'High' },
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* These values are sent to the server and mapped to token budgets.
|
||||
*/
|
||||
export enum ReasoningEffort {
|
||||
DEFAULT = 'default',
|
||||
OFF = 'off',
|
||||
LOW = 'low',
|
||||
MEDIUM = 'medium',
|
||||
|
||||
@@ -17,6 +17,7 @@ import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
export interface UseReasoningMenuReturn {
|
||||
readonly modelSupportsThinking: boolean;
|
||||
readonly thinkingEnabled: boolean;
|
||||
readonly isOff: boolean;
|
||||
readonly currentEffort: ReasoningEffort;
|
||||
readonly levels: ReasoningEffortLevel[];
|
||||
isSelected(level: ReasoningEffortLevel): boolean;
|
||||
@@ -59,8 +60,10 @@ export function useReasoningMenu(): UseReasoningMenuReturn {
|
||||
return supportsThinking() || modelSupportsThinkingFromMessages;
|
||||
});
|
||||
|
||||
const thinkingEnabled = $derived(conversationsStore.getThinkingEnabled());
|
||||
const currentEffort = $derived(conversationsStore.getReasoningEffort());
|
||||
const thinkingEnabled = $derived(
|
||||
currentEffort !== ReasoningEffort.OFF && currentEffort !== ReasoningEffort.DEFAULT
|
||||
);
|
||||
|
||||
return {
|
||||
get modelSupportsThinking() {
|
||||
@@ -69,6 +72,9 @@ export function useReasoningMenu(): UseReasoningMenuReturn {
|
||||
get thinkingEnabled() {
|
||||
return thinkingEnabled;
|
||||
},
|
||||
get isOff() {
|
||||
return currentEffort === ReasoningEffort.OFF;
|
||||
},
|
||||
get currentEffort() {
|
||||
return currentEffort;
|
||||
},
|
||||
@@ -76,20 +82,15 @@ export function useReasoningMenu(): UseReasoningMenuReturn {
|
||||
return REASONING_EFFORT_LEVELS;
|
||||
},
|
||||
isSelected(level: ReasoningEffortLevel): boolean {
|
||||
if (level.isOff) return !thinkingEnabled;
|
||||
return thinkingEnabled && currentEffort === level.value;
|
||||
return currentEffort === level.value;
|
||||
},
|
||||
tokenLabel(level: ReasoningEffortLevel): string | null {
|
||||
if (level.isOff) return null;
|
||||
if (level.value === ReasoningEffort.DEFAULT) return 'Model default';
|
||||
const tokens = REASONING_EFFORT_TOKENS[level.value];
|
||||
if (tokens === undefined) return null;
|
||||
return tokens === -1 ? 'Unlimited' : `Max ${tokens.toLocaleString()} tokens`;
|
||||
},
|
||||
select(level: ReasoningEffortLevel): void {
|
||||
if (level.isOff) {
|
||||
conversationsStore.setThinkingEnabled(false);
|
||||
return;
|
||||
}
|
||||
conversationsStore.setThinkingEnabled(true);
|
||||
conversationsStore.setReasoningEffort(level.value as ReasoningEffort);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,10 +271,14 @@ export class ChatService {
|
||||
const reasoningBudgetTokens =
|
||||
enableThinking && reasoningEffort ? (REASONING_EFFORT_TOKENS[reasoningEffort] ?? -1) : -1;
|
||||
|
||||
requestBody.chat_template_kwargs = {
|
||||
...(requestBody.chat_template_kwargs ?? {}),
|
||||
enable_thinking: enableThinking
|
||||
};
|
||||
// an explicit user choice injects the kwarg, otherwise it is omitted so
|
||||
// the server default applies (--reasoning flag or chat template)
|
||||
if (enableThinking !== undefined) {
|
||||
requestBody.chat_template_kwargs = {
|
||||
...(requestBody.chat_template_kwargs ?? {}),
|
||||
enable_thinking: enableThinking
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoningBudgetTokens >= 0) {
|
||||
requestBody.thinking_budget_tokens = reasoningBudgetTokens;
|
||||
|
||||
@@ -2373,9 +2373,12 @@ class ChatStore {
|
||||
|
||||
if (currentConfig.excludeReasoningFromContext) apiOptions.excludeReasoningFromContext = true;
|
||||
|
||||
apiOptions.enableThinking = conversationsStore.getThinkingEnabled();
|
||||
// an explicit reasoning choice overrides the server default, DEFAULT sends nothing
|
||||
const effort = conversationsStore.getReasoningEffort();
|
||||
if (effort !== ReasoningEffort.OFF) apiOptions.reasoningEffort = effort;
|
||||
if (effort !== ReasoningEffort.DEFAULT) {
|
||||
apiOptions.enableThinking = effort !== ReasoningEffort.OFF;
|
||||
if (effort !== ReasoningEffort.OFF) apiOptions.reasoningEffort = effort;
|
||||
}
|
||||
|
||||
if (hasValue(currentConfig.temperature))
|
||||
apiOptions.temperature = Number(currentConfig.temperature);
|
||||
|
||||
@@ -80,25 +80,17 @@ class ConversationsStore {
|
||||
/** Whether the store has been initialized */
|
||||
isInitialized = $state(false);
|
||||
|
||||
/** Global (non-conversation-specific) thinking toggle default, derived from reasoning effort */
|
||||
pendingThinkingEnabled = $state(false);
|
||||
|
||||
/** Global (non-conversation-specific) reasoning effort default */
|
||||
pendingReasoningEffort = $state<ReasoningEffort | ReasoningEffort.OFF>(
|
||||
ConversationsStore.loadReasoningEffortDefault()
|
||||
);
|
||||
pendingReasoningEffort = $state<ReasoningEffort>(ConversationsStore.loadReasoningEffortDefault());
|
||||
|
||||
/** Last non-off reasoning effort, restored when re-enabling thinking globally */
|
||||
private lastNonOffEffort: ReasoningEffort | null = null;
|
||||
|
||||
/** Load reasoning effort default from localStorage */
|
||||
private static loadReasoningEffortDefault(): ReasoningEffort | ReasoningEffort.OFF {
|
||||
if (typeof globalThis.localStorage === 'undefined') return ReasoningEffort.OFF;
|
||||
/** Load reasoning effort default from localStorage, DEFAULT defers to the server */
|
||||
private static loadReasoningEffortDefault(): ReasoningEffort {
|
||||
if (typeof globalThis.localStorage === 'undefined') return ReasoningEffort.DEFAULT;
|
||||
try {
|
||||
const raw = localStorage.getItem(REASONING_EFFORT_DEFAULT_LOCALSTORAGE_KEY);
|
||||
return (raw as ReasoningEffort | ReasoningEffort.OFF) || ReasoningEffort.OFF;
|
||||
return (raw as ReasoningEffort) || ReasoningEffort.DEFAULT;
|
||||
} catch {
|
||||
return ReasoningEffort.OFF;
|
||||
return ReasoningEffort.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,17 +227,10 @@ class ConversationsStore {
|
||||
// servers without a per-conversation override to `mcpServers[i].enabled`,
|
||||
// and only explicit toggles are stored on the conversation.
|
||||
|
||||
// Inherit global thinking/reasoning defaults into the new conversation
|
||||
const thinkingEnabled = this.getThinkingEnabled();
|
||||
conversation.thinkingEnabled = thinkingEnabled;
|
||||
conversation.reasoningEffort =
|
||||
this.pendingReasoningEffort === ReasoningEffort.OFF ? undefined : this.pendingReasoningEffort;
|
||||
// Inherit the global reasoning default into the new conversation
|
||||
conversation.reasoningEffort = this.pendingReasoningEffort;
|
||||
await DatabaseService.updateConversation(conversation.id, {
|
||||
thinkingEnabled,
|
||||
reasoningEffort:
|
||||
this.pendingReasoningEffort === ReasoningEffort.OFF
|
||||
? undefined
|
||||
: this.pendingReasoningEffort
|
||||
reasoningEffort: this.pendingReasoningEffort
|
||||
});
|
||||
|
||||
this.conversations = [conversation, ...this.conversations];
|
||||
@@ -793,63 +778,21 @@ class ConversationsStore {
|
||||
await this.setMcpServerOverride(serverId, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective thinking-enabled state for the active conversation.
|
||||
* Returns the conversation override if set, otherwise the global default.
|
||||
*/
|
||||
getThinkingEnabled(): boolean {
|
||||
if (this.activeConversation) {
|
||||
if (this.activeConversation.thinkingEnabled !== undefined) {
|
||||
return this.activeConversation.thinkingEnabled;
|
||||
}
|
||||
}
|
||||
return this.getReasoningEffort() !== ReasoningEffort.OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thinking-enabled state for the active conversation.
|
||||
* If no conversation exists, stores the global default.
|
||||
* @param enabled - The enabled state
|
||||
*/
|
||||
async setThinkingEnabled(enabled: boolean): Promise<void> {
|
||||
if (!this.activeConversation) {
|
||||
if (enabled) {
|
||||
const effort = this.lastNonOffEffort ?? ReasoningEffort.LOW;
|
||||
this.pendingReasoningEffort = effort;
|
||||
this.saveReasoningEffortDefaults();
|
||||
} else {
|
||||
if (this.pendingReasoningEffort !== ReasoningEffort.OFF) {
|
||||
this.lastNonOffEffort = this.pendingReasoningEffort;
|
||||
}
|
||||
this.pendingReasoningEffort = ReasoningEffort.OFF;
|
||||
this.saveReasoningEffortDefaults();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeConversation = {
|
||||
...this.activeConversation,
|
||||
thinkingEnabled: enabled
|
||||
};
|
||||
|
||||
await DatabaseService.updateConversation(this.activeConversation.id, {
|
||||
thinkingEnabled: enabled
|
||||
});
|
||||
|
||||
const convIndex = this.conversations.findIndex((c) => c.id === this.activeConversation!.id);
|
||||
if (convIndex !== -1) {
|
||||
this.conversations[convIndex].thinkingEnabled = enabled;
|
||||
this.conversations = [...this.conversations];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective reasoning effort for the active conversation.
|
||||
* Returns the conversation override if set, otherwise the global default.
|
||||
* DEFAULT means no override is sent and the server decides.
|
||||
*/
|
||||
getReasoningEffort(): ReasoningEffort | ReasoningEffort.OFF {
|
||||
getReasoningEffort(): ReasoningEffort {
|
||||
if (this.activeConversation) {
|
||||
return this.activeConversation.reasoningEffort ?? this.pendingReasoningEffort;
|
||||
if (this.activeConversation.reasoningEffort !== undefined) {
|
||||
return this.activeConversation.reasoningEffort;
|
||||
}
|
||||
// conversations created before the tri-state store an explicit
|
||||
// opt-out only as thinkingEnabled = false
|
||||
if (this.activeConversation.thinkingEnabled === false) {
|
||||
return ReasoningEffort.OFF;
|
||||
}
|
||||
}
|
||||
return this.pendingReasoningEffort;
|
||||
}
|
||||
@@ -857,7 +800,7 @@ class ConversationsStore {
|
||||
/**
|
||||
* Sets the reasoning effort for the active conversation.
|
||||
* If no conversation exists, stores the global default.
|
||||
* @param effort - The effort level ('low' | 'medium' | 'high' | 'max')
|
||||
* @param effort - The effort level ('default' | 'off' | 'low' | 'medium' | 'high' | 'max')
|
||||
*/
|
||||
async setReasoningEffort(effort: ReasoningEffort): Promise<void> {
|
||||
if (!this.activeConversation) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export interface ReasoningEffortLevel {
|
||||
value: string;
|
||||
label: string;
|
||||
isOff?: boolean;
|
||||
hasInfo?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user