From eb7ccf1dd2e1bb34e82fee20a9537ddf87813ea9 Mon Sep 17 00:00:00 2001
From: JOJO <1498581755@qq.com>
Date: Tue, 2 Dec 2025 19:03:33 +0800
Subject: [PATCH] feat: restore run mode personalization
---
core/main_terminal.py | 9 +++
core/web_terminal.py | 34 +++++++---
modules/personalization_manager.py | 18 ++++++
static/icons/brain-cog.svg | 1 +
static/src/App.vue | 6 +-
static/src/app.ts | 22 +++++--
static/src/components/input/InputComposer.vue | 10 ++-
static/src/components/input/QuickMenu.vue | 63 ++++++++++++++++---
static/src/components/panels/LeftPanel.vue | 49 +++++++++++++--
.../personalization/PersonalizationDrawer.vue | 43 +++++++++++++
static/src/stores/connection.ts | 8 ++-
static/src/stores/personalization.ts | 24 ++++++-
.../styles/components/input/_composer.scss | 12 ++--
.../styles/components/overlays/_overlays.scss | 60 ++++++++++++++++++
static/src/utils/icons.ts | 1 +
15 files changed, 324 insertions(+), 36 deletions(-)
create mode 100644 static/icons/brain-cog.svg
diff --git a/core/main_terminal.py b/core/main_terminal.py
index a3c6de0..3033ff1 100644
--- a/core/main_terminal.py
+++ b/core/main_terminal.py
@@ -357,6 +357,15 @@ class MainTerminal:
self.tool_category_states[key] = False if key in disabled_categories else category.default_enabled
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:
"""集中处理 read_file 工具的三种模式。"""
diff --git a/core/web_terminal.py b/core/web_terminal.py
index ffbfa6e..b9e89fd 100644
--- a/core/web_terminal.py
+++ b/core/web_terminal.py
@@ -48,13 +48,21 @@ class WebTerminal(MainTerminal):
self,
project_path: str,
thinking_mode: bool = False,
+ run_mode: Optional[str] = None,
message_callback: Optional[Callable] = None,
data_dir: Optional[str] = None,
container_session: Optional["ContainerHandle"] = 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特有属性
self.message_callback = message_callback
@@ -74,7 +82,7 @@ class WebTerminal(MainTerminal):
)
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
- print(f"[WebTerminal] 思考模式: {'开启' if thinking_mode else '关闭'}")
+ print(f"[WebTerminal] 初始模式: {self.run_mode}")
print(f"[WebTerminal] 对话管理已就绪")
# 设置token更新回调
@@ -88,25 +96,34 @@ class WebTerminal(MainTerminal):
# 新增:对话管理相关方法(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版本)
-
+
Args:
thinking_mode: 思考模式,None则使用当前设置
-
+ run_mode: 显式的运行模式(fast/thinking/deep)
+
Returns:
Dict: 包含新对话信息
"""
if thinking_mode is None:
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:
conversation_id = self.context_manager.start_new_conversation(
project_path=self.project_path,
- thinking_mode=thinking_mode
+ thinking_mode=thinking_mode,
+ run_mode=self.run_mode
)
-
+
# 重置相关状态
if self.thinking_mode:
self.api_client.start_new_task()
@@ -268,6 +285,7 @@ class WebTerminal(MainTerminal):
"project_path": self.project_path,
"thinking_mode": self.thinking_mode,
"thinking_status": self.get_thinking_mode_status(),
+ "run_mode": self.run_mode,
"context": {
"usage_percent": context_status['usage_percent'],
"total_size": context_status['sizes']['total'],
diff --git a/modules/personalization_manager.py b/modules/personalization_manager.py
index 3555e3a..4bb3cad 100644
--- a/modules/personalization_manager.py
+++ b/modules/personalization_manager.py
@@ -14,6 +14,8 @@ except ImportError:
from core.tool_config import TOOL_CATEGORIES
+ALLOWED_RUN_MODES = {"fast", "thinking", "deep"}
+
PERSONALIZATION_FILENAME = "personalization.json"
MAX_SHORT_FIELD_LENGTH = 20
MAX_CONSIDERATION_LENGTH = 50
@@ -31,6 +33,7 @@ DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
"considerations": [],
"thinking_interval": None,
"disabled_tool_categories": [],
+ "default_run_mode": None,
}
__all__ = [
@@ -123,6 +126,11 @@ def sanitize_personalization_payload(
base["disabled_tool_categories"] = _sanitize_tool_categories(data.get("disabled_tool_categories"), allowed_tool_categories)
else:
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
@@ -222,3 +230,13 @@ def _sanitize_tool_categories(value: Any, allowed: set) -> list:
if candidate not in result:
result.append(candidate)
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
diff --git a/static/icons/brain-cog.svg b/static/icons/brain-cog.svg
new file mode 100644
index 0000000..777a2a7
--- /dev/null
+++ b/static/icons/brain-cog.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/src/App.vue b/static/src/App.vue
index c3e2901..d4226ef 100644
--- a/static/src/App.vue
+++ b/static/src/App.vue
@@ -63,6 +63,7 @@
:icon-style="iconStyle"
:agent-version="agentVersion"
:thinking-mode="thinkingMode"
+ :run-mode="resolvedRunMode"
:is-connected="isConnected"
:panel-menu-open="panelMenuOpen"
:panel-mode="panelMode"
@@ -148,8 +149,10 @@
:streaming-message="streamingMessage"
:uploading="uploading"
:thinking-mode="thinkingMode"
+ :run-mode="resolvedRunMode"
:quick-menu-open="quickMenuOpen"
:tool-menu-open="toolMenuOpen"
+ :mode-menu-open="modeMenuOpen"
:tool-settings="toolSettings"
:tool-settings-loading="toolSettingsLoading"
:settings-open="settingsOpen"
@@ -166,7 +169,8 @@
@send-or-stop="handleSendOrStop"
@quick-upload="handleQuickUpload"
@toggle-tool-menu="toggleToolMenu"
- @quick-mode-toggle="handleQuickModeToggle"
+ @toggle-mode-menu="toggleModeMenu"
+ @select-run-mode="handleModeSelect"
@toggle-settings="toggleSettings"
@update-tool-category="updateToolCategory"
@realtime-terminal="handleRealtimeTerminalClick"
diff --git a/static/src/app.ts b/static/src/app.ts
index 5f94b21..e74f997 100644
--- a/static/src/app.ts
+++ b/static/src/app.ts
@@ -256,6 +256,13 @@ const appOptions = {
'toolMenuOpen',
'settingsOpen'
]),
+ resolvedRunMode() {
+ const allowed = ['fast', 'thinking', 'deep'];
+ if (allowed.includes(this.runMode)) {
+ return this.runMode;
+ }
+ return this.thinkingMode ? 'thinking' : 'fast';
+ },
...mapWritableState(useToolStore, [
'preparingTools',
'activeTools',
@@ -1824,8 +1831,14 @@ const appOptions = {
},
async toggleThinkingMode() {
- const target = this.thinkingMode ? 'fast' : 'thinking';
- await this.setRunMode(target);
+ await this.handleCycleRunMode();
+ },
+
+ handleQuickModeToggle() {
+ if (!this.isConnected || this.streamingMessage) {
+ return;
+ }
+ this.handleCycleRunMode();
},
triggerFileUpload() {
@@ -2034,7 +2047,8 @@ const appOptions = {
async handleCycleRunMode() {
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];
await this.setRunMode(nextMode);
},
@@ -2044,7 +2058,7 @@ const appOptions = {
this.modeMenuOpen = false;
return;
}
- if (mode === this.runMode) {
+ if (mode === this.resolvedRunMode) {
this.modeMenuOpen = false;
this.closeQuickMenu();
return;
diff --git a/static/src/components/input/InputComposer.vue b/static/src/components/input/InputComposer.vue
index 11f314f..bafc374 100644
--- a/static/src/components/input/InputComposer.vue
+++ b/static/src/components/input/InputComposer.vue
@@ -42,18 +42,21 @@
:uploading="uploading"
:streaming-message="streamingMessage"
:thinking-mode="thinkingMode"
+ :run-mode="runMode"
:tool-menu-open="toolMenuOpen"
:tool-settings="toolSettings"
:tool-settings-loading="toolSettingsLoading"
:settings-open="settingsOpen"
+ :mode-menu-open="modeMenuOpen"
:compressing="compressing"
:current-conversation-id="currentConversationId"
:icon-style="iconStyle"
:tool-category-icon="toolCategoryIcon"
@quick-upload="triggerQuickUpload"
@toggle-tool-menu="$emit('toggle-tool-menu')"
- @quick-mode-toggle="$emit('quick-mode-toggle')"
@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)"
@realtime-terminal="$emit('realtime-terminal')"
@toggle-focus-panel="$emit('toggle-focus-panel')"
@@ -81,7 +84,8 @@ const emit = defineEmits([
'send-or-stop',
'quick-upload',
'toggle-tool-menu',
- 'quick-mode-toggle',
+ 'toggle-mode-menu',
+ 'select-run-mode',
'toggle-settings',
'update-tool-category',
'realtime-terminal',
@@ -99,8 +103,10 @@ const props = defineProps<{
streamingMessage: boolean;
uploading: boolean;
thinkingMode: boolean;
+ runMode: 'fast' | 'thinking' | 'deep';
quickMenuOpen: boolean;
toolMenuOpen: boolean;
+ modeMenuOpen: boolean;
toolSettings: Array<{ id: string; label: string; enabled: boolean }>;
toolSettingsLoading: boolean;
settingsOpen: boolean;
diff --git a/static/src/components/input/QuickMenu.vue b/static/src/components/input/QuickMenu.vue
index 28f1496..3d34ba2 100644
--- a/static/src/components/input/QuickMenu.vue
+++ b/static/src/components/input/QuickMenu.vue
@@ -4,6 +4,15 @@
+
-
+
+
+
+