myagent-ai 1.26.6 → 1.26.7

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.
@@ -206,7 +206,7 @@ GUI桌面 (仅Windows/macOS):
206
206
  self._execution_events = []
207
207
  self._exec_event_counter = 0
208
208
 
209
- async def process(self, context: AgentContext) -> AgentContext:
209
+ async def process(self, context: AgentContext, stream_callback=None) -> AgentContext:
210
210
  """
211
211
  主处理循环。
212
212
 
@@ -250,6 +250,7 @@ GUI桌面 (仅Windows/macOS):
250
250
  agent_name=_injected_name or self.name,
251
251
  agent_description=_injected_desc or self.description,
252
252
  agent_override_prompt=_override_prompt,
253
+ stream_callback=stream_callback,
253
254
  agent_path=getattr(self, '_agent_override_path', None),
254
255
  )
255
256
  finally:
package/main.py CHANGED
@@ -488,6 +488,7 @@ class MyAgentApp:
488
488
  self,
489
489
  user_message: str,
490
490
  session_id: str = "",
491
+ stream_callback=None,
491
492
  ) -> str:
492
493
  """
493
494
  处理用户消息并返回回复。
@@ -495,6 +496,7 @@ class MyAgentApp:
495
496
  Args:
496
497
  user_message: 用户消息
497
498
  session_id: 会话 ID
499
+ stream_callback: SSE 流式回调(群聊等场景需要,用于推送 v2_file 等事件)
498
500
 
499
501
  Returns:
500
502
  助手回复文本
@@ -511,7 +513,7 @@ class MyAgentApp:
511
513
  )
512
514
 
513
515
  try:
514
- result_context = await self.main_agent.process(context)
516
+ result_context = await self.main_agent.process(context, stream_callback=stream_callback)
515
517
  response = result_context.working_memory.get(
516
518
  "final_response", "⚠️ 未能生成回复"
517
519
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.26.6",
3
+ "version": "1.26.7",
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
@@ -5496,22 +5496,22 @@ window.addEventListener('beforeunload', function() {{
5496
5496
 
5497
5497
  async def _try_model_chain(self, model_chain: list[dict], message: str, session_id: str,
5498
5498
  agent_path: str = None, agent_system_prompt: str = None,
5499
- chat_mode: str = "") -> str:
5499
+ chat_mode: str = "", stream_callback=None) -> str:
5500
5500
  """依次尝试模型链中的模型,直到成功或全部失败
5501
-
5501
+
5502
5502
  使用 asyncio.Lock 保护共享的 self.core.llm,防止并发请求互相干扰。
5503
5503
  """
5504
5504
  if not model_chain:
5505
- return await self.core.process_message(message, session_id)
5505
+ return await self.core.process_message(message, session_id, stream_callback=stream_callback)
5506
5506
 
5507
5507
  async with self._model_chain_lock:
5508
5508
  return await self._try_model_chain_inner(model_chain, message, session_id,
5509
5509
  agent_path=agent_path, agent_system_prompt=agent_system_prompt,
5510
- chat_mode=chat_mode)
5510
+ chat_mode=chat_mode, stream_callback=stream_callback)
5511
5511
 
5512
5512
  async def _try_model_chain_inner(self, model_chain: list[dict], message: str, session_id: str,
5513
5513
  agent_path: str = None, agent_system_prompt: str = None,
5514
- chat_mode: str = "") -> str:
5514
+ chat_mode: str = "", stream_callback=None) -> str:
5515
5515
  """_try_model_chain 的实际执行体(已在 _model_chain_lock 保护下)"""
5516
5516
  llm = self.core.llm
5517
5517
  last_error = ""
@@ -5598,7 +5598,7 @@ window.addEventListener('beforeunload', function() {{
5598
5598
  self.core.main_agent.context_builder.agent_knowledge_dir = str(agent_kb_dir)
5599
5599
 
5600
5600
  try:
5601
- response = await self.core.process_message(message, session_id)
5601
+ response = await self.core.process_message(message, session_id, stream_callback=stream_callback)
5602
5602
  finally:
5603
5603
  if self.core.main_agent:
5604
5604
  self.core.main_agent._agent_override_prompt = None
@@ -8324,14 +8324,34 @@ window.addEventListener('beforeunload', function() {{
8324
8324
 
8325
8325
  agent_content = content
8326
8326
 
8327
+ # [v1.26.6] 创建群聊 stream_callback,让 file_send 等工具的 v2_file 事件
8328
+ # 能推送到前端。群聊中 v2_file 需要携带 agent_path 标识发送者。
8329
+ _group_sent_files = []
8330
+ async def _group_stream_callback(event):
8331
+ evt_type = event.get("type", "")
8332
+ if evt_type == "v2_file" and event.get("data"):
8333
+ file_data = event["data"]
8334
+ # 转发为群聊格式的 v2_file 事件(附带 agent_path)
8335
+ await safe_write({
8336
+ "type": "v2_file",
8337
+ "data": file_data,
8338
+ "agent_path": agent_path,
8339
+ "agent_name": display_name,
8340
+ "agent_emoji": avatar,
8341
+ })
8342
+ # 追加到 sent_files 用于持久化
8343
+ if isinstance(file_data, dict) and file_data.get("file_id"):
8344
+ _group_sent_files.append(file_data)
8345
+
8327
8346
  # 调用 LLM 获取回复
8328
8347
  if model_chain and self.core.llm:
8329
8348
  response_text = await self._try_model_chain_inner(
8330
8349
  model_chain, agent_content, session_id,
8331
8350
  agent_path=agent_path, agent_system_prompt=agent_system_prompt,
8351
+ stream_callback=_group_stream_callback,
8332
8352
  )
8333
8353
  else:
8334
- response_text = await self.core.process_message(agent_content, session_id)
8354
+ response_text = await self.core.process_message(agent_content, session_id, stream_callback=_group_stream_callback)
8335
8355
 
8336
8356
  # 流式输出该 agent 的回复文本(逐块发送)
8337
8357
  await self._stream_text_chunked(response_text, safe_write, chunk_size=6, delay=0.01)
@@ -469,6 +469,16 @@ function startPrivateChatFromGroup(agentPath) {
469
469
  // ── Render Group Messages ──
470
470
  // ══════════════════════════════════════════════════════
471
471
 
472
+ // [v1.26.6] formatFileSize polyfill(chat_main.js 中已有全局定义,此处作兜底)
473
+ if (typeof formatFileSize !== 'function') {
474
+ function formatFileSize(bytes) {
475
+ if (!bytes || bytes < 0) return '';
476
+ if (bytes < 1024) return bytes + ' B';
477
+ if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
478
+ return (bytes / 1048576).toFixed(1) + ' MB';
479
+ }
480
+ }
481
+
472
482
  function renderGroupMessages() {
473
483
  try {
474
484
  _renderGroupMessagesInner();
@@ -516,6 +526,33 @@ function _renderGroupMessagesInner() {
516
526
  var _clickAvatar = agentPath ? ' onclick="startPrivateChatFromGroup(\'' + escapeHtml(agentPath) + '\')" style="background:' + agentColor + ';color:#fff;cursor:pointer" title="点击私聊 ' + escapeHtml(agentName) + '"' : ' style="background:' + agentColor + ';color:#fff"';
517
527
  // [v1.23.82] 流式输出时显示闪烁光标
518
528
  var _streamingCursor = msg._streaming ? '<span class="streaming-cursor" style="display:inline-block;width:2px;height:1em;background:var(--accent);margin-left:2px;animation:blink 1s step-end infinite;vertical-align:text-bottom"></span>' : '';
529
+ // [v1.26.6] 文件附件(群聊 file_send)
530
+ var _filesHtml = '';
531
+ if (msg._files && msg._files.length > 0) {
532
+ for (var _fi2 = 0; _fi2 < msg._files.length; _fi2++) {
533
+ var _ff = msg._files[_fi2];
534
+ var _fId = _ff.id || _ff.file_id || '';
535
+ var _fName = _ff.name || '文件';
536
+ var _fSize = _ff.size ? formatFileSize(_ff.size) : '';
537
+ var _fIcon = _ff.type && _ff.type.startsWith('image/') ? '🖼' : (_ff.type && _ff.type.startsWith('audio/') ? '🎵' : (_ff.type && _ff.type.startsWith('video/') ? '🎬' : '📄'));
538
+ var _fUrl = '/api/file/' + _fId + '?name=' + encodeURIComponent(_fName);
539
+ // 图片类型显示缩略图
540
+ if (_ff.type && _ff.type.startsWith('image/')) {
541
+ _filesHtml += '<div class="group-msg-file-image" style="margin-top:6px">'
542
+ + '<img src="' + _fUrl + '" style="max-width:300px;max-height:200px;border-radius:8px;cursor:pointer" onclick="window.open(this.src)" onerror="this.style.display=\'none\'">'
543
+ + '</div>';
544
+ } else {
545
+ _filesHtml += '<div class="group-msg-file-item" style="margin-top:6px;padding:8px 12px;background:var(--bg-tertiary);border-radius:8px;display:flex;align-items:center;gap:8px;cursor:pointer" onclick="window.open(\'' + _fUrl + '\')">'
546
+ + '<span style="font-size:20px">' + _fIcon + '</span>'
547
+ + '<span style="flex:1;min-width:0"><span style="display:block;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escapeHtml(_fName) + '</span>'
548
+ + (_fSize ? '<span style="display:block;font-size:11px;color:var(--text-secondary)">' + _fSize + '</span>' : '')
549
+ + '</span>'
550
+ + '<a href="' + _fUrl + '" download="' + escapeHtml(_fName) + '" style="color:var(--text-secondary);text-decoration:none;font-size:16px" onclick="event.stopPropagation()" title="下载">⬇</a>'
551
+ + '</div>';
552
+ }
553
+ }
554
+ }
555
+
519
556
  html += '<div class="group-msg-row">'
520
557
  + '<div class="group-msg-avatar"' + _clickAvatar + '>' + agentEmoji + '</div>'
521
558
  + '<div>'
@@ -523,6 +560,7 @@ function _renderGroupMessagesInner() {
523
560
  + (agentRole ? ' <span class="role-badge">' + escapeHtml(agentRole) + '</span>' : '')
524
561
  + '</div>'
525
562
  + '<div class="group-msg-bubble" style="border-left-color:' + agentColor + '">' + renderMarkdown(msg.content || '') + _streamingCursor + '</div>'
563
+ + _filesHtml
526
564
  + (msg.time ? '<div class="group-msg-time">' + formatTime(msg.time) + '</div>' : '')
527
565
  + '</div></div>';
528
566
  } else if (msg.responses) {
@@ -1114,6 +1152,31 @@ async function sendGroupChat() {
1114
1152
  _streamingContent = '';
1115
1153
  } else if (evt.type === 'system_message') {
1116
1154
  groupMessages.push({type: 'system', content: evt.content, time: new Date().toISOString()});
1155
+ } else if (evt.type === 'v2_file') {
1156
+ // [v1.26.6] 群聊文件卡片 — Agent 通过 file_send 工具发送文件
1157
+ if (evt.data) {
1158
+ var fd = evt.data;
1159
+ // 归一化: 后端用 file_id,渲染代码用 id
1160
+ if (fd.file_id && !fd.id) fd.id = fd.file_id;
1161
+ // 找到当前 agent 的消息(可能是正在流式输出的那条,或最后一条该 agent 的消息)
1162
+ var _targetPath = evt.agent_path || _streamingAgentPath;
1163
+ for (var fi = groupMessages.length - 1; fi >= 0; fi--) {
1164
+ var fm = groupMessages[fi];
1165
+ if (fm.agent === _targetPath || (fm._streaming && fm.agent === _streamingAgentPath)) {
1166
+ if (!fm._files) fm._files = [];
1167
+ // 去重
1168
+ var _fid2 = fd.id || fd.file_id;
1169
+ var _dup2 = _fid2 && fm._files.some(function(f) { return (f.id || f.file_id) === _fid2; });
1170
+ if (!_dup2) {
1171
+ fm._files.push(fd);
1172
+ }
1173
+ break;
1174
+ }
1175
+ }
1176
+ // 重新渲染以显示文件卡片
1177
+ _renderGroupMessagesInner();
1178
+ if (typeof scrollToBottom === 'function') scrollToBottom(true);
1179
+ }
1117
1180
  } else if (evt.type === 'done') {
1118
1181
  // 全部完成
1119
1182
  } else if (evt.type === 'error') {