myagent-ai 1.23.18 → 1.23.19
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/core/tool_dispatcher.py +11 -0
- package/package.json +1 -1
- package/web/api_server.py +18 -4
- package/web/ui/chat/chat_main.js +21 -4
- package/web/ui/chat/flow_engine.js +67 -1
package/core/tool_dispatcher.py
CHANGED
|
@@ -361,6 +361,17 @@ class ToolDispatcher:
|
|
|
361
361
|
embed_url = media_url
|
|
362
362
|
embed_title = embed_title or ("在线音乐" if media_type == "audio" else "在线视频")
|
|
363
363
|
|
|
364
|
+
# [v1.23.19] 构建媒体元数据,持久化到 sent_files 以支持历史消息恢复
|
|
365
|
+
_media_meta = {
|
|
366
|
+
"_type": "media", # 标记为媒体类型(区别于文件)
|
|
367
|
+
"media_type": media_type,
|
|
368
|
+
"embed_url": embed_url or "",
|
|
369
|
+
"title": embed_title,
|
|
370
|
+
"original_url": media_url or fallback_link or "",
|
|
371
|
+
}
|
|
372
|
+
if sent_files is not None:
|
|
373
|
+
sent_files.append(_media_meta)
|
|
374
|
+
|
|
364
375
|
if embed_url and stream_callback:
|
|
365
376
|
await self._emit_sse("v2_media", {
|
|
366
377
|
"media_type": media_type,
|
package/package.json
CHANGED
package/web/api_server.py
CHANGED
|
@@ -4252,8 +4252,15 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4252
4252
|
meta = e.metadata or {}
|
|
4253
4253
|
if meta.get("images"):
|
|
4254
4254
|
msg["images"] = meta["images"]
|
|
4255
|
-
|
|
4256
|
-
|
|
4255
|
+
# [v1.23.19] 分离 _files 和 _media(前端用 msg._files 和 msg._media)
|
|
4256
|
+
_raw_files = meta.get("files") or []
|
|
4257
|
+
if _raw_files:
|
|
4258
|
+
_file_items = [f for f in _raw_files if f.get("_type") != "media"]
|
|
4259
|
+
_media_items = [f for f in _raw_files if f.get("_type") == "media"]
|
|
4260
|
+
if _file_items:
|
|
4261
|
+
msg["_files"] = _file_items
|
|
4262
|
+
if _media_items:
|
|
4263
|
+
msg["_media"] = _media_items
|
|
4257
4264
|
result.append(msg)
|
|
4258
4265
|
return web.json_response(result)
|
|
4259
4266
|
|
|
@@ -4306,8 +4313,15 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4306
4313
|
meta = e.metadata or {}
|
|
4307
4314
|
if meta.get("images"):
|
|
4308
4315
|
msg["images"] = meta["images"]
|
|
4309
|
-
|
|
4310
|
-
|
|
4316
|
+
# [v1.23.19] 分离 _files 和 _media(前端用 msg._files 和 msg._media)
|
|
4317
|
+
_raw_files = meta.get("files") or []
|
|
4318
|
+
if _raw_files:
|
|
4319
|
+
_file_items = [f for f in _raw_files if f.get("_type") != "media"]
|
|
4320
|
+
_media_items = [f for f in _raw_files if f.get("_type") == "media"]
|
|
4321
|
+
if _file_items:
|
|
4322
|
+
msg["_files"] = _file_items
|
|
4323
|
+
if _media_items:
|
|
4324
|
+
msg["_media"] = _media_items
|
|
4311
4325
|
result.append(msg)
|
|
4312
4326
|
return web.json_response(result)
|
|
4313
4327
|
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -2246,10 +2246,15 @@ async function selectSession(id) {
|
|
|
2246
2246
|
if (m.images && m.images.length > 0) {
|
|
2247
2247
|
mapped.images = m.images;
|
|
2248
2248
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
mapped.
|
|
2249
|
+
// [v1.23.19] 兼容旧格式(m.files)和新格式(m._files / m._media)
|
|
2250
|
+
var _rawFiles = m._files || m.files || [];
|
|
2251
|
+
if (_rawFiles.length > 0) {
|
|
2252
|
+
mapped.files = _rawFiles;
|
|
2253
|
+
mapped._files = _rawFiles;
|
|
2254
|
+
}
|
|
2255
|
+
// [v1.23.19] 在线媒体嵌入
|
|
2256
|
+
if (m._media && m._media.length > 0) {
|
|
2257
|
+
mapped._media = m._media;
|
|
2253
2258
|
}
|
|
2254
2259
|
return mapped;
|
|
2255
2260
|
});
|
|
@@ -2625,6 +2630,8 @@ function groupHistoryMessages(messages) {
|
|
|
2625
2630
|
var firstMsg = msg;
|
|
2626
2631
|
// [v1.19.3] 收集组内所有消息的文件(agent 通过 file_send 发送的文件存在 metadata.files 中)
|
|
2627
2632
|
var allAgentFiles = [];
|
|
2633
|
+
// [v1.23.19] 收集组内所有消息的在线媒体嵌入
|
|
2634
|
+
var allAgentMedia = [];
|
|
2628
2635
|
|
|
2629
2636
|
while (i < messages.length && messages[i].role !== 'user') {
|
|
2630
2637
|
var m = messages[i];
|
|
@@ -2643,6 +2650,12 @@ function groupHistoryMessages(messages) {
|
|
|
2643
2650
|
allAgentFiles.push(m._files[_fi]);
|
|
2644
2651
|
}
|
|
2645
2652
|
}
|
|
2653
|
+
// [v1.23.19] 收集后端持久化的 _media 数据
|
|
2654
|
+
if (m._media && Array.isArray(m._media) && m._media.length > 0) {
|
|
2655
|
+
for (var _mi = 0; _mi < m._media.length; _mi++) {
|
|
2656
|
+
allAgentMedia.push(m._media[_mi]);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2646
2659
|
|
|
2647
2660
|
if (mkey === 'reasoning') {
|
|
2648
2661
|
reasoningText = reasoningText ? (reasoningText + '\n\n' + (m.content || '')) : (m.content || '');
|
|
@@ -2677,6 +2690,10 @@ function groupHistoryMessages(messages) {
|
|
|
2677
2690
|
if (allAgentFiles.length > 0) entry._files = allAgentFiles;
|
|
2678
2691
|
// [v1.20.3] 从 playaudio/playvideo 工具调用中重建 _media 数据(历史回放支持)
|
|
2679
2692
|
var mediaEmbeds = [];
|
|
2693
|
+
// [v1.23.19] 先收集后端持久化的 _media 数据
|
|
2694
|
+
for (var _pmi = 0; _pmi < allAgentMedia.length; _pmi++) {
|
|
2695
|
+
mediaEmbeds.push(allAgentMedia[_pmi]);
|
|
2696
|
+
}
|
|
2680
2697
|
for (var ei = 0; ei < parts.length; ei++) {
|
|
2681
2698
|
var ep = parts[ei];
|
|
2682
2699
|
if (ep.type === 'exec' && ep.data.tool_name && (ep.data.tool_name === 'playaudio' || ep.data.tool_name === 'playvideo')) {
|
|
@@ -628,7 +628,31 @@ function updateStreamingMessage(msgIdx) {
|
|
|
628
628
|
var _isImg = _f.type && _f.type.indexOf('image/') === 0;
|
|
629
629
|
var _isAud = _f.type && _f.type.indexOf('audio/') === 0;
|
|
630
630
|
var _isVid = _f.type && _f.type.indexOf('video/') === 0;
|
|
631
|
-
|
|
631
|
+
// [v1.23.19] 图片/音频/视频也直接渲染(不再跳过等 renderMessages)
|
|
632
|
+
if (_isImg && _fId) {
|
|
633
|
+
var _imgDiv = document.createElement('div');
|
|
634
|
+
_imgDiv.className = 'msg-image-wrapper agent-image';
|
|
635
|
+
_imgDiv.innerHTML = '<img src="/api/file/' + _fId + '" class="msg-image" loading="lazy" alt="' + escapeHtml(_f.name || 'image') + '" onerror="this.onerror=null;this.style.background=\'var(--bg3)\';this.style.minHeight=\'60px\';this.alt=\'[图片加载失败]\'" onclick="openFileViewer(\'' + _fId + '\', this.src, \'' + escapeHtml(_f.name) + '\')" />';
|
|
636
|
+
fileContainer.appendChild(_imgDiv);
|
|
637
|
+
renderedIds.push(_fId);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
if (_isAud && _fId) {
|
|
641
|
+
var _audDiv = document.createElement('div');
|
|
642
|
+
_audDiv.className = 'msg-media-player';
|
|
643
|
+
_audDiv.innerHTML = '<audio controls src="/api/file/' + _fId + '" style="width:100%;max-width:480px" preload="metadata" onplay="if(typeof muteTTS===\'function\')muteTTS()" onended="if(typeof unmuteTTS===\'function\')unmuteTTS()"></audio><div class="msg-media-title">' + escapeHtml(_f.name || '音频') + '</div>';
|
|
644
|
+
fileContainer.appendChild(_audDiv);
|
|
645
|
+
renderedIds.push(_fId);
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (_isVid && _fId) {
|
|
649
|
+
var _vidDiv = document.createElement('div');
|
|
650
|
+
_vidDiv.className = 'msg-media-player';
|
|
651
|
+
_vidDiv.innerHTML = '<video controls src="/api/file/' + _fId + '" style="width:100%;max-width:640px;border-radius:8px" preload="metadata" onplay="if(typeof muteTTS===\'function\')muteTTS()" onended="if(typeof unmuteTTS===\'function\')unmuteTTS()"></video><div class="msg-media-title">' + escapeHtml(_f.name || '视频') + '</div>';
|
|
652
|
+
fileContainer.appendChild(_vidDiv);
|
|
653
|
+
renderedIds.push(_fId);
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
632
656
|
var _icon = _getFileIcon(_f.name || _f.type || '');
|
|
633
657
|
var _sizeStr = _f.size ? formatFileSize(_f.size) : '';
|
|
634
658
|
var _fDiv = document.createElement('div');
|
|
@@ -647,6 +671,48 @@ function updateStreamingMessage(msgIdx) {
|
|
|
647
671
|
fileContainer._renderedFileIds = renderedIds;
|
|
648
672
|
}
|
|
649
673
|
|
|
674
|
+
// ── [v1.23.19] 增量渲染在线媒体嵌入播放器(v2_media 事件) ──
|
|
675
|
+
if (msg._media && msg._media.length > 0) {
|
|
676
|
+
var existingMedia = bubbleWrapper ? bubbleWrapper.querySelectorAll(':scope > .msg-attachments-media') : contentArea.querySelectorAll(':scope > .msg-attachments-media');
|
|
677
|
+
var mediaContainer = existingMedia.length > 0 ? existingMedia[0] : null;
|
|
678
|
+
if (!mediaContainer) {
|
|
679
|
+
mediaContainer = document.createElement('div');
|
|
680
|
+
mediaContainer.className = 'msg-attachments msg-attachments-media';
|
|
681
|
+
if (bubbleWrapper) {
|
|
682
|
+
bubbleWrapper.appendChild(mediaContainer);
|
|
683
|
+
} else {
|
|
684
|
+
contentArea.appendChild(mediaContainer);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
var renderedMediaUrls = mediaContainer._renderedMediaUrls || [];
|
|
688
|
+
for (var _mIdx = 0; _mIdx < msg._media.length; _mIdx++) {
|
|
689
|
+
var _m = msg._media[_mIdx];
|
|
690
|
+
var _mUrl = _m.embed_url || _m.original_url || '';
|
|
691
|
+
if (!_mUrl || renderedMediaUrls.indexOf(_mUrl) >= 0) continue;
|
|
692
|
+
var _isAud2 = _m.media_type === 'audio';
|
|
693
|
+
var _mTitle = _m.title || (_isAud2 ? '在线音乐' : '在线视频');
|
|
694
|
+
var _mOrig = _m.original_url || _mUrl;
|
|
695
|
+
var _mDiv = document.createElement('div');
|
|
696
|
+
_mDiv.className = 'msg-media-embed' + (_isAud2 ? ' msg-media-audio' : ' msg-media-video');
|
|
697
|
+
if (!_m.embed_url && _mOrig) {
|
|
698
|
+
// 无嵌入 URL,渲染链接卡片
|
|
699
|
+
_mDiv.className += ' msg-media-link-card';
|
|
700
|
+
_mDiv.style.cssText = 'padding:10px 14px;border-radius:8px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);cursor:pointer';
|
|
701
|
+
_mDiv.setAttribute('onclick', "window.open('" + _mOrig.replace(/'/g, "\\'") + "','_blank')");
|
|
702
|
+
_mDiv.innerHTML = '<div class="msg-media-header"><span class="msg-media-icon">' + (_isAud2 ? '🎵' : '🎬') + '</span><span class="msg-media-label">' + escapeHtml(_mTitle) + '</span><span style="margin-left:auto;font-size:12px;opacity:0.6">点击在新窗口打开 ↗</span></div><div style="font-size:12px;opacity:0.5;margin-top:4px;word-break:break-all">' + escapeHtml(_mOrig) + '</div>';
|
|
703
|
+
} else if (_isAud2) {
|
|
704
|
+
// 音频嵌入播放器
|
|
705
|
+
_mDiv.innerHTML = '<div class="msg-media-header"><span class="msg-media-icon">🎵</span><span class="msg-media-label">' + escapeHtml(_mTitle) + '</span><a class="msg-media-link" href="' + escapeHtml(_mOrig) + '" target="_blank" rel="noopener" title="在新窗口打开">↗</a></div><iframe src="' + escapeHtml(_mUrl) + '" style="width:100%;max-width:480px;height:80px;border:none;border-radius:8px" loading="lazy" allow="autoplay" sandbox="allow-scripts allow-same-origin allow-presentation"></iframe>';
|
|
706
|
+
} else {
|
|
707
|
+
// 视频嵌入播放器
|
|
708
|
+
_mDiv.innerHTML = '<div class="msg-media-header"><span class="msg-media-icon">🎬</span><span class="msg-media-label">' + escapeHtml(_mTitle) + '</span><a class="msg-media-link" href="' + escapeHtml(_mOrig) + '" target="_blank" rel="noopener" title="在新窗口打开">↗</a></div><iframe src="' + escapeHtml(_mUrl) + '" style="width:100%;max-width:640px;height:360px;border:none;border-radius:8px" loading="lazy" allow="autoplay;encrypted-media;picture-in-picture" allowfullscreen></iframe>';
|
|
709
|
+
}
|
|
710
|
+
mediaContainer.appendChild(_mDiv);
|
|
711
|
+
renderedMediaUrls.push(_mUrl);
|
|
712
|
+
}
|
|
713
|
+
mediaContainer._renderedMediaUrls = renderedMediaUrls;
|
|
714
|
+
}
|
|
715
|
+
|
|
650
716
|
// Remove exec events panel if present (events are now inline in timeline)
|
|
651
717
|
const execPanel = contentArea.querySelector('.exec-events-panel');
|
|
652
718
|
if (execPanel) execPanel.remove();
|