fix(goal): 停止对话后清理工作区目标状态,避免继承到新对话
- /api/tasks/<task_id>/cancel 现在会同步停止该工作区的目标模式 - POST /api/tasks 创建非目标模式任务时清理残留活动目标 - 保留 /api/client_debug_log 前端日志上报端点供后续复用
This commit is contained in:
parent
7b683ab390
commit
8235c4ce70
@ -783,6 +783,7 @@ DEBUG_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "debug_stream.log"
|
||||
CHUNK_BACKEND_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "chunk_backend.log"
|
||||
CHUNK_FRONTEND_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "chunk_frontend.log"
|
||||
STREAMING_DEBUG_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "streaming_debug.log"
|
||||
GOAL_MODE_DEBUG_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "goal_mode_debug.log"
|
||||
UPLOAD_FOLDER_NAME = ".agents/user_upload"
|
||||
|
||||
|
||||
@ -1171,6 +1172,14 @@ def log_streaming_debug_entry(data: Dict[str, Any]):
|
||||
_write_log(STREAMING_DEBUG_LOG_FILE, serialized)
|
||||
|
||||
|
||||
def log_goal_mode_debug_entry(data: Dict[str, Any]):
|
||||
try:
|
||||
serialized = json.dumps(data, ensure_ascii=False)
|
||||
except Exception:
|
||||
serialized = str(data)
|
||||
_write_log(GOAL_MODE_DEBUG_LOG_FILE, serialized)
|
||||
|
||||
|
||||
def get_thinking_state(terminal: WebTerminal) -> Dict[str, Any]:
|
||||
"""获取(或初始化)思考调度状态。"""
|
||||
state = getattr(terminal, "_thinking_state", None)
|
||||
@ -1399,6 +1408,19 @@ def voice_debug():
|
||||
return jsonify({'ok': True, 'file': filename})
|
||||
|
||||
|
||||
@app.route('/api/client_debug_log', methods=['POST'])
|
||||
def client_debug_log():
|
||||
"""接收前端目标模式等调试日志"""
|
||||
try:
|
||||
data = request.get_json(silent=True) or {}
|
||||
entry = dict(data)
|
||||
entry.setdefault('server_ts', time.time())
|
||||
log_goal_mode_debug_entry(entry)
|
||||
return jsonify({'ok': True})
|
||||
except Exception as e:
|
||||
return jsonify({'ok': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_arguments()
|
||||
run_server(
|
||||
|
||||
@ -20,6 +20,7 @@ from .state import stop_flags
|
||||
from .utils_common import debug_log, log_conn_diag
|
||||
from utils.host_workspace_debug import write_host_workspace_debug
|
||||
from config import DATA_DIR, WORKSPACE_SKILLS_DIRNAME
|
||||
from modules.goal_state_manager import GoalStateManager, REASON_USER_CANCEL
|
||||
|
||||
|
||||
SKILL_FRONTMATTER_RE = re.compile(r"^---\s*\n(?P<body>.*?)\n---\s*\n?", re.S)
|
||||
@ -980,6 +981,18 @@ def create_task_api():
|
||||
message_source = payload.get("message_source")
|
||||
max_iterations = payload.get("max_iterations")
|
||||
goal_mode = bool(payload.get("goal_mode"))
|
||||
# 用户显式发送非目标模式消息时,若工作区仍有上一轮残留的活动目标,先清理,
|
||||
# 避免新对话错误继承旧目标状态。
|
||||
if not goal_mode:
|
||||
try:
|
||||
_terminal, workspace = get_user_resources(username, workspace_id)
|
||||
if workspace:
|
||||
gsm = GoalStateManager(workspace.data_dir)
|
||||
if gsm.is_active():
|
||||
gsm.mark_stopped("new_conversation")
|
||||
debug_log(f"[Goal] 新任务未开启目标模式,清理工作区残留目标状态")
|
||||
except Exception as exc:
|
||||
debug_log(f"[Goal] 新任务清理残留目标状态失败: {exc}")
|
||||
skill_context_messages: List[Dict[str, str]] = []
|
||||
raw_skill_refs = payload.get("skill_refs")
|
||||
if raw_skill_refs:
|
||||
@ -1133,9 +1146,21 @@ def get_task_api(task_id: str):
|
||||
@api_login_required
|
||||
def cancel_task_api(task_id: str):
|
||||
username = get_current_username()
|
||||
ok = task_manager.cancel_task(username, task_id)
|
||||
if not ok:
|
||||
rec = task_manager.get_task(username, task_id)
|
||||
if not rec:
|
||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
||||
ok = task_manager.cancel_task(username, task_id)
|
||||
# 用户取消任务时,一并停止该工作区的目标模式,避免后续新对话继承旧目标。
|
||||
try:
|
||||
if ok and rec.workspace_id:
|
||||
_, workspace = get_user_resources(username, rec.workspace_id)
|
||||
if workspace:
|
||||
gsm = GoalStateManager(workspace.data_dir)
|
||||
if gsm.is_active():
|
||||
gsm.mark_stopped(REASON_USER_CANCEL)
|
||||
debug_log(f"[Goal] 用户取消任务 {task_id},同步停止工作区目标模式")
|
||||
except Exception as exc:
|
||||
debug_log(f"[Goal] 取消任务时停止目标模式失败: {exc}")
|
||||
return jsonify({"success": True})
|
||||
|
||||
|
||||
|
||||
@ -19,3 +19,45 @@ export const traceLog = (...args) => {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
// 目标模式调试日志:上报到后端 goal_mode_debug.log,便于复现状态继承问题。
|
||||
// 默认关闭,避免日常刷屏;需要时可在浏览器控制台执行 localStorage.setItem('goalModeDebug','1') 后刷新。
|
||||
function isGoalModeDebugEnabled() {
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicit = (window as any).__GOAL_MODE_DEBUG__;
|
||||
if (explicit === true || explicit === '1') return true;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
const localFlag = window.localStorage?.getItem('goalModeDebug');
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let goalModeDebugSeq = 0;
|
||||
export function goalModeDebugLog(event: string, payload: Record<string, any> = {}) {
|
||||
if (!isGoalModeDebugEnabled()) return;
|
||||
try {
|
||||
goalModeDebugSeq += 1;
|
||||
const entry = {
|
||||
event,
|
||||
seq: goalModeDebugSeq,
|
||||
client_ts: new Date().toISOString(),
|
||||
...payload
|
||||
};
|
||||
// 同时输出到浏览器控制台,方便开发时查看
|
||||
console.log('[GOAL_MODE_DEBUG]', event, entry);
|
||||
// 上报后端持久化
|
||||
if (typeof fetch !== 'undefined') {
|
||||
void fetch('/api/client_debug_log', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(entry)
|
||||
}).catch(() => {});
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore logging errors
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user