feat: restore run mode personalization
This commit is contained in:
parent
c1e9f33208
commit
eb7ccf1dd2
@ -357,6 +357,15 @@ class MainTerminal:
|
|||||||
self.tool_category_states[key] = False if key in disabled_categories else category.default_enabled
|
self.tool_category_states[key] = False if key in disabled_categories else category.default_enabled
|
||||||
self._refresh_disabled_tools()
|
self._refresh_disabled_tools()
|
||||||
|
|
||||||
|
preferred_mode = effective_config.get("default_run_mode")
|
||||||
|
if isinstance(preferred_mode, str):
|
||||||
|
normalized_mode = preferred_mode.strip().lower()
|
||||||
|
if normalized_mode in {"fast", "thinking", "deep"} and normalized_mode != self.run_mode:
|
||||||
|
try:
|
||||||
|
self.set_run_mode(normalized_mode)
|
||||||
|
except ValueError:
|
||||||
|
logger.warning("忽略无效默认运行模式: %s", preferred_mode)
|
||||||
|
|
||||||
|
|
||||||
def _handle_read_tool(self, arguments: Dict) -> Dict:
|
def _handle_read_tool(self, arguments: Dict) -> Dict:
|
||||||
"""集中处理 read_file 工具的三种模式。"""
|
"""集中处理 read_file 工具的三种模式。"""
|
||||||
|
|||||||
@ -48,13 +48,21 @@ class WebTerminal(MainTerminal):
|
|||||||
self,
|
self,
|
||||||
project_path: str,
|
project_path: str,
|
||||||
thinking_mode: bool = False,
|
thinking_mode: bool = False,
|
||||||
|
run_mode: Optional[str] = None,
|
||||||
message_callback: Optional[Callable] = None,
|
message_callback: Optional[Callable] = None,
|
||||||
data_dir: Optional[str] = None,
|
data_dir: Optional[str] = None,
|
||||||
container_session: Optional["ContainerHandle"] = None,
|
container_session: Optional["ContainerHandle"] = None,
|
||||||
usage_tracker: Optional[object] = None,
|
usage_tracker: Optional[object] = None,
|
||||||
):
|
):
|
||||||
# 调用父类初始化(包含对话持久化功能)
|
# 调用父类初始化(包含对话持久化功能)
|
||||||
super().__init__(project_path, thinking_mode, data_dir=data_dir, container_session=container_session, usage_tracker=usage_tracker)
|
super().__init__(
|
||||||
|
project_path,
|
||||||
|
thinking_mode,
|
||||||
|
run_mode=run_mode,
|
||||||
|
data_dir=data_dir,
|
||||||
|
container_session=container_session,
|
||||||
|
usage_tracker=usage_tracker
|
||||||
|
)
|
||||||
|
|
||||||
# Web特有属性
|
# Web特有属性
|
||||||
self.message_callback = message_callback
|
self.message_callback = message_callback
|
||||||
@ -74,7 +82,7 @@ class WebTerminal(MainTerminal):
|
|||||||
)
|
)
|
||||||
|
|
||||||
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
||||||
print(f"[WebTerminal] 思考模式: {'开启' if thinking_mode else '关闭'}")
|
print(f"[WebTerminal] 初始模式: {self.run_mode}")
|
||||||
print(f"[WebTerminal] 对话管理已就绪")
|
print(f"[WebTerminal] 对话管理已就绪")
|
||||||
|
|
||||||
# 设置token更新回调
|
# 设置token更新回调
|
||||||
@ -88,12 +96,13 @@ class WebTerminal(MainTerminal):
|
|||||||
# 新增:对话管理相关方法(Web版本)
|
# 新增:对话管理相关方法(Web版本)
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|
||||||
def create_new_conversation(self, thinking_mode: bool = None) -> Dict:
|
def create_new_conversation(self, thinking_mode: bool = None, run_mode: Optional[str] = None) -> Dict:
|
||||||
"""
|
"""
|
||||||
创建新对话(Web版本)
|
创建新对话(Web版本)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
thinking_mode: 思考模式,None则使用当前设置
|
thinking_mode: 思考模式,None则使用当前设置
|
||||||
|
run_mode: 显式的运行模式(fast/thinking/deep)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict: 包含新对话信息
|
Dict: 包含新对话信息
|
||||||
@ -101,10 +110,18 @@ class WebTerminal(MainTerminal):
|
|||||||
if thinking_mode is None:
|
if thinking_mode is None:
|
||||||
thinking_mode = self.thinking_mode
|
thinking_mode = self.thinking_mode
|
||||||
|
|
||||||
|
if isinstance(run_mode, str):
|
||||||
|
try:
|
||||||
|
self.set_run_mode(run_mode)
|
||||||
|
thinking_mode = self.thinking_mode
|
||||||
|
except ValueError:
|
||||||
|
logger.warning("无效的 run_mode 参数: %s", run_mode)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conversation_id = self.context_manager.start_new_conversation(
|
conversation_id = self.context_manager.start_new_conversation(
|
||||||
project_path=self.project_path,
|
project_path=self.project_path,
|
||||||
thinking_mode=thinking_mode
|
thinking_mode=thinking_mode,
|
||||||
|
run_mode=self.run_mode
|
||||||
)
|
)
|
||||||
|
|
||||||
# 重置相关状态
|
# 重置相关状态
|
||||||
@ -268,6 +285,7 @@ class WebTerminal(MainTerminal):
|
|||||||
"project_path": self.project_path,
|
"project_path": self.project_path,
|
||||||
"thinking_mode": self.thinking_mode,
|
"thinking_mode": self.thinking_mode,
|
||||||
"thinking_status": self.get_thinking_mode_status(),
|
"thinking_status": self.get_thinking_mode_status(),
|
||||||
|
"run_mode": self.run_mode,
|
||||||
"context": {
|
"context": {
|
||||||
"usage_percent": context_status['usage_percent'],
|
"usage_percent": context_status['usage_percent'],
|
||||||
"total_size": context_status['sizes']['total'],
|
"total_size": context_status['sizes']['total'],
|
||||||
|
|||||||
@ -14,6 +14,8 @@ except ImportError:
|
|||||||
|
|
||||||
from core.tool_config import TOOL_CATEGORIES
|
from core.tool_config import TOOL_CATEGORIES
|
||||||
|
|
||||||
|
ALLOWED_RUN_MODES = {"fast", "thinking", "deep"}
|
||||||
|
|
||||||
PERSONALIZATION_FILENAME = "personalization.json"
|
PERSONALIZATION_FILENAME = "personalization.json"
|
||||||
MAX_SHORT_FIELD_LENGTH = 20
|
MAX_SHORT_FIELD_LENGTH = 20
|
||||||
MAX_CONSIDERATION_LENGTH = 50
|
MAX_CONSIDERATION_LENGTH = 50
|
||||||
@ -31,6 +33,7 @@ DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
|
|||||||
"considerations": [],
|
"considerations": [],
|
||||||
"thinking_interval": None,
|
"thinking_interval": None,
|
||||||
"disabled_tool_categories": [],
|
"disabled_tool_categories": [],
|
||||||
|
"default_run_mode": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -123,6 +126,11 @@ def sanitize_personalization_payload(
|
|||||||
base["disabled_tool_categories"] = _sanitize_tool_categories(data.get("disabled_tool_categories"), allowed_tool_categories)
|
base["disabled_tool_categories"] = _sanitize_tool_categories(data.get("disabled_tool_categories"), allowed_tool_categories)
|
||||||
else:
|
else:
|
||||||
base["disabled_tool_categories"] = _sanitize_tool_categories(base.get("disabled_tool_categories"), allowed_tool_categories)
|
base["disabled_tool_categories"] = _sanitize_tool_categories(base.get("disabled_tool_categories"), allowed_tool_categories)
|
||||||
|
|
||||||
|
if "default_run_mode" in data:
|
||||||
|
base["default_run_mode"] = _sanitize_run_mode(data.get("default_run_mode"))
|
||||||
|
else:
|
||||||
|
base["default_run_mode"] = _sanitize_run_mode(base.get("default_run_mode"))
|
||||||
return base
|
return base
|
||||||
|
|
||||||
|
|
||||||
@ -222,3 +230,13 @@ def _sanitize_tool_categories(value: Any, allowed: set) -> list:
|
|||||||
if candidate not in result:
|
if candidate not in result:
|
||||||
result.append(candidate)
|
result.append(candidate)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_run_mode(value: Any) -> Optional[str]:
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if isinstance(value, str):
|
||||||
|
candidate = value.strip().lower()
|
||||||
|
if candidate in ALLOWED_RUN_MODES:
|
||||||
|
return candidate
|
||||||
|
return None
|
||||||
|
|||||||
1
static/icons/brain-cog.svg
Normal file
1
static/icons/brain-cog.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="m10.852 14.772l-.383.923m.383-6.467l-.383-.923m2.679 6.467l.382.924m.001-7.391l-.383.923m1.624 1.624l.923-.383m-.923 2.679l.923.383M17.598 6.5A3 3 0 1 0 12 5a3 3 0 0 0-5.63-1.446a3 3 0 0 0-.368 1.571a4 4 0 0 0-2.525 5.771"/><path d="M17.998 5.125a4 4 0 0 1 2.525 5.771"/><path d="M19.505 10.294a4 4 0 0 1-1.5 7.706"/><path d="M4.032 17.483A4 4 0 0 0 11.464 20c.18-.311.892-.311 1.072 0a4 4 0 0 0 7.432-2.516"/><path d="M4.5 10.291A4 4 0 0 0 6 18m.002-12.875a3 3 0 0 0 .4 1.375m2.826 4.352l-.923-.383m.923 2.679l-.923.383"/><circle cx="12" cy="12" r="3"/></svg>
|
||||||
|
After Width: | Height: | Size: 764 B |
@ -63,6 +63,7 @@
|
|||||||
:icon-style="iconStyle"
|
:icon-style="iconStyle"
|
||||||
:agent-version="agentVersion"
|
:agent-version="agentVersion"
|
||||||
:thinking-mode="thinkingMode"
|
:thinking-mode="thinkingMode"
|
||||||
|
:run-mode="resolvedRunMode"
|
||||||
:is-connected="isConnected"
|
:is-connected="isConnected"
|
||||||
:panel-menu-open="panelMenuOpen"
|
:panel-menu-open="panelMenuOpen"
|
||||||
:panel-mode="panelMode"
|
:panel-mode="panelMode"
|
||||||
@ -148,8 +149,10 @@
|
|||||||
:streaming-message="streamingMessage"
|
:streaming-message="streamingMessage"
|
||||||
:uploading="uploading"
|
:uploading="uploading"
|
||||||
:thinking-mode="thinkingMode"
|
:thinking-mode="thinkingMode"
|
||||||
|
:run-mode="resolvedRunMode"
|
||||||
:quick-menu-open="quickMenuOpen"
|
:quick-menu-open="quickMenuOpen"
|
||||||
:tool-menu-open="toolMenuOpen"
|
:tool-menu-open="toolMenuOpen"
|
||||||
|
:mode-menu-open="modeMenuOpen"
|
||||||
:tool-settings="toolSettings"
|
:tool-settings="toolSettings"
|
||||||
:tool-settings-loading="toolSettingsLoading"
|
:tool-settings-loading="toolSettingsLoading"
|
||||||
:settings-open="settingsOpen"
|
:settings-open="settingsOpen"
|
||||||
@ -166,7 +169,8 @@
|
|||||||
@send-or-stop="handleSendOrStop"
|
@send-or-stop="handleSendOrStop"
|
||||||
@quick-upload="handleQuickUpload"
|
@quick-upload="handleQuickUpload"
|
||||||
@toggle-tool-menu="toggleToolMenu"
|
@toggle-tool-menu="toggleToolMenu"
|
||||||
@quick-mode-toggle="handleQuickModeToggle"
|
@toggle-mode-menu="toggleModeMenu"
|
||||||
|
@select-run-mode="handleModeSelect"
|
||||||
@toggle-settings="toggleSettings"
|
@toggle-settings="toggleSettings"
|
||||||
@update-tool-category="updateToolCategory"
|
@update-tool-category="updateToolCategory"
|
||||||
@realtime-terminal="handleRealtimeTerminalClick"
|
@realtime-terminal="handleRealtimeTerminalClick"
|
||||||
|
|||||||
@ -256,6 +256,13 @@ const appOptions = {
|
|||||||
'toolMenuOpen',
|
'toolMenuOpen',
|
||||||
'settingsOpen'
|
'settingsOpen'
|
||||||
]),
|
]),
|
||||||
|
resolvedRunMode() {
|
||||||
|
const allowed = ['fast', 'thinking', 'deep'];
|
||||||
|
if (allowed.includes(this.runMode)) {
|
||||||
|
return this.runMode;
|
||||||
|
}
|
||||||
|
return this.thinkingMode ? 'thinking' : 'fast';
|
||||||
|
},
|
||||||
...mapWritableState(useToolStore, [
|
...mapWritableState(useToolStore, [
|
||||||
'preparingTools',
|
'preparingTools',
|
||||||
'activeTools',
|
'activeTools',
|
||||||
@ -1824,8 +1831,14 @@ const appOptions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async toggleThinkingMode() {
|
async toggleThinkingMode() {
|
||||||
const target = this.thinkingMode ? 'fast' : 'thinking';
|
await this.handleCycleRunMode();
|
||||||
await this.setRunMode(target);
|
},
|
||||||
|
|
||||||
|
handleQuickModeToggle() {
|
||||||
|
if (!this.isConnected || this.streamingMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.handleCycleRunMode();
|
||||||
},
|
},
|
||||||
|
|
||||||
triggerFileUpload() {
|
triggerFileUpload() {
|
||||||
@ -2034,7 +2047,8 @@ const appOptions = {
|
|||||||
|
|
||||||
async handleCycleRunMode() {
|
async handleCycleRunMode() {
|
||||||
const modes: Array<'fast' | 'thinking' | 'deep'> = ['fast', 'thinking', 'deep'];
|
const modes: Array<'fast' | 'thinking' | 'deep'> = ['fast', 'thinking', 'deep'];
|
||||||
const currentIndex = modes.indexOf(this.runMode);
|
const currentMode = this.resolvedRunMode;
|
||||||
|
const currentIndex = modes.indexOf(currentMode);
|
||||||
const nextMode = modes[(currentIndex + 1) % modes.length];
|
const nextMode = modes[(currentIndex + 1) % modes.length];
|
||||||
await this.setRunMode(nextMode);
|
await this.setRunMode(nextMode);
|
||||||
},
|
},
|
||||||
@ -2044,7 +2058,7 @@ const appOptions = {
|
|||||||
this.modeMenuOpen = false;
|
this.modeMenuOpen = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mode === this.runMode) {
|
if (mode === this.resolvedRunMode) {
|
||||||
this.modeMenuOpen = false;
|
this.modeMenuOpen = false;
|
||||||
this.closeQuickMenu();
|
this.closeQuickMenu();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -42,18 +42,21 @@
|
|||||||
:uploading="uploading"
|
:uploading="uploading"
|
||||||
:streaming-message="streamingMessage"
|
:streaming-message="streamingMessage"
|
||||||
:thinking-mode="thinkingMode"
|
:thinking-mode="thinkingMode"
|
||||||
|
:run-mode="runMode"
|
||||||
:tool-menu-open="toolMenuOpen"
|
:tool-menu-open="toolMenuOpen"
|
||||||
:tool-settings="toolSettings"
|
:tool-settings="toolSettings"
|
||||||
:tool-settings-loading="toolSettingsLoading"
|
:tool-settings-loading="toolSettingsLoading"
|
||||||
:settings-open="settingsOpen"
|
:settings-open="settingsOpen"
|
||||||
|
:mode-menu-open="modeMenuOpen"
|
||||||
:compressing="compressing"
|
:compressing="compressing"
|
||||||
:current-conversation-id="currentConversationId"
|
:current-conversation-id="currentConversationId"
|
||||||
:icon-style="iconStyle"
|
:icon-style="iconStyle"
|
||||||
:tool-category-icon="toolCategoryIcon"
|
:tool-category-icon="toolCategoryIcon"
|
||||||
@quick-upload="triggerQuickUpload"
|
@quick-upload="triggerQuickUpload"
|
||||||
@toggle-tool-menu="$emit('toggle-tool-menu')"
|
@toggle-tool-menu="$emit('toggle-tool-menu')"
|
||||||
@quick-mode-toggle="$emit('quick-mode-toggle')"
|
|
||||||
@toggle-settings="$emit('toggle-settings')"
|
@toggle-settings="$emit('toggle-settings')"
|
||||||
|
@toggle-mode-menu="$emit('toggle-mode-menu')"
|
||||||
|
@select-run-mode="(mode) => $emit('select-run-mode', mode)"
|
||||||
@update-tool-category="(id, enabled) => $emit('update-tool-category', id, enabled)"
|
@update-tool-category="(id, enabled) => $emit('update-tool-category', id, enabled)"
|
||||||
@realtime-terminal="$emit('realtime-terminal')"
|
@realtime-terminal="$emit('realtime-terminal')"
|
||||||
@toggle-focus-panel="$emit('toggle-focus-panel')"
|
@toggle-focus-panel="$emit('toggle-focus-panel')"
|
||||||
@ -81,7 +84,8 @@ const emit = defineEmits([
|
|||||||
'send-or-stop',
|
'send-or-stop',
|
||||||
'quick-upload',
|
'quick-upload',
|
||||||
'toggle-tool-menu',
|
'toggle-tool-menu',
|
||||||
'quick-mode-toggle',
|
'toggle-mode-menu',
|
||||||
|
'select-run-mode',
|
||||||
'toggle-settings',
|
'toggle-settings',
|
||||||
'update-tool-category',
|
'update-tool-category',
|
||||||
'realtime-terminal',
|
'realtime-terminal',
|
||||||
@ -99,8 +103,10 @@ const props = defineProps<{
|
|||||||
streamingMessage: boolean;
|
streamingMessage: boolean;
|
||||||
uploading: boolean;
|
uploading: boolean;
|
||||||
thinkingMode: boolean;
|
thinkingMode: boolean;
|
||||||
|
runMode: 'fast' | 'thinking' | 'deep';
|
||||||
quickMenuOpen: boolean;
|
quickMenuOpen: boolean;
|
||||||
toolMenuOpen: boolean;
|
toolMenuOpen: boolean;
|
||||||
|
modeMenuOpen: boolean;
|
||||||
toolSettings: Array<{ id: string; label: string; enabled: boolean }>;
|
toolSettings: Array<{ id: string; label: string; enabled: boolean }>;
|
||||||
toolSettingsLoading: boolean;
|
toolSettingsLoading: boolean;
|
||||||
settingsOpen: boolean;
|
settingsOpen: boolean;
|
||||||
|
|||||||
@ -4,6 +4,15 @@
|
|||||||
<button type="button" class="menu-entry" @click="$emit('quick-upload')" :disabled="!isConnected || uploading">
|
<button type="button" class="menu-entry" @click="$emit('quick-upload')" :disabled="!isConnected || uploading">
|
||||||
{{ uploading ? '上传中...' : '上传文件' }}
|
{{ uploading ? '上传中...' : '上传文件' }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="menu-entry has-submenu"
|
||||||
|
@click.stop="$emit('toggle-mode-menu')"
|
||||||
|
:disabled="!isConnected || streamingMessage"
|
||||||
|
>
|
||||||
|
<span>运行模式</span>
|
||||||
|
<span class="entry-arrow">{{ runModeLabel }}</span>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="menu-entry has-submenu"
|
class="menu-entry has-submenu"
|
||||||
@ -13,14 +22,6 @@
|
|||||||
工具禁用
|
工具禁用
|
||||||
<span class="entry-arrow">›</span>
|
<span class="entry-arrow">›</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="menu-entry"
|
|
||||||
@click="$emit('quick-mode-toggle')"
|
|
||||||
:disabled="streamingMessage || !isConnected"
|
|
||||||
>
|
|
||||||
{{ thinkingMode ? '快速模式' : '思考模式' }}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="menu-entry has-submenu"
|
class="menu-entry has-submenu"
|
||||||
@ -31,6 +32,24 @@
|
|||||||
<span class="entry-arrow">›</span>
|
<span class="entry-arrow">›</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<transition name="submenu-slide">
|
||||||
|
<div class="quick-submenu mode-submenu" v-if="modeMenuOpen">
|
||||||
|
<div class="submenu-list">
|
||||||
|
<button
|
||||||
|
v-for="option in runModeOptions"
|
||||||
|
:key="option.value"
|
||||||
|
type="button"
|
||||||
|
class="menu-entry submenu-entry"
|
||||||
|
:class="{ active: option.value === resolvedRunMode }"
|
||||||
|
@click.stop="$emit('select-run-mode', option.value)"
|
||||||
|
:disabled="streamingMessage || !isConnected"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
<transition name="submenu-slide">
|
<transition name="submenu-slide">
|
||||||
<div class="quick-submenu tool-submenu" v-if="toolMenuOpen">
|
<div class="quick-submenu tool-submenu" v-if="toolMenuOpen">
|
||||||
<div class="submenu-status" v-if="toolSettingsLoading">正在同步工具状态...</div>
|
<div class="submenu-status" v-if="toolSettingsLoading">正在同步工具状态...</div>
|
||||||
@ -98,6 +117,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
defineOptions({ name: 'QuickMenu' });
|
defineOptions({ name: 'QuickMenu' });
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -114,19 +135,43 @@ const props = defineProps<{
|
|||||||
currentConversationId: string | null;
|
currentConversationId: string | null;
|
||||||
iconStyle?: (key: string) => Record<string, string>;
|
iconStyle?: (key: string) => Record<string, string>;
|
||||||
toolCategoryIcon: (categoryId: string) => string;
|
toolCategoryIcon: (categoryId: string) => string;
|
||||||
|
modeMenuOpen: boolean;
|
||||||
|
runMode?: 'fast' | 'thinking' | 'deep';
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
(event: 'quick-upload'): void;
|
(event: 'quick-upload'): void;
|
||||||
(event: 'toggle-tool-menu'): void;
|
(event: 'toggle-tool-menu'): void;
|
||||||
(event: 'quick-mode-toggle'): void;
|
|
||||||
(event: 'toggle-settings'): void;
|
(event: 'toggle-settings'): void;
|
||||||
(event: 'update-tool-category', id: string, enabled: boolean): void;
|
(event: 'update-tool-category', id: string, enabled: boolean): void;
|
||||||
(event: 'realtime-terminal'): void;
|
(event: 'realtime-terminal'): void;
|
||||||
(event: 'toggle-focus-panel'): void;
|
(event: 'toggle-focus-panel'): void;
|
||||||
(event: 'toggle-token-panel'): void;
|
(event: 'toggle-token-panel'): void;
|
||||||
(event: 'compress-conversation'): void;
|
(event: 'compress-conversation'): void;
|
||||||
|
(event: 'toggle-mode-menu'): void;
|
||||||
|
(event: 'select-run-mode', mode: 'fast' | 'thinking' | 'deep'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const runModeOptions = [
|
||||||
|
{ value: 'fast', label: '快速模式' },
|
||||||
|
{ value: 'thinking', label: '思考模式' },
|
||||||
|
{ value: 'deep', label: '深度思考模式' }
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const runModeLabelMap: Record<'fast' | 'thinking' | 'deep', string> = {
|
||||||
|
fast: '快速模式',
|
||||||
|
thinking: '思考模式',
|
||||||
|
deep: '深度思考'
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolvedRunMode = computed<'fast' | 'thinking' | 'deep'>(() => {
|
||||||
|
if (props.runMode === 'deep' || props.runMode === 'thinking' || props.runMode === 'fast') {
|
||||||
|
return props.runMode;
|
||||||
|
}
|
||||||
|
return props.thinkingMode ? 'thinking' : 'fast';
|
||||||
|
});
|
||||||
|
|
||||||
|
const runModeLabel = computed(() => runModeLabelMap[resolvedRunMode.value]);
|
||||||
|
|
||||||
const getIconStyle = (key: string) => (props.iconStyle ? props.iconStyle(key) : {});
|
const getIconStyle = (key: string) => (props.iconStyle ? props.iconStyle(key) : {});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -14,15 +14,15 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mode-indicator"
|
class="mode-indicator"
|
||||||
:class="{ thinking: thinkingMode, fast: !thinkingMode }"
|
:class="modeIndicatorClass"
|
||||||
:title="thinkingMode ? '思考模式(点击切换)' : '快速模式(点击切换)'"
|
:title="modeIndicatorTitle"
|
||||||
@click="$emit('toggle-thinking-mode')"
|
@click="$emit('toggle-thinking-mode')"
|
||||||
>
|
>
|
||||||
<transition name="mode-icon" mode="out-in">
|
<transition name="mode-icon" mode="out-in">
|
||||||
<span
|
<span
|
||||||
class="icon icon-sm"
|
class="icon icon-sm"
|
||||||
:style="iconStyle(thinkingMode ? 'brain' : 'zap')"
|
:style="iconStyle(modeIndicatorIcon)"
|
||||||
:key="thinkingMode ? 'brain' : 'zap'"
|
:key="modeIndicatorIcon"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></span>
|
></span>
|
||||||
</transition>
|
</transition>
|
||||||
@ -145,6 +145,7 @@ const props = defineProps<{
|
|||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
panelMenuOpen: boolean;
|
panelMenuOpen: boolean;
|
||||||
panelMode: 'files' | 'todo' | 'subAgents';
|
panelMode: 'files' | 'todo' | 'subAgents';
|
||||||
|
runMode: 'fast' | 'thinking' | 'deep';
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
@ -168,6 +169,46 @@ const panelStyle = computed(() => {
|
|||||||
minWidth: px
|
minWidth: px
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resolveRunMode = () => {
|
||||||
|
if (props.runMode === 'deep' || props.runMode === 'thinking' || props.runMode === 'fast') {
|
||||||
|
return props.runMode;
|
||||||
|
}
|
||||||
|
return props.thinkingMode ? 'thinking' : 'fast';
|
||||||
|
};
|
||||||
|
|
||||||
|
const modeIndicatorClass = computed(() => {
|
||||||
|
const mode = resolveRunMode();
|
||||||
|
if (mode === 'deep') {
|
||||||
|
return 'deep';
|
||||||
|
}
|
||||||
|
if (mode === 'thinking') {
|
||||||
|
return 'thinking';
|
||||||
|
}
|
||||||
|
return 'fast';
|
||||||
|
});
|
||||||
|
|
||||||
|
const modeIndicatorIcon = computed(() => {
|
||||||
|
const mode = resolveRunMode();
|
||||||
|
if (mode === 'deep') {
|
||||||
|
return 'brainCog';
|
||||||
|
}
|
||||||
|
if (mode === 'thinking') {
|
||||||
|
return 'brain';
|
||||||
|
}
|
||||||
|
return 'zap';
|
||||||
|
});
|
||||||
|
|
||||||
|
const modeIndicatorTitle = computed(() => {
|
||||||
|
const mode = resolveRunMode();
|
||||||
|
if (mode === 'deep') {
|
||||||
|
return '深度思考模式(点击切换)';
|
||||||
|
}
|
||||||
|
if (mode === 'thinking') {
|
||||||
|
return '思考模式(点击切换)';
|
||||||
|
}
|
||||||
|
return '快速模式(点击切换)';
|
||||||
|
});
|
||||||
const fileStore = useFileStore();
|
const fileStore = useFileStore();
|
||||||
const subAgentStore = useSubAgentStore();
|
const subAgentStore = useSubAgentStore();
|
||||||
const { fileTree, expandedFolders, todoList } = storeToRefs(fileStore);
|
const { fileTree, expandedFolders, todoList } = storeToRefs(fileStore);
|
||||||
|
|||||||
@ -190,6 +190,29 @@
|
|||||||
</section>
|
</section>
|
||||||
<section v-else key="behavior" class="personal-page behavior-page">
|
<section v-else key="behavior" class="personal-page behavior-page">
|
||||||
<div class="behavior-section">
|
<div class="behavior-section">
|
||||||
|
<div class="behavior-field">
|
||||||
|
<div class="behavior-field-header">
|
||||||
|
<span class="field-title">默认思考模型</span>
|
||||||
|
<p class="field-desc">设定登录或新建任务时的初始运行模式,仍可通过主界面随时切换。</p>
|
||||||
|
</div>
|
||||||
|
<div class="run-mode-options">
|
||||||
|
<button
|
||||||
|
v-for="option in runModeOptions"
|
||||||
|
:key="option.id"
|
||||||
|
type="button"
|
||||||
|
class="run-mode-card"
|
||||||
|
:class="{ active: isRunModeActive(option.value) }"
|
||||||
|
:aria-pressed="isRunModeActive(option.value)"
|
||||||
|
@click.prevent="setDefaultRunMode(option.value)"
|
||||||
|
>
|
||||||
|
<div class="run-mode-card-header">
|
||||||
|
<span class="run-mode-title">{{ option.label }}</span>
|
||||||
|
<span v-if="option.badge" class="run-mode-badge">{{ option.badge }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="run-mode-desc">{{ option.desc }}</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="behavior-field">
|
<div class="behavior-field">
|
||||||
<div class="behavior-field-header">
|
<div class="behavior-field-header">
|
||||||
<span class="field-title">思考频率</span>
|
<span class="field-title">思考频率</span>
|
||||||
@ -303,6 +326,15 @@ const {
|
|||||||
const activeTab = ref<'preferences' | 'behavior'>('preferences');
|
const activeTab = ref<'preferences' | 'behavior'>('preferences');
|
||||||
const swipeState = ref<{ startY: number; active: boolean }>({ startY: 0, active: false });
|
const swipeState = ref<{ startY: number; active: boolean }>({ startY: 0, active: false });
|
||||||
|
|
||||||
|
type RunModeValue = 'fast' | 'thinking' | 'deep' | null;
|
||||||
|
|
||||||
|
const runModeOptions: Array<{ id: string; label: string; desc: string; value: RunModeValue; badge?: string }> = [
|
||||||
|
{ id: 'auto', label: '跟随系统', desc: '沿用工作区默认设置', value: null },
|
||||||
|
{ id: 'fast', label: '快速模式', desc: '追求响应速度,跳过思考模型', value: 'fast' },
|
||||||
|
{ id: 'thinking', label: '思考模式', desc: '首轮回复会先输出思考过程', value: 'thinking', badge: '推荐' },
|
||||||
|
{ id: 'deep', label: '深度思考', desc: '整轮对话都使用思考模型', value: 'deep' }
|
||||||
|
];
|
||||||
|
|
||||||
const thinkingPresets = [
|
const thinkingPresets = [
|
||||||
{ id: 'low', label: '低', value: 10 },
|
{ id: 'low', label: '低', value: 10 },
|
||||||
{ id: 'medium', label: '中', value: 5 },
|
{ id: 'medium', label: '中', value: 5 },
|
||||||
@ -340,6 +372,17 @@ const applyThinkingPreset = (value: number) => {
|
|||||||
personalization.setThinkingInterval(value);
|
personalization.setThinkingInterval(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isRunModeActive = (value: RunModeValue) => {
|
||||||
|
if (value === null) {
|
||||||
|
return !form.value.default_run_mode;
|
||||||
|
}
|
||||||
|
return form.value.default_run_mode === value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setDefaultRunMode = (value: RunModeValue) => {
|
||||||
|
personalization.setDefaultRunMode(value);
|
||||||
|
};
|
||||||
|
|
||||||
const handleThinkingInput = (event: Event) => {
|
const handleThinkingInput = (event: Event) => {
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
if (!target.value) {
|
if (!target.value) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ interface ConnectionState {
|
|||||||
projectPath: string;
|
projectPath: string;
|
||||||
agentVersion: string;
|
agentVersion: string;
|
||||||
thinkingMode: boolean;
|
thinkingMode: boolean;
|
||||||
|
runMode: 'fast' | 'thinking' | 'deep';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useConnectionStore = defineStore('connection', {
|
export const useConnectionStore = defineStore('connection', {
|
||||||
@ -17,7 +18,8 @@ export const useConnectionStore = defineStore('connection', {
|
|||||||
stopRequested: false,
|
stopRequested: false,
|
||||||
projectPath: '',
|
projectPath: '',
|
||||||
agentVersion: '',
|
agentVersion: '',
|
||||||
thinkingMode: true
|
thinkingMode: true,
|
||||||
|
runMode: 'thinking'
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
setSocket(socket: Socket | null) {
|
setSocket(socket: Socket | null) {
|
||||||
@ -46,6 +48,10 @@ export const useConnectionStore = defineStore('connection', {
|
|||||||
},
|
},
|
||||||
toggleThinkingMode() {
|
toggleThinkingMode() {
|
||||||
this.thinkingMode = !this.thinkingMode;
|
this.thinkingMode = !this.thinkingMode;
|
||||||
|
},
|
||||||
|
setRunMode(mode: 'fast' | 'thinking' | 'deep') {
|
||||||
|
this.runMode = mode;
|
||||||
|
this.thinkingMode = mode !== 'fast';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
type RunMode = 'fast' | 'thinking' | 'deep';
|
||||||
|
|
||||||
interface PersonalForm {
|
interface PersonalForm {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
self_identify: string;
|
self_identify: string;
|
||||||
@ -9,6 +11,7 @@ interface PersonalForm {
|
|||||||
considerations: string[];
|
considerations: string[];
|
||||||
thinking_interval: number | null;
|
thinking_interval: number | null;
|
||||||
disabled_tool_categories: string[];
|
disabled_tool_categories: string[];
|
||||||
|
default_run_mode: RunMode | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PersonalizationState {
|
interface PersonalizationState {
|
||||||
@ -32,6 +35,7 @@ interface PersonalizationState {
|
|||||||
|
|
||||||
const DEFAULT_INTERVAL = 10;
|
const DEFAULT_INTERVAL = 10;
|
||||||
const DEFAULT_INTERVAL_RANGE = { min: 1, max: 50 };
|
const DEFAULT_INTERVAL_RANGE = { min: 1, max: 50 };
|
||||||
|
const RUN_MODE_OPTIONS: RunMode[] = ['fast', 'thinking', 'deep'];
|
||||||
|
|
||||||
const defaultForm = (): PersonalForm => ({
|
const defaultForm = (): PersonalForm => ({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -41,7 +45,8 @@ const defaultForm = (): PersonalForm => ({
|
|||||||
tone: '',
|
tone: '',
|
||||||
considerations: [],
|
considerations: [],
|
||||||
thinking_interval: null,
|
thinking_interval: null,
|
||||||
disabled_tool_categories: []
|
disabled_tool_categories: [],
|
||||||
|
default_run_mode: null
|
||||||
});
|
});
|
||||||
|
|
||||||
export const usePersonalizationStore = defineStore('personalization', {
|
export const usePersonalizationStore = defineStore('personalization', {
|
||||||
@ -121,7 +126,11 @@ export const usePersonalizationStore = defineStore('personalization', {
|
|||||||
tone: data.tone || '',
|
tone: data.tone || '',
|
||||||
considerations: Array.isArray(data.considerations) ? [...data.considerations] : [],
|
considerations: Array.isArray(data.considerations) ? [...data.considerations] : [],
|
||||||
thinking_interval: typeof data.thinking_interval === 'number' ? data.thinking_interval : null,
|
thinking_interval: typeof data.thinking_interval === 'number' ? data.thinking_interval : null,
|
||||||
disabled_tool_categories: Array.isArray(data.disabled_tool_categories) ? data.disabled_tool_categories.filter((item: unknown) => typeof item === 'string') : []
|
disabled_tool_categories: Array.isArray(data.disabled_tool_categories) ? data.disabled_tool_categories.filter((item: unknown) => typeof item === 'string') : [],
|
||||||
|
default_run_mode:
|
||||||
|
typeof data.default_run_mode === 'string' && RUN_MODE_OPTIONS.includes(data.default_run_mode as RunMode)
|
||||||
|
? data.default_run_mode as RunMode
|
||||||
|
: null
|
||||||
};
|
};
|
||||||
this.clearFeedback();
|
this.clearFeedback();
|
||||||
},
|
},
|
||||||
@ -271,6 +280,17 @@ export const usePersonalizationStore = defineStore('personalization', {
|
|||||||
};
|
};
|
||||||
this.clearFeedback();
|
this.clearFeedback();
|
||||||
},
|
},
|
||||||
|
setDefaultRunMode(mode: RunMode | null) {
|
||||||
|
let target: RunMode | null = null;
|
||||||
|
if (typeof mode === 'string' && RUN_MODE_OPTIONS.includes(mode as RunMode)) {
|
||||||
|
target = mode as RunMode;
|
||||||
|
}
|
||||||
|
this.form = {
|
||||||
|
...this.form,
|
||||||
|
default_run_mode: target
|
||||||
|
};
|
||||||
|
this.clearFeedback();
|
||||||
|
},
|
||||||
applyTonePreset(preset: string) {
|
applyTonePreset(preset: string) {
|
||||||
if (!preset) {
|
if (!preset) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -218,6 +218,12 @@
|
|||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-entry.active {
|
||||||
|
background: rgba(118, 103, 84, 0.12);
|
||||||
|
color: var(--claude-text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-entry:disabled {
|
.menu-entry:disabled {
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
@ -272,11 +278,7 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submenu-label {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-menu-enter-active,
|
.quick-menu-enter-active,
|
||||||
.quick-menu-leave-active {
|
.quick-menu-leave-active {
|
||||||
|
|||||||
@ -295,6 +295,66 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.run-mode-options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-card {
|
||||||
|
border: 1px solid rgba(118, 103, 84, 0.2);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
padding: 16px;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-card:hover {
|
||||||
|
border-color: rgba(118, 103, 84, 0.4);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-card.active {
|
||||||
|
border-color: var(--claude-accent);
|
||||||
|
background: rgba(118, 103, 84, 0.08);
|
||||||
|
box-shadow: 0 8px 20px rgba(118, 103, 84, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-badge {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--claude-accent);
|
||||||
|
font-weight: 600;
|
||||||
|
background: rgba(118, 103, 84, 0.12);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-mode-desc {
|
||||||
|
color: var(--claude-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
.thinking-presets {
|
.thinking-presets {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ export const ICONS = Object.freeze({
|
|||||||
bot: '/static/icons/bot.svg',
|
bot: '/static/icons/bot.svg',
|
||||||
book: '/static/icons/book.svg',
|
book: '/static/icons/book.svg',
|
||||||
brain: '/static/icons/brain.svg',
|
brain: '/static/icons/brain.svg',
|
||||||
|
brainCog: '/static/icons/brain-cog.svg',
|
||||||
camera: '/static/icons/camera.svg',
|
camera: '/static/icons/camera.svg',
|
||||||
check: '/static/icons/check.svg',
|
check: '/static/icons/check.svg',
|
||||||
checkbox: '/static/icons/checkbox.svg',
|
checkbox: '/static/icons/checkbox.svg',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user