myagent-ai 1.15.48 → 1.15.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.15.48",
3
+ "version": "1.15.49",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
package/web/api_server.py CHANGED
@@ -279,7 +279,9 @@ class ApiServer:
279
279
  # ── 会话管理 ──
280
280
  r.add_get("/api/sessions", self.handle_list_sessions)
281
281
  r.add_get("/api/sessions/{sid}/messages", self.handle_get_messages)
282
+ r.add_get("/api/sessions/{sid}/raw", self.handle_get_raw_messages)
282
283
  r.add_get("/api/session/messages", self.handle_get_messages_query) # query param version (supports / in sid)
284
+ r.add_get("/api/session/raw", self.handle_get_raw_messages_query) # query param version for raw
283
285
  r.add_delete("/api/sessions/{sid}", self.handle_delete_session)
284
286
  r.add_delete("/api/session", self.handle_delete_session_query) # query param version
285
287
  r.add_delete("/api/sessions/{sid}/messages", self.handle_clear_session_messages)
@@ -2570,6 +2572,36 @@ class ApiServer:
2570
2572
  entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
2571
2573
  return web.json_response([{"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""} for e in entries])
2572
2574
 
2575
+ async def handle_get_raw_messages(self, request):
2576
+ """GET /api/sessions/{sid}/raw - 获取会话全部原始消息(含 llm_output 等)"""
2577
+ sid = request.match_info["sid"]
2578
+ if not self.core.memory:
2579
+ return web.json_response([])
2580
+ limit = min(int(request.query.get("limit", 5000)), 5000)
2581
+ offset = int(request.query.get("offset", 0))
2582
+ entries = self.core.memory.get_conversation_all(sid, limit=limit + offset)
2583
+ entries = entries[offset:]
2584
+ return web.json_response([{
2585
+ "role": e.role, "content": e.content, "time": e.created_at,
2586
+ "key": e.key or "", "id": e.id,
2587
+ } for e in entries])
2588
+
2589
+ async def handle_get_raw_messages_query(self, request):
2590
+ """GET /api/session/raw?sid=... - 通过 query 参数获取原始消息(支持 session_id 中包含 /)"""
2591
+ sid = request.query.get("sid", "")
2592
+ if not sid:
2593
+ return web.json_response([])
2594
+ if not self.core.memory:
2595
+ return web.json_response([])
2596
+ limit = min(int(request.query.get("limit", 5000)), 5000)
2597
+ offset = int(request.query.get("offset", 0))
2598
+ entries = self.core.memory.get_conversation_all(sid, limit=limit + offset)
2599
+ entries = entries[offset:]
2600
+ return web.json_response([{
2601
+ "role": e.role, "content": e.content, "time": e.created_at,
2602
+ "key": e.key or "", "id": e.id,
2603
+ } for e in entries])
2604
+
2573
2605
  async def handle_get_messages_query(self, request):
2574
2606
  """GET /api/session/messages?sid=... - 通过 query 参数获取会话消息(支持 session_id 中包含 /)"""
2575
2607
  sid = request.query.get("sid", "")
package/web/ui/index.html CHANGED
@@ -216,6 +216,7 @@ tr:hover{background:var(--surface2)}
216
216
  .modal{width:98%;padding:12px}
217
217
  .form-group{margin-bottom:10px}
218
218
  .form-group label{font-size:12px}
219
+ .raw-collapsed{max-height:0 !important;padding:0 10px !important;overflow:hidden}
219
220
  }
220
221
  </style>
221
222
  </head>
@@ -976,9 +977,101 @@ async function _loadSessionMessages(){
976
977
  html+='</div>';
977
978
  if(hasMore)html+=`<button class="btn btn-ghost mt-8" onclick="window._viewSessionOffset=${offset+100};_loadSessionMessages()">加载更多...</button>`;
978
979
  html+='<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="renderSessions()">返回</button>';
980
+ html+=`<button class="btn" style="background:var(--accent);color:#fff" onclick="viewSessionRaw('${escHtml(sid)}')">Raw 原始消息</button>`;
979
981
  html+=`<button class="btn btn-primary" onclick="enterSession('${escHtml(sid)}','${escHtml(sid.split('_web_')[0]||'default')}')">在聊天中查看完整记录</button></div>`;
980
982
  $('content').innerHTML=html;
981
983
  }
984
+ // ========== Raw 原始消息查看 ==========
985
+ async function viewSessionRaw(sid){
986
+ const msgs=await api(`/api/session/raw?sid=${encodeURIComponent(sid)}&limit=5000`);
987
+ if(!Array.isArray(msgs)){showToast('加载失败','danger');return}
988
+ // 按时间分组(同秒内合并)
989
+ const keyLabelMap={'llm_output':'LLM 输出','tool_call':'工具调用','tool_result':'工具结果','reasoning':'推理过程','conversation_insight':'会话洞察','':'对话','llm_request':'LLM 请求'};
990
+ let html=`<h3 style="margin-bottom:12px">Raw: ${escHtml(sid)} <span class="badge badge-blue">${msgs.length} 条</span></h3>`;
991
+ // 筛选按钮
992
+ html+=`<div style="margin-bottom:10px;display:flex;gap:6px;flex-wrap:wrap" id="rawFilterBar">`;
993
+ html+=`<button class="btn btn-sm" style="background:var(--accent);color:#fff" data-filter="all" onclick="rawFilter('all',this)">全部</button>`;
994
+ // 收集所有 key 类型
995
+ const keys=[...new Set(msgs.map(m=>m.key||''))];
996
+ for(const k of keys){
997
+ const label=keyLabelMap[k]||k||'对话';
998
+ const count=msgs.filter(m=>(m.key||'')===k).length;
999
+ html+=`<button class="btn btn-sm btn-ghost" data-filter="${escHtml(k)}" onclick="rawFilter('${escHtml(k)}',this)">${escHtml(label)} (${count})</button>`;
1000
+ }
1001
+ html+=`</div>`;
1002
+ // 时间索引导航
1003
+ html+=`<div style="margin-bottom:8px;font-size:12px;color:var(--text3)" id="rawTimeNav"></div>`;
1004
+ // 消息列表
1005
+ html+=`<div style="max-height:65vh;overflow-y:auto;font-family:monospace" id="rawMsgList">`;
1006
+ for(let i=0;i<msgs.length;i++){
1007
+ const m=msgs[i];
1008
+ const role=m.role||'?';
1009
+ const key=m.key||'';
1010
+ const time=(m.time||'').slice(0,19);
1011
+ const content=(m.content||'');
1012
+ const keyLabel=keyLabelMap[key]||key;
1013
+ // 颜色标识
1014
+ let borderColor='var(--border)';
1015
+ let bgColor='var(--surface)';
1016
+ if(key==='llm_output'){borderColor='#e6a817';bgColor='#1a1700'}
1017
+ else if(key==='tool_call'){borderColor='#3b82f6';bgColor='#001029'}
1018
+ else if(key==='tool_result'){borderColor='#22c55e';bgColor='#001a0d'}
1019
+ else if(key==='reasoning'){borderColor='#a855f7';bgColor='#0d0020'}
1020
+ else if(role==='user'){borderColor='var(--primary)';bgColor='var(--surface)'}
1021
+ else if(role==='assistant'){borderColor='#6b7280';bgColor='var(--surface2)'}
1022
+ const mid='raw_'+i;
1023
+ html+=`<div class="raw-item" data-key="${escHtml(key)}" data-time="${escHtml(time)}" style="margin:2px 0;border-left:3px solid ${borderColor};background:${bgColor};border-radius:0 4px 4px 0;overflow:hidden">`;
1024
+ html+=`<div style="padding:4px 10px;font-size:11px;color:var(--text3);display:flex;justify-content:space-between;align-items:center;cursor:pointer" onclick="document.getElementById('${mid}').classList.toggle('raw-collapsed')">`;
1025
+ html+=`<span><span style="color:var(--text);font-weight:600">${escHtml(role)}</span>`;
1026
+ if(keyLabel)html+=` <span class="badge badge-blue" style="font-size:10px">${escHtml(keyLabel)}</span>`;
1027
+ html+=`</span>`;
1028
+ html+=`<span>${escHtml(time)} <span style="margin-left:4px;opacity:0.5">${content.length}字符</span></span>`;
1029
+ html+=`</div>`;
1030
+ html+=`<div id="${mid}" class="raw-body" style="padding:4px 10px 8px;font-size:12px;white-space:pre-wrap;word-break:break-all;color:var(--text2);max-height:400px;overflow-y:auto;transition:max-height 0.2s">`;
1031
+ html+=escHtml(content.length>8000?content.slice(0,8000)+'\n... (共'+content.length+'字符)':content);
1032
+ if(content.length>8000)html+=`<button class="btn btn-sm btn-ghost" style="margin-top:4px;font-size:10px" onclick="event.stopPropagation();this.parentElement.textContent=this.dataset.full;this.remove()" data-full="${escHtml(content).replace(/"/g,'&quot;')}">展开全部 (${content.length}字符)</button>`;
1033
+ html+=`</div></div>`;
1034
+ }
1035
+ html+=`</div>`;
1036
+ html+=`<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="viewSession('${escHtml(sid)}')">返回查看</button>`;
1037
+ html+=`<button class="btn btn-ghost" onclick="renderSessions()">返回会话列表</button></div>`;
1038
+ $('content').innerHTML=html;
1039
+ // 生成时间索引
1040
+ _buildTimeNav(msgs);
1041
+ }
1042
+ function rawFilter(key,btn){
1043
+ const items=document.querySelectorAll('.raw-item');
1044
+ for(const item of items){
1045
+ if(key==='all'||item.dataset.key===key){item.style.display=''}
1046
+ else{item.style.display='none'}
1047
+ }
1048
+ // 更新按钮样式
1049
+ const bar=document.getElementById('rawFilterBar');
1050
+ if(bar){
1051
+ bar.querySelectorAll('.btn').forEach(b=>{b.className='btn btn-sm btn-ghost'});
1052
+ if(btn)btn.className='btn btn-sm';
1053
+ if(btn)btn.style.background='var(--accent)';
1054
+ if(btn)btn.style.color='#fff';
1055
+ }
1056
+ }
1057
+ function _buildTimeNav(msgs){
1058
+ // 按分钟分组生成时间跳转锚点
1059
+ const nav=document.getElementById('rawTimeNav');
1060
+ if(!nav||!msgs.length)return;
1061
+ const minutes={};
1062
+ for(let i=0;i<msgs.length;i++){
1063
+ const t=(msgs[i].time||'').slice(0,16); // YYYY-MM-DDTHH:MM
1064
+ if(!minutes[t])minutes[t]=[];
1065
+ minutes[t].push(i);
1066
+ }
1067
+ const times=Object.keys(minutes);
1068
+ if(times.length<=1)return;
1069
+ let navHtml='时间索引: ';
1070
+ for(const t of times){
1071
+ navHtml+=`<a href="javascript:void(0)" style="color:var(--accent);margin-right:8px;text-decoration:none" onclick="document.querySelectorAll('.raw-item')[${minutes[t][0]}].scrollIntoView({behavior:'smooth',block:'center'})">${escHtml(t.slice(11))}</a>`;
1072
+ }
1073
+ nav.innerHTML=navHtml;
1074
+ }
982
1075
  async function clearSession(sid){await api(`/api/sessions/${encodeURIComponent(sid)}`,{method:'DELETE'});renderSessions()}
983
1076
  // 切入会话: 打开聊天页面并自动加载指定会话
984
1077
  function enterSession(sid,agentName){