- 彻底移除「每 N 次请求强制思考」的间隔调度机制(思考/非思考交替导致缓存命中率极低) - 运行模式收敛为 fast/thinking:原 deep 改名 thinking,历史数据读取时映射 - 新增推理强度五档滑块(low/medium/high/xhigh/max + 默认不传参),置于模型/模式弹窗 - 模型配置新增 reasoning_effort 开关;档位会话级持久化到对话 meta,随对话保存/恢复 - 个人空间新增「默认推理强度」;新用户默认运行模式改为 thinking - 滑块交互:JS 插值驱动(点击/拖动/吸附全程平滑),首帧档位语义定位无闪烁 - 保存链路:前端防抖 600ms + conversation_id 兜底,修复 /new 发消息丢档位、切对话恢复错误
128 lines
5.2 KiB
Python
128 lines
5.2 KiB
Python
from __future__ import annotations
|
|
from server.chat import chat_bp
|
|
import json, time
|
|
from datetime import datetime
|
|
from typing import Dict, Any, Optional
|
|
from pathlib import Path
|
|
from io import BytesIO
|
|
import zipfile
|
|
import os
|
|
|
|
from flask import Blueprint, jsonify, request, session, send_file
|
|
from werkzeug.utils import secure_filename
|
|
from werkzeug.exceptions import RequestEntityTooLarge
|
|
import secrets
|
|
|
|
from config import MAX_UPLOAD_SIZE, OUTPUT_FORMATS
|
|
from modules.personalization_manager import (
|
|
load_personalization_config,
|
|
resolve_context_compression_settings,
|
|
save_personalization_config,
|
|
RECENT_CONVERSATIONS_PROMPT_LIMIT_MIN,
|
|
RECENT_CONVERSATIONS_PROMPT_LIMIT_MAX,
|
|
)
|
|
from modules.skills_manager import (
|
|
get_skills_catalog,
|
|
infer_private_skills_dir,
|
|
merge_enabled_skills,
|
|
sync_workspace_skills,
|
|
)
|
|
from modules.upload_security import UploadSecurityError
|
|
from modules.host_sandbox_policy import load_policy, save_policy
|
|
from modules.user_manager import UserWorkspace
|
|
from core.web_terminal import WebTerminal
|
|
from config.model_profiles import get_model_context_window
|
|
|
|
from server.auth_helpers import api_login_required, resolve_admin_policy, get_current_user_record, get_current_username
|
|
from server.context import with_terminal, get_gui_manager, get_upload_guard, build_upload_error_response, ensure_conversation_loaded, get_or_create_usage_tracker
|
|
from server.security import rate_limited, prune_socket_tokens
|
|
from server.utils_common import debug_log
|
|
from server.state import PROJECT_MAX_STORAGE_MB, pending_socket_tokens, SOCKET_TOKEN_TTL_SECONDS
|
|
from server.state import tool_approval_manager, user_question_manager
|
|
from server.extensions import socketio
|
|
from server.monitor import get_cached_monitor_snapshot
|
|
from server.files import sanitize_filename_preserve_unicode
|
|
|
|
UPLOAD_FOLDER_NAME = ".astrion/user_upload"
|
|
@chat_bp.route('/api/user-questions/pending', methods=['GET'])
|
|
@api_login_required
|
|
@with_terminal
|
|
def list_pending_user_questions(terminal: WebTerminal, workspace: UserWorkspace, username: str):
|
|
"""获取当前用户待回答的问题列表。"""
|
|
requested_conv_id = (request.args.get("conversation_id") or "").strip() or None
|
|
if requested_conv_id is None:
|
|
requested_conv_id = getattr(terminal.context_manager, "current_conversation_id", None)
|
|
items = user_question_manager.list_pending(username=username, conversation_id=requested_conv_id)
|
|
return jsonify({
|
|
"success": True,
|
|
"items": items,
|
|
"conversation_id": requested_conv_id,
|
|
})
|
|
|
|
@chat_bp.route('/api/user-questions/<question_id>/answer', methods=['POST'])
|
|
@api_login_required
|
|
@with_terminal
|
|
@rate_limited("user_question_answer", 120, 60, scope="user")
|
|
def answer_user_question(terminal: WebTerminal, workspace: UserWorkspace, username: str, question_id: str):
|
|
"""提交 ask_user 工具问题的回答。"""
|
|
data = request.get_json() or {}
|
|
try:
|
|
item = user_question_manager.answer(
|
|
question_id=question_id,
|
|
username=username,
|
|
selected_option_id=data.get("selected_option_id"),
|
|
text=data.get("text"),
|
|
)
|
|
except ValueError as exc:
|
|
return jsonify({"success": False, "error": str(exc)}), 400
|
|
except KeyError:
|
|
return jsonify({"success": False, "error": "问题不存在"}), 404
|
|
except PermissionError as exc:
|
|
return jsonify({"success": False, "error": str(exc)}), 403
|
|
except Exception as exc:
|
|
return jsonify({"success": False, "error": str(exc)}), 500
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"item": item,
|
|
})
|
|
|
|
@chat_bp.route('/api/tool-approvals/pending', methods=['GET'])
|
|
@api_login_required
|
|
@with_terminal
|
|
def list_pending_tool_approvals(terminal: WebTerminal, workspace: UserWorkspace, username: str):
|
|
"""获取当前用户待审批工具列表。"""
|
|
requested_conv_id = (request.args.get("conversation_id") or "").strip() or None
|
|
if requested_conv_id is None:
|
|
requested_conv_id = getattr(terminal.context_manager, "current_conversation_id", None)
|
|
items = tool_approval_manager.list_pending(username=username, conversation_id=requested_conv_id)
|
|
return jsonify({
|
|
"success": True,
|
|
"items": items,
|
|
"conversation_id": requested_conv_id,
|
|
})
|
|
|
|
@chat_bp.route('/api/tool-approvals/<approval_id>/decision', methods=['POST'])
|
|
@api_login_required
|
|
@with_terminal
|
|
@rate_limited("tool_approval_decision", 60, 60, scope="user")
|
|
def decide_tool_approval(terminal: WebTerminal, workspace: UserWorkspace, username: str, approval_id: str):
|
|
"""提交工具审批决策。"""
|
|
data = request.get_json() or {}
|
|
decision = str(data.get("decision") or "").strip().lower()
|
|
try:
|
|
item = tool_approval_manager.decide(approval_id=approval_id, username=username, decision=decision)
|
|
except ValueError as exc:
|
|
return jsonify({"success": False, "error": str(exc)}), 400
|
|
except KeyError:
|
|
return jsonify({"success": False, "error": "审批请求不存在"}), 404
|
|
except PermissionError as exc:
|
|
return jsonify({"success": False, "error": str(exc)}), 403
|
|
except Exception as exc:
|
|
return jsonify({"success": False, "error": str(exc)}), 500
|
|
|
|
return jsonify({
|
|
"success": True,
|
|
"item": item,
|
|
})
|