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.
- package/agents/main_agent.py +2 -1
- package/main.py +3 -1
- package/package.json +1 -1
- package/web/api_server.py +27 -7
- package/web/ui/chat/groupchat.js +63 -0
package/agents/main_agent.py
CHANGED
|
@@ -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
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)
|
package/web/ui/chat/groupchat.js
CHANGED
|
@@ -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') {
|