fix: 修复会话详情解析 JSONL 格式
- 正确解析 OpenClaw JSONL 会话文件格式 - 提取 type=message 类型的记录 - 解析 content 数组中的 text 和 thinking 内容 - 限制返回最近 30 条消息
This commit is contained in:
106
api/sessions.py
106
api/sessions.py
@@ -20,42 +20,22 @@ def get_sessions():
|
||||
try:
|
||||
sessions = []
|
||||
|
||||
# 遍历会话目录
|
||||
for filepath in glob.glob(os.path.join(SESSIONS_DIR, '*.jsonl')):
|
||||
if filepath.endswith('.lock'):
|
||||
if '.reset.' in filepath or filepath.endswith('.lock'):
|
||||
continue
|
||||
|
||||
session_id = os.path.basename(filepath).replace('.jsonl', '')
|
||||
|
||||
# 获取文件信息
|
||||
stat = os.stat(filepath)
|
||||
updated_at = stat.st_mtime
|
||||
size = stat.st_size
|
||||
|
||||
# 读取最后几行获取会话信息
|
||||
messages_count = 0
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
lines = f.readlines()
|
||||
total_tokens = 0
|
||||
channel = 'unknown'
|
||||
|
||||
# 解析最后一条消息获取信息
|
||||
if lines:
|
||||
try:
|
||||
last_line = lines[-1].strip()
|
||||
if last_line:
|
||||
data = json.loads(last_line)
|
||||
if 'usage' in data:
|
||||
total_tokens = data['usage'].get('total_tokens', 0)
|
||||
except:
|
||||
pass
|
||||
|
||||
messages_count = len([l for l in lines if l.strip()])
|
||||
messages_count = len([l for l in f.readlines() if l.strip()])
|
||||
except:
|
||||
messages_count = 0
|
||||
total_tokens = 0
|
||||
pass
|
||||
|
||||
# 判断是否活跃(5分钟内更新)
|
||||
is_active = (datetime.now().timestamp() - updated_at) < 300
|
||||
|
||||
sessions.append({
|
||||
@@ -65,11 +45,9 @@ def get_sessions():
|
||||
'updatedTime': datetime.fromtimestamp(updated_at).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'size': size,
|
||||
'messagesCount': messages_count,
|
||||
'totalTokens': total_tokens,
|
||||
'status': 'active' if is_active else 'inactive'
|
||||
})
|
||||
|
||||
# 按更新时间排序
|
||||
sessions.sort(key=lambda x: x['updatedAt'], reverse=True)
|
||||
|
||||
return jsonify({
|
||||
@@ -80,10 +58,7 @@ def get_sessions():
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
})
|
||||
return jsonify({'success': False, 'error': str(e)})
|
||||
|
||||
@api.route('/sessions/<session_id>')
|
||||
def get_session_detail(session_id):
|
||||
@@ -92,10 +67,7 @@ def get_session_detail(session_id):
|
||||
filepath = os.path.join(SESSIONS_DIR, f'{session_id}.jsonl')
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '会话不存在'
|
||||
})
|
||||
return jsonify({'success': False, 'error': '会话不存在'})
|
||||
|
||||
messages = []
|
||||
|
||||
@@ -106,26 +78,36 @@ def get_session_detail(session_id):
|
||||
continue
|
||||
|
||||
try:
|
||||
data = json.loads(line)
|
||||
record = json.loads(line)
|
||||
|
||||
# 提取消息
|
||||
if 'messages' in data:
|
||||
for msg in data['messages']:
|
||||
# 只处理 message 类型的记录
|
||||
if record.get('type') == 'message':
|
||||
msg = record.get('message', {})
|
||||
role = msg.get('role', 'unknown')
|
||||
content_parts = msg.get('content', [])
|
||||
|
||||
# 提取文本内容
|
||||
text_content = ''
|
||||
for part in content_parts:
|
||||
if isinstance(part, dict):
|
||||
if part.get('type') == 'text':
|
||||
text_content += part.get('text', '')
|
||||
elif part.get('type') == 'thinking':
|
||||
text_content += '[思考中...] '
|
||||
elif isinstance(part, str):
|
||||
text_content += part
|
||||
|
||||
if text_content.strip():
|
||||
messages.append({
|
||||
'role': msg.get('role', 'unknown'),
|
||||
'content': msg.get('content', '')[:500], # 截断内容
|
||||
'timestamp': data.get('created', 0)
|
||||
'role': role,
|
||||
'content': text_content[:500],
|
||||
'timestamp': record.get('timestamp', '')
|
||||
})
|
||||
elif 'content' in data:
|
||||
messages.append({
|
||||
'role': data.get('role', 'unknown'),
|
||||
'content': data.get('content', '')[:500]
|
||||
})
|
||||
except:
|
||||
continue
|
||||
|
||||
# 只保留最近 20 条消息
|
||||
messages = messages[-20:]
|
||||
# 只保留最近 30 条消息
|
||||
messages = messages[-30:]
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -136,38 +118,20 @@ def get_session_detail(session_id):
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
})
|
||||
return jsonify({'success': False, 'error': str(e)})
|
||||
|
||||
@api.route('/sessions/<session_id>/kill', methods=['POST'])
|
||||
def kill_session(session_id):
|
||||
"""终止会话(删除会话文件)"""
|
||||
"""终止会话"""
|
||||
try:
|
||||
filepath = os.path.join(SESSIONS_DIR, f'{session_id}.jsonl')
|
||||
lockfile = filepath + '.lock'
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '会话不存在'
|
||||
})
|
||||
return jsonify({'success': False, 'error': '会话不存在'})
|
||||
|
||||
# 重命名为备份文件而不是直接删除
|
||||
backup_path = filepath + f'.reset.{datetime.now().isoformat()}'
|
||||
os.rename(filepath, backup_path)
|
||||
|
||||
# 删除锁文件
|
||||
if os.path.exists(lockfile):
|
||||
os.remove(lockfile)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '会话已终止'
|
||||
})
|
||||
return jsonify({'success': True, 'message': '会话已终止'})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
})
|
||||
return jsonify({'success': False, 'error': str(e)})
|
||||
|
||||
Reference in New Issue
Block a user