diff --git a/api_doc/openapi.yaml b/api_doc/openapi.yaml index 4aecfba..0869241 100644 --- a/api_doc/openapi.yaml +++ b/api_doc/openapi.yaml @@ -117,7 +117,7 @@ components: default_model: type: string default: kimi - description: kimi/deepseek/qwen3-max/qwen3-vl-plus + description: kimi/deepseek/qwen3-vl-plus/minimax-m2.5 additionalProperties: true example: enabled: true diff --git a/api_doc/prompts_personalization.md b/api_doc/prompts_personalization.md index 3aa8496..9f6fd66 100644 --- a/api_doc/prompts_personalization.md +++ b/api_doc/prompts_personalization.md @@ -47,7 +47,7 @@ | `default_run_mode` | "fast"/"thinking"/"deep"/null | null | 默认运行模式:非法值会变成 null | | `auto_generate_title` | bool | true | 是否自动生成对话标题 | | `tool_intent_enabled` | bool | true | 工具意图提示开关(属于配置结构的一部分) | -| `default_model` | string | "kimi" | 默认模型:仅允许 `"kimi"|"deepseek"|"qwen3-max"|"qwen3-vl-plus"`,非法值回落到 `"kimi"` | +| `default_model` | string | "kimi" | 默认模型:仅允许 `"kimi"|"deepseek"|"qwen3-vl-plus"|"minimax-m2.5"`,非法值回落到 `"kimi"` | ### 最小示例(启用 + 2 条注意事项) diff --git a/config/model_profiles.py b/config/model_profiles.py index 874544d..109cb15 100644 --- a/config/model_profiles.py +++ b/config/model_profiles.py @@ -11,6 +11,7 @@ CONTEXT_WINDOWS = { "kimi-k2.5": 256_000, "qwen3-max": 256_000, "qwen3-vl-plus": 256_000, + "minimax-m2.5": 204_800, "deepseek": 128_000, } @@ -32,7 +33,12 @@ DEEPSEEK_THINK_MODEL = _env("MODEL_DEEPSEEK_THINK", "deepseek-reasoner") QWEN_BASE = _env("API_BASE_QWEN", "https://dashscope.aliyuncs.com/compatible-mode/v1") QWEN_KEY = _env("API_KEY_QWEN", _env("DASHSCOPE_API_KEY", "")) QWEN_MAX_MODEL = _env("MODEL_QWEN_MAX", "qwen3-max") -QWEN_VL_MODEL = _env("MODEL_QWEN_VL", "qwen3-vl-plus") +QWEN_VL_MODEL = _env("MODEL_QWEN_VL", "qwen3.5-plus") + +# MiniMax +MINIMAX_BASE = _env("API_BASE_MINIMAX", "https://api.minimaxi.com/v1") +MINIMAX_KEY = _env("API_KEY_MINIMAX", "") +MINIMAX_MODEL = _env("MODEL_MINIMAX", "MiniMax-M2.5") MODEL_PROFILES = { @@ -110,7 +116,8 @@ MODEL_PROFILES = { "thinking": None, # 不支持思考 "supports_thinking": False, "fast_only": True, - "name": "Qwen3-Max" + "name": "Qwen3-Max", + "hidden": True }, "qwen3-vl-plus": { "context_window": CONTEXT_WINDOWS["qwen3-vl-plus"], @@ -132,7 +139,30 @@ MODEL_PROFILES = { }, "supports_thinking": True, "fast_only": False, - "name": "Qwen3-VL" + "name": "Qwen3.5" + }, + "minimax-m2.5": { + "context_window": CONTEXT_WINDOWS["minimax-m2.5"], + "fast": { + "base_url": MINIMAX_BASE, + "api_key": MINIMAX_KEY, + "model_id": MINIMAX_MODEL, + "max_tokens": 65536, + "context_window": CONTEXT_WINDOWS["minimax-m2.5"], + "extra_params": {"reasoning_split": True} + }, + "thinking": { + "base_url": MINIMAX_BASE, + "api_key": MINIMAX_KEY, + "model_id": MINIMAX_MODEL, + "max_tokens": 65536, + "context_window": CONTEXT_WINDOWS["minimax-m2.5"], + "extra_params": {"reasoning_split": True} + }, + "supports_thinking": True, + "fast_only": False, + "deep_only": True, + "name": "MiniMax-M2.5" } } @@ -158,9 +188,14 @@ MODEL_PROMPT_OVERRIDES = { "deep_thinking_line": "Qwen3-Max 不支持深度思考模式,将保持快速模式。" }, "qwen3-vl-plus": { - "model_description": "你的基础模型是 Qwen3-VL-Plus,支持图文多模态理解,接口来自通义千问 DashScope。", - "thinking_model_line": "思考模式时,请求的模型仍为 Qwen3-VL-Plus(开启思考能力),后续请求会切回快速模型。", - "deep_thinking_line": "在深度思考模式中,请求的模型是 Qwen3-VL-Plus(思考版),以获得更强的分析能力。" + "model_description": "你的基础模型是 Qwen3.5,由通义千问提供,支持图文多模态理解。", + "thinking_model_line": "思考模式时仍使用 Qwen3.5,并开启思考能力。", + "deep_thinking_line": "深度思考模式下,所有请求都将启用思考能力,以获得更强的分析表现。" + }, + "minimax-m2.5": { + "model_description": "你的基础模型是 MiniMax-M2.5,支持超长上下文,当前仅以深度思考模式运行。", + "thinking_model_line": "MiniMax-M2.5 为思考模型,快速模式不会使用。", + "deep_thinking_line": "深度思考模式下,所有请求持续输出思考过程并给出最终回答。" } } diff --git a/core/main_terminal.py b/core/main_terminal.py index da2237e..34cbd11 100644 --- a/core/main_terminal.py +++ b/core/main_terminal.py @@ -1479,7 +1479,7 @@ class MainTerminal: "type": "function", "function": { "name": "vlm_analyze", - "description": "使用大参数视觉语言模型(Qwen-VL模型)理解图片:文字、物体、布局、表格等,仅支持本地路径。", + "description": "使用大参数视觉语言模型(Qwen3.5)理解图片:文字、物体、布局、表格等,仅支持本地路径。", "parameters": { "type": "object", "properties": self._inject_intent({ @@ -1872,7 +1872,7 @@ class MainTerminal: } } ] - # 视觉模型(Qwen-VL / Kimi-k2.5)自带多模态能力,不再暴露 vlm_analyze,改为 view_image + # 视觉模型(Qwen3.5 / Kimi-k2.5)自带多模态能力,不再暴露 vlm_analyze,改为 view_image / view_video if getattr(self, "model_key", None) in {"qwen3-vl-plus", "kimi-k2.5"}: tools = [ tool for tool in tools @@ -1895,24 +1895,23 @@ class MainTerminal: } } }) - if getattr(self, "model_key", None) == "kimi-k2.5": - tools.append({ - "type": "function", - "function": { + tools.append({ + "type": "function", + "function": { "name": "view_video", "description": "将指定本地视频附加到工具结果中(tool 消息携带 video_url),便于模型查看视频内容。", - "parameters": { - "type": "object", - "properties": self._inject_intent({ + "parameters": { + "type": "object", + "properties": self._inject_intent({ "path": { "type": "string", "description": "项目内的视频相对路径(不要以 /workspace 开头);宿主机模式可用绝对路径。支持 mp4/mov/mkv/avi/webm。" - } - }), - "required": ["path"] - } + } + }), + "required": ["path"] } - }) + } + }) # 附加自定义工具(仅管理员可见) custom_tools = self._build_custom_tools() if custom_tools: @@ -2045,7 +2044,11 @@ class MainTerminal: if abs_path.stat().st_size > 50 * 1024 * 1024: return json.dumps({"success": False, "error": "视频过大,需 <= 50MB"}, ensure_ascii=False) self.pending_video_view = {"path": str(path)} - result = {"success": True, "message": "视频已附加到工具结果中,将随 tool 返回。", "path": path} + result = { + "success": True, + "message": "视频已附加到工具结果中,将随 tool 返回。", + "path": path + } # 终端会话管理工具 elif tool_name == "terminal_session": @@ -2528,7 +2531,7 @@ class MainTerminal: def build_messages(self, context: Dict, user_input: str) -> List[Dict]: """构建消息列表(添加终端内容注入)""" - # 加载系统提示(Qwen-VL 使用专用提示) + # 加载系统提示(Qwen3.5 使用专用提示) prompt_name = "main_system_qwenvl" if getattr(self, "model_key", "kimi") in {"qwen3-vl-plus", "kimi-k2.5"} else "main_system" system_prompt = self.load_prompt(prompt_name) @@ -2810,9 +2813,9 @@ class MainTerminal: normalized = mode.lower() if normalized not in allowed: raise ValueError(f"不支持的模式: {mode}") - # Qwen-VL 官方不支持深度思考模式 - if getattr(self, "model_key", None) == "qwen3-vl-plus" and normalized == "deep": - raise ValueError("Qwen-VL 不支持深度思考模式") + # 仅深度思考模型限制 + if getattr(self, "model_profile", {}).get("deep_only") and normalized != "deep": + raise ValueError("当前模型仅支持深度思考模式") # fast-only 模型限制 if getattr(self, "model_profile", {}).get("fast_only") and normalized != "fast": raise ValueError("当前模型仅支持快速模式") @@ -2840,9 +2843,9 @@ class MainTerminal: def set_model(self, model_key: str) -> str: profile = get_model_profile(model_key) if getattr(self.context_manager, "has_images", False) and model_key not in {"qwen3-vl-plus", "kimi-k2.5"}: - raise ValueError("当前对话包含图片,仅支持 Qwen-VL 或 Kimi-k2.5") - if getattr(self.context_manager, "has_videos", False) and model_key != "kimi-k2.5": - raise ValueError("当前对话包含视频,仅支持 Kimi-k2.5") + raise ValueError("当前对话包含图片,仅支持 Qwen3.5 或 Kimi-k2.5") + if getattr(self.context_manager, "has_videos", False) and model_key not in {"qwen3-vl-plus", "kimi-k2.5"}: + raise ValueError("当前对话包含视频,仅支持 Qwen3.5 或 Kimi-k2.5") self.model_key = model_key self.model_profile = profile # 将模型标识传递给底层 API 客户端,便于按模型做兼容处理 @@ -2852,9 +2855,9 @@ class MainTerminal: # fast-only 模型强制快速模式 if profile.get("fast_only") and self.run_mode != "fast": self.set_run_mode("fast") - # Qwen-VL 不支持深度思考,自动回落到思考模式 - if model_key == "qwen3-vl-plus" and self.run_mode == "deep": - self.set_run_mode("thinking") + # 仅深度思考模型强制 deep + if profile.get("deep_only") and self.run_mode != "deep": + self.set_run_mode("deep") # 如果模型支持思考,但当前 run_mode 为 thinking/deep,则保持;否则无需调整 self.api_client.start_new_task(force_deep=self.deep_thinking_mode) return self.model_key diff --git a/modules/admin_policy_manager.py b/modules/admin_policy_manager.py index b371b0f..9fe38c8 100644 --- a/modules/admin_policy_manager.py +++ b/modules/admin_policy_manager.py @@ -17,7 +17,7 @@ from config.paths import ADMIN_POLICY_FILE from modules.custom_tool_registry import CustomToolRegistry, build_default_tool_category # 可用的模型 key(与前端、model_profiles 保持一致) -ALLOWED_MODELS = {"kimi", "deepseek", "qwen3-max", "qwen3-vl-plus"} +ALLOWED_MODELS = {"kimi", "deepseek", "qwen3-vl-plus", "minimax-m2.5"} # UI 禁用项键名,前后端统一 UI_BLOCK_KEYS = [ diff --git a/modules/ocr_client.py b/modules/ocr_client.py index 455f27d..23a1b4d 100644 --- a/modules/ocr_client.py +++ b/modules/ocr_client.py @@ -13,7 +13,7 @@ from modules.file_manager import FileManager class OCRClient: - """封装 VLM(如 DeepSeek-OCR / Qwen-VL)调用逻辑。""" + """封装 VLM(如 DeepSeek-OCR / Qwen3.5)调用逻辑。""" def __init__(self, project_path: str, file_manager: FileManager): self.project_path = Path(project_path).resolve() diff --git a/modules/personalization_manager.py b/modules/personalization_manager.py index b4e6e26..78b4e4b 100644 --- a/modules/personalization_manager.py +++ b/modules/personalization_manager.py @@ -113,7 +113,7 @@ def sanitize_personalization_payload( base.update(fallback) data = payload or {} allowed_tool_categories = set(TOOL_CATEGORIES.keys()) - allowed_models = {"kimi", "kimi-k2.5", "deepseek", "qwen3-max", "qwen3-vl-plus"} + allowed_models = {"kimi", "kimi-k2.5", "deepseek", "qwen3-vl-plus", "minimax-m2.5"} allowed_image_modes = {"original", "1080p", "720p", "540p"} def _resolve_short_field(key: str) -> str: diff --git a/prompts/main_system.txt b/prompts/main_system.txt index 602bb38..222ee12 100644 --- a/prompts/main_system.txt +++ b/prompts/main_system.txt @@ -57,7 +57,7 @@ ### 3.3 视觉理解 - **非视觉模型**:`vlm_analyze` 调用 VLM 分析图片 -- **视觉模型**(Qwen-VL / Kimi-k2.5):`view_image` 直接查看图片,`view_video` 查看视频(Kimi-k2.5) +- **视觉模型**(Qwen3.5 / Kimi-k2.5):`view_image` 直接查看图片,`view_video` 查看视频 ### 3.4 终端操作 diff --git a/prompts/main_system_qwenvl.txt b/prompts/main_system_qwenvl.txt index 8e7b06c..b3c7ed4 100644 --- a/prompts/main_system_qwenvl.txt +++ b/prompts/main_system_qwenvl.txt @@ -56,7 +56,7 @@ ### 3.3 视觉理解(重点) -你**自带多模态能力**,用户可以直接发送图片;如需主动查看本地图片/视频,可调用 `view_image`/`view_video` 指定路径,系统会在工具结果中附带媒体(tool 消息携带 image_url/video_url)供你查看。 +你**自带多模态能力**,用户可以直接发送图片/视频;如需主动查看本地图片/视频,可调用 `view_image`/`view_video` 指定路径,系统会在工具结果中附带媒体(tool 消息携带 image_url/video_url)供你查看。 当用户提出"这是什么""识别文字/表格/票据""找瑕疵/细节""读屏/按钮含义"等图片分析任务时,优先采用下面的方法,保证细节充分、结论可验证: diff --git a/server/_conversation_segment.py b/server/_conversation_segment.py index 68b53b1..a2fbbd9 100644 --- a/server/_conversation_segment.py +++ b/server/_conversation_segment.py @@ -1457,21 +1457,33 @@ async def handle_task_with_sender(terminal: WebTerminal, workspace: UserWorkspac if finish_reason: last_finish_reason = finish_reason - # 处理思考内容 + # 处理思考内容(兼容 reasoning_content / reasoning_details) + reasoning_content = "" if "reasoning_content" in delta: - reasoning_content = delta["reasoning_content"] - if reasoning_content: - reasoning_chunks += 1 - debug_log(f" 思考内容 #{reasoning_chunks}: {len(reasoning_content)} 字符") - - if not thinking_started: - in_thinking = True - thinking_started = True - sender('thinking_start', {}) - await asyncio.sleep(0.05) - - current_thinking += reasoning_content - sender('thinking_chunk', {'content': reasoning_content}) + reasoning_content = delta.get("reasoning_content") or "" + elif "reasoning_details" in delta: + details = delta.get("reasoning_details") + if isinstance(details, list): + parts = [] + for item in details: + if isinstance(item, dict): + text = item.get("text") + if text: + parts.append(text) + if parts: + reasoning_content = "".join(parts) + if reasoning_content: + reasoning_chunks += 1 + debug_log(f" 思考内容 #{reasoning_chunks}: {len(reasoning_content)} 字符") + + if not thinking_started: + in_thinking = True + thinking_started = True + sender('thinking_start', {}) + await asyncio.sleep(0.05) + + current_thinking += reasoning_content + sender('thinking_chunk', {'content': reasoning_content}) # 处理正常内容 if "content" in delta: @@ -2424,10 +2436,11 @@ async def handle_task_with_sender(terminal: WebTerminal, workspace: UserWorkspac video_path = inj.get("path") if isinstance(inj, dict) else None if video_path: text_part = tool_result_content if isinstance(tool_result_content, str) else "" + video_payload = [video_path] tool_message_content = web_terminal.context_manager._build_content_with_images( text_part, [], - [video_path] + video_payload ) tool_videos = [video_path] if metadata_payload is None: diff --git a/server/_socket_segment.py b/server/_socket_segment.py index 934522a..2f8f3e0 100644 --- a/server/_socket_segment.py +++ b/server/_socket_segment.py @@ -196,7 +196,7 @@ def handle_message(data): emit('error', {'message': '消息不能为空'}) return if images and getattr(terminal, "model_key", None) not in {"qwen3-vl-plus", "kimi-k2.5"}: - emit('error', {'message': '当前模型不支持图片,请切换到 Qwen-VL 或 Kimi-k2.5'}) + emit('error', {'message': '当前模型不支持图片,请切换到 Qwen3.5 或 Kimi-k2.5'}) return print(f"[WebSocket] 收到消息: {message}") diff --git a/server/api_v1.py b/server/api_v1.py index 84bcdeb..1b4ed34 100644 --- a/server/api_v1.py +++ b/server/api_v1.py @@ -677,11 +677,14 @@ def create_personalization_api(): def list_models_api(): items = [] for key, profile in MODEL_PROFILES.items(): + if profile.get("hidden"): + continue items.append({ "model_key": key, "name": profile.get("name", key), "supports_thinking": profile.get("supports_thinking", False), "fast_only": profile.get("fast_only", False), + "deep_only": profile.get("deep_only", False), }) return jsonify({"success": True, "items": items}) diff --git a/server/app_legacy.py b/server/app_legacy.py index 58a5e5d..b3fdffa 100644 --- a/server/app_legacy.py +++ b/server/app_legacy.py @@ -922,7 +922,7 @@ def get_user_resources(username: Optional[str] = None) -> Tuple[Optional[WebTerm terminal.admin_policy_version = policy.get("updated_at") # 若当前模型被禁用,则回退到第一个可用模型 if terminal.model_key in disabled_models: - for candidate in ["kimi-k2.5", "kimi", "deepseek", "qwen3-vl-plus", "qwen3-max"]: + for candidate in ["kimi-k2.5", "kimi", "deepseek", "qwen3-vl-plus", "minimax-m2.5"]: if candidate not in disabled_models: try: terminal.set_model(candidate) diff --git a/server/chat_flow.py b/server/chat_flow.py index 2a7b65c..2c5e4c1 100644 --- a/server/chat_flow.py +++ b/server/chat_flow.py @@ -1335,21 +1335,33 @@ async def handle_task_with_sender(terminal: WebTerminal, workspace: UserWorkspac if finish_reason: last_finish_reason = finish_reason - # 处理思考内容 + # 处理思考内容(兼容 reasoning_content / reasoning_details) + reasoning_content = "" if "reasoning_content" in delta: - reasoning_content = delta["reasoning_content"] - if reasoning_content: - reasoning_chunks += 1 - debug_log(f" 思考内容 #{reasoning_chunks}: {len(reasoning_content)} 字符") - - if not thinking_started: - in_thinking = True - thinking_started = True - sender('thinking_start', {}) - await asyncio.sleep(0.05) - - current_thinking += reasoning_content - sender('thinking_chunk', {'content': reasoning_content}) + reasoning_content = delta.get("reasoning_content") or "" + elif "reasoning_details" in delta: + details = delta.get("reasoning_details") + if isinstance(details, list): + parts = [] + for item in details: + if isinstance(item, dict): + text = item.get("text") + if text: + parts.append(text) + if parts: + reasoning_content = "".join(parts) + if reasoning_content: + reasoning_chunks += 1 + debug_log(f" 思考内容 #{reasoning_chunks}: {len(reasoning_content)} 字符") + + if not thinking_started: + in_thinking = True + thinking_started = True + sender('thinking_start', {}) + await asyncio.sleep(0.05) + + current_thinking += reasoning_content + sender('thinking_chunk', {'content': reasoning_content}) # 处理正常内容 if "content" in delta: @@ -2335,10 +2347,11 @@ async def handle_task_with_sender(terminal: WebTerminal, workspace: UserWorkspac video_path = inj.get("path") if isinstance(inj, dict) else None if video_path: text_part = tool_result_content if isinstance(tool_result_content, str) else "" + video_payload = [video_path] tool_message_content = web_terminal.context_manager._build_content_with_images( text_part, [], - [video_path] + video_payload ) tool_videos = [video_path] if metadata_payload is None: diff --git a/server/context.py b/server/context.py index f955e90..ef8c44c 100644 --- a/server/context.py +++ b/server/context.py @@ -218,7 +218,7 @@ def get_user_resources(username: Optional[str] = None, workspace_id: Optional[st terminal.admin_policy_ui_blocks = policy.get("ui_blocks") or {} terminal.admin_policy_version = policy.get("updated_at") if terminal.model_key in disabled_models: - for candidate in ["kimi-k2.5", "kimi", "deepseek", "qwen3-vl-plus", "qwen3-max"]: + for candidate in ["kimi-k2.5", "kimi", "deepseek", "qwen3-vl-plus", "minimax-m2.5"]: if candidate not in disabled_models: try: terminal.set_model(candidate) diff --git a/server/socket_handlers.py b/server/socket_handlers.py index 3e1b3e6..19058b1 100644 --- a/server/socket_handlers.py +++ b/server/socket_handlers.py @@ -230,10 +230,10 @@ def handle_message(data): emit('error', {'message': '消息不能为空'}) return if images and getattr(terminal, "model_key", None) not in {"qwen3-vl-plus", "kimi-k2.5"}: - emit('error', {'message': '当前模型不支持图片,请切换到 Qwen-VL 或 Kimi-k2.5'}) + emit('error', {'message': '当前模型不支持图片,请切换到 Qwen3.5 或 Kimi-k2.5'}) return - if videos and getattr(terminal, "model_key", None) != "kimi-k2.5": - emit('error', {'message': '当前模型不支持视频,请切换到 Kimi-k2.5'}) + if videos and getattr(terminal, "model_key", None) not in {"qwen3-vl-plus", "kimi-k2.5"}: + emit('error', {'message': '当前模型不支持视频,请切换到 Qwen3.5 或 Kimi-k2.5'}) return if images and videos: emit('error', {'message': '图片和视频请分开发送'}) diff --git a/static/src/app.ts b/static/src/app.ts index 61223e0..490450a 100644 --- a/static/src/app.ts +++ b/static/src/app.ts @@ -2612,16 +2612,16 @@ const appOptions = { if (hasImages && !['qwen3-vl-plus', 'kimi-k2.5'].includes(this.currentModelKey)) { this.uiPushToast({ title: '当前模型不支持图片', - message: '请切换到 Qwen-VL 或 Kimi-k2.5 再发送图片', + message: '请切换到 Qwen3.5 或 Kimi-k2.5 再发送图片', type: 'error' }); return; } - if (hasVideos && this.currentModelKey !== 'kimi-k2.5') { + if (hasVideos && !['qwen3-vl-plus', 'kimi-k2.5'].includes(this.currentModelKey)) { this.uiPushToast({ title: '当前模型不支持视频', - message: '请切换到 Kimi-k2.5 后再发送视频', + message: '请切换到 Qwen3.5 或 Kimi-k2.5 后再发送视频', type: 'error' }); return; @@ -2876,7 +2876,7 @@ const appOptions = { if (!['qwen3-vl-plus', 'kimi-k2.5'].includes(this.currentModelKey)) { this.uiPushToast({ title: '当前模型不支持图片', - message: '请选择 Qwen-VL 或 Kimi-k2.5 后再发送图片', + message: '请选择 Qwen3.5 或 Kimi-k2.5 后再发送图片', type: 'error' }); return; @@ -2891,10 +2891,10 @@ const appOptions = { }, async openVideoPicker() { - if (this.currentModelKey !== 'kimi-k2.5') { + if (!['qwen3-vl-plus', 'kimi-k2.5'].includes(this.currentModelKey)) { this.uiPushToast({ title: '当前模型不支持视频', - message: '请切换到 Kimi-k2.5 后再发送视频', + message: '请切换到 Qwen3.5 或 Kimi-k2.5 后再发送视频', type: 'error' }); return; @@ -3163,7 +3163,7 @@ const appOptions = { if (this.conversationHasImages && !['qwen3-vl-plus', 'kimi-k2.5'].includes(key)) { this.uiPushToast({ title: '切换失败', - message: '当前对话包含图片,仅支持 Qwen-VL 或 Kimi-k2.5', + message: '当前对话包含图片,仅支持 Qwen3.5 或 Kimi-k2.5', type: 'error' }); return; @@ -3187,16 +3187,11 @@ const appOptions = { this.thinkingMode = data.thinking_mode ?? (data.run_mode !== 'fast'); } else { // 前端兼容策略:根据模型特性自动调整运行模式 - if (key === 'qwen3-vl-plus') { - // Qwen-VL 不支持深度思考,若当前为 deep 则回落到思考模式 - if (this.runMode === 'deep') { - this.runMode = 'thinking'; - this.thinkingMode = true; - } else { - this.thinkingMode = this.runMode !== 'fast'; - } - } else if (key === 'qwen3-max') { - // Qwen-Max 仅快速模式 + const currentModel = modelStore.currentModel; + if (currentModel?.deepOnly) { + this.runMode = 'deep'; + this.thinkingMode = true; + } else if (currentModel?.fastOnly) { this.runMode = 'fast'; this.thinkingMode = false; } else { @@ -3244,12 +3239,12 @@ const appOptions = { } const modelStore = useModelStore(); const fastOnly = modelStore.currentModel?.fastOnly; - const currentModelKey = modelStore.currentModel?.key; + const deepOnly = modelStore.currentModel?.deepOnly; if (fastOnly && mode !== 'fast') { if (!options.suppressToast) { this.uiPushToast({ title: '模式不可用', - message: 'Qwen-Max只支持快速模式', + message: '当前模型仅支持快速模式', type: 'warning' }); } @@ -3257,12 +3252,11 @@ const appOptions = { this.inputCloseMenus(); return; } - // Qwen-VL 不支持深度思考模式 - if (currentModelKey === 'qwen3-vl-plus' && mode === 'deep') { + if (deepOnly && mode !== 'deep') { if (!options.suppressToast) { this.uiPushToast({ title: '模式不可用', - message: 'Qwen-VL 不支持深度思考模式,请使用快速或思考模式', + message: '当前模型仅支持深度思考模式', type: 'warning' }); } diff --git a/static/src/components/input/QuickMenu.vue b/static/src/components/input/QuickMenu.vue index 5a56078..03cb954 100644 --- a/static/src/components/input/QuickMenu.vue +++ b/static/src/components/input/QuickMenu.vue @@ -27,7 +27,7 @@ 发送图片