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_BACKEND_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "chunk_backend.log"
|
||||||
CHUNK_FRONTEND_LOG_FILE = Path(LOGS_DIR).expanduser().resolve() / "chunk_frontend.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"
|
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"
|
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)
|
_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]:
|
def get_thinking_state(terminal: WebTerminal) -> Dict[str, Any]:
|
||||||
"""获取(或初始化)思考调度状态。"""
|
"""获取(或初始化)思考调度状态。"""
|
||||||
state = getattr(terminal, "_thinking_state", None)
|
state = getattr(terminal, "_thinking_state", None)
|
||||||
@ -1399,6 +1408,19 @@ def voice_debug():
|
|||||||
return jsonify({'ok': True, 'file': filename})
|
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__":
|
if __name__ == "__main__":
|
||||||
args = parse_arguments()
|
args = parse_arguments()
|
||||||
run_server(
|
run_server(
|
||||||
|
|||||||
@ -20,6 +20,7 @@ from .state import stop_flags
|
|||||||
from .utils_common import debug_log, log_conn_diag
|
from .utils_common import debug_log, log_conn_diag
|
||||||
from utils.host_workspace_debug import write_host_workspace_debug
|
from utils.host_workspace_debug import write_host_workspace_debug
|
||||||
from config import DATA_DIR, WORKSPACE_SKILLS_DIRNAME
|
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)
|
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")
|
message_source = payload.get("message_source")
|
||||||
max_iterations = payload.get("max_iterations")
|
max_iterations = payload.get("max_iterations")
|
||||||
goal_mode = bool(payload.get("goal_mode"))
|
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]] = []
|
skill_context_messages: List[Dict[str, str]] = []
|
||||||
raw_skill_refs = payload.get("skill_refs")
|
raw_skill_refs = payload.get("skill_refs")
|
||||||
if raw_skill_refs:
|
if raw_skill_refs:
|
||||||
@ -1133,9 +1146,21 @@ def get_task_api(task_id: str):
|
|||||||
@api_login_required
|
@api_login_required
|
||||||
def cancel_task_api(task_id: str):
|
def cancel_task_api(task_id: str):
|
||||||
username = get_current_username()
|
username = get_current_username()
|
||||||
ok = task_manager.cancel_task(username, task_id)
|
rec = task_manager.get_task(username, task_id)
|
||||||
if not ok:
|
if not rec:
|
||||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
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})
|
return jsonify({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,3 +19,45 @@ export const traceLog = (...args) => {
|
|||||||
// ignore
|
// 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