myagent-ai 1.20.10 → 1.20.12
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 +53 -6
- package/package.json +1 -1
- package/web/ui/chat/chat_main.js +15 -2
package/agents/main_agent.py
CHANGED
|
@@ -544,6 +544,18 @@ class MainAgent(BaseAgent):
|
|
|
544
544
|
text_delta_callback=None,
|
|
545
545
|
) -> AgentContext:
|
|
546
546
|
"""V2 内部循环逻辑 — 结构化输出 + 工具调度 + SSE 事件推送"""
|
|
547
|
+
|
|
548
|
+
# [v1.20.11] 安全发送 SSE 事件 — 兼容 sync/async 回调
|
|
549
|
+
async def _safe_sse(cb, event: dict):
|
|
550
|
+
"""安全调用 stream_callback,兼容同步和异步回调。"""
|
|
551
|
+
try:
|
|
552
|
+
if asyncio.iscoroutinefunction(cb):
|
|
553
|
+
await cb(event)
|
|
554
|
+
else:
|
|
555
|
+
cb(event)
|
|
556
|
+
except Exception as e:
|
|
557
|
+
logger.debug(f"SSE 事件发送失败: {e}")
|
|
558
|
+
|
|
547
559
|
max_iter = self.config.agent.max_iterations
|
|
548
560
|
current_task_plan = ""
|
|
549
561
|
all_tool_outputs = ""
|
|
@@ -1669,6 +1681,7 @@ class MainAgent(BaseAgent):
|
|
|
1669
1681
|
_media_type = "audio" if tool_name == "playaudio" else "video"
|
|
1670
1682
|
_embed_url = None
|
|
1671
1683
|
_embed_title = params.get("title", "")
|
|
1684
|
+
_fallback_link = None # [v1.20.10] 无法嵌入时提供外部链接
|
|
1672
1685
|
|
|
1673
1686
|
if _media_url:
|
|
1674
1687
|
# 在线链接 — 提取嵌入式播放 URL
|
|
@@ -1679,6 +1692,21 @@ class MainAgent(BaseAgent):
|
|
|
1679
1692
|
if _yt_match:
|
|
1680
1693
|
_embed_url = f"https://www.youtube.com/embed/{_yt_match.group(1)}"
|
|
1681
1694
|
_embed_title = _embed_title or "YouTube 视频"
|
|
1695
|
+
# YouTube Music 播放列表: https://music.youtube.com/playlist?list=xxx
|
|
1696
|
+
elif 'music.youtube.com' in _url_lower:
|
|
1697
|
+
_ym_match = re.search(r'list=([\w-]+)', _media_url)
|
|
1698
|
+
if _ym_match:
|
|
1699
|
+
_embed_url = f"https://music.youtube.com/embed?list={_ym_match.group(1)}&layout=full"
|
|
1700
|
+
_embed_title = _embed_title or "YouTube Music"
|
|
1701
|
+
else:
|
|
1702
|
+
# 单曲: https://music.youtube.com/watch?v=xxx
|
|
1703
|
+
_ymv_match = re.search(r'watch\?v=([\w-]+)', _media_url)
|
|
1704
|
+
if _ymv_match:
|
|
1705
|
+
_embed_url = f"https://music.youtube.com/embed/{_ymv_match.group(1)}"
|
|
1706
|
+
_embed_title = _embed_title or "YouTube Music"
|
|
1707
|
+
else:
|
|
1708
|
+
_fallback_link = _media_url
|
|
1709
|
+
_embed_title = _embed_title or "YouTube Music"
|
|
1682
1710
|
# Bilibili: https://www.bilibili.com/video/BVxxx 或 b23.tv/xxx
|
|
1683
1711
|
elif 'bilibili.com' in _url_lower or 'b23.tv' in _url_lower:
|
|
1684
1712
|
_bv_match = re.search(r'bilibili\.com/video/(BV[\w]+)', _media_url)
|
|
@@ -1690,8 +1718,14 @@ class MainAgent(BaseAgent):
|
|
|
1690
1718
|
_embed_title = _embed_title or "B站视频"
|
|
1691
1719
|
# QQ音乐: https://y.qq.com/n/ryqq/songDetail/xxx
|
|
1692
1720
|
elif 'y.qq.com' in _url_lower:
|
|
1693
|
-
|
|
1694
|
-
|
|
1721
|
+
# QQ音乐支持 outchain player
|
|
1722
|
+
_qq_match = re.search(r'songDetail/(\w+)', _media_url)
|
|
1723
|
+
if _qq_match:
|
|
1724
|
+
_embed_url = f"https://y.qq.com/n/ryqq/songDetail/{_qq_match.group(1)}"
|
|
1725
|
+
_embed_title = _embed_title or "QQ音乐"
|
|
1726
|
+
else:
|
|
1727
|
+
_embed_url = _media_url
|
|
1728
|
+
_embed_title = _embed_title or "QQ音乐"
|
|
1695
1729
|
# 网易云音乐: https://music.163.com/song?id=xxx
|
|
1696
1730
|
elif 'music.163.com' in _url_lower:
|
|
1697
1731
|
_song_match = re.search(r'music\.163\.com.*[?&]id=(\d+)', _media_url)
|
|
@@ -1712,7 +1746,7 @@ class MainAgent(BaseAgent):
|
|
|
1712
1746
|
|
|
1713
1747
|
if _embed_url and stream_callback:
|
|
1714
1748
|
# 在线播放 — 发送 v2_media 事件让前端渲染嵌入播放器
|
|
1715
|
-
stream_callback
|
|
1749
|
+
await _safe_sse(stream_callback, {
|
|
1716
1750
|
"type": "v2_media",
|
|
1717
1751
|
"data": {
|
|
1718
1752
|
"media_type": _media_type,
|
|
@@ -1723,6 +1757,19 @@ class MainAgent(BaseAgent):
|
|
|
1723
1757
|
})
|
|
1724
1758
|
result = {"success": True, "output": f"已嵌入{_embed_title}播放器: {_media_url}"}
|
|
1725
1759
|
|
|
1760
|
+
elif _fallback_link and stream_callback:
|
|
1761
|
+
# [v1.20.10] 无法嵌入但提供外部链接 — 发送链接卡片
|
|
1762
|
+
await _safe_sse(stream_callback, {
|
|
1763
|
+
"type": "v2_media",
|
|
1764
|
+
"data": {
|
|
1765
|
+
"media_type": _media_type,
|
|
1766
|
+
"embed_url": "", # 空 embed_url 告诉前端渲染链接
|
|
1767
|
+
"title": _embed_title,
|
|
1768
|
+
"original_url": _fallback_link,
|
|
1769
|
+
}
|
|
1770
|
+
})
|
|
1771
|
+
result = {"success": True, "output": f"已提供{_embed_title}链接(不支持嵌入播放): {_fallback_link}"}
|
|
1772
|
+
|
|
1726
1773
|
elif _media_file:
|
|
1727
1774
|
# 本地文件 — 使用 file_send 发送文件,前端渲染内嵌播放器
|
|
1728
1775
|
from pathlib import Path as _P
|
|
@@ -1777,7 +1824,7 @@ class MainAgent(BaseAgent):
|
|
|
1777
1824
|
if _wc_url:
|
|
1778
1825
|
_wc_session.current_url = _wc_url
|
|
1779
1826
|
if stream_callback:
|
|
1780
|
-
stream_callback
|
|
1827
|
+
await _safe_sse(stream_callback, {
|
|
1781
1828
|
"type": "v2_web_control",
|
|
1782
1829
|
"data": {
|
|
1783
1830
|
"action": "open",
|
|
@@ -1795,7 +1842,7 @@ class MainAgent(BaseAgent):
|
|
|
1795
1842
|
elif _wc_action == "close":
|
|
1796
1843
|
_wc_mgr.close_session(_wc_session_id)
|
|
1797
1844
|
if stream_callback:
|
|
1798
|
-
stream_callback
|
|
1845
|
+
await _safe_sse(stream_callback, {
|
|
1799
1846
|
"type": "v2_web_control",
|
|
1800
1847
|
"data": {"action": "close", "session_id": _wc_session_id}
|
|
1801
1848
|
})
|
|
@@ -1808,7 +1855,7 @@ class MainAgent(BaseAgent):
|
|
|
1808
1855
|
else:
|
|
1809
1856
|
_wc_session.current_url = _wc_url
|
|
1810
1857
|
if stream_callback:
|
|
1811
|
-
stream_callback
|
|
1858
|
+
await _safe_sse(stream_callback, {
|
|
1812
1859
|
"type": "v2_web_control",
|
|
1813
1860
|
"data": {"action": "navigate", "session_id": _wc_session_id, "url": _wc_url}
|
|
1814
1861
|
})
|
package/package.json
CHANGED
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -2825,15 +2825,28 @@ function _renderMessagesInner() {
|
|
|
2825
2825
|
let embedUrl = m.embed_url || '';
|
|
2826
2826
|
const title = m.title || (isAudio ? '在线音乐' : '在线视频');
|
|
2827
2827
|
const origUrl = m.original_url || embedUrl;
|
|
2828
|
-
if (!embedUrl) continue;
|
|
2828
|
+
if (!embedUrl && !origUrl) continue;
|
|
2829
|
+
// [v1.20.10] 如果 embed_url 为空,渲染链接卡片而非 iframe
|
|
2830
|
+
if (!embedUrl) {
|
|
2831
|
+
const icon = isAudio ? '🎵' : '🎬';
|
|
2832
|
+
parts.push('<div class="msg-media-embed msg-media-link-card" style="padding:10px 14px;border-radius:8px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);cursor:pointer" onclick="window.open(\'' + escapeHtml(origUrl) + '\',\'_blank\')">' +
|
|
2833
|
+
'<div class="msg-media-header"><span class="msg-media-icon">' + icon + '</span><span class="msg-media-label">' + escapeHtml(title) + '</span>' +
|
|
2834
|
+
'<span style="margin-left:auto;font-size:12px;opacity:0.6">点击在新窗口打开 ↗</span></div>' +
|
|
2835
|
+
'<div style="font-size:12px;opacity:0.5;margin-top:4px;word-break:break-all">' + escapeHtml(origUrl) + '</div>' +
|
|
2836
|
+
'</div>');
|
|
2837
|
+
continue;
|
|
2838
|
+
}
|
|
2829
2839
|
// 前端 URL → 嵌入 URL 转换(历史回放时 embed_url 可能是原始 URL)
|
|
2830
|
-
if (embedUrl && !embedUrl.includes('/embed/') && !embedUrl.includes('/player')) {
|
|
2840
|
+
if (embedUrl && !embedUrl.includes('/embed/') && !embedUrl.includes('/player') && !embedUrl.includes('/outchain/')) {
|
|
2831
2841
|
const ytMatch = embedUrl.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
|
|
2832
2842
|
if (ytMatch) { embedUrl = 'https://www.youtube.com/embed/' + ytMatch[1]; }
|
|
2833
2843
|
const biliMatch = embedUrl.match(/bilibili\.com\/video\/(BV[\w]+)/);
|
|
2834
2844
|
if (biliMatch) { embedUrl = 'https://player.bilibili.com/player.html?bvid=' + biliMatch[1] + '&autoplay=0'; }
|
|
2835
2845
|
const neteaseMatch = embedUrl.match(/music\.163\.com.*[?&]id=(\d+)/);
|
|
2836
2846
|
if (neteaseMatch) { embedUrl = 'https://music.163.com/outchain/player?type=2&id=' + neteaseMatch[1] + '&auto=0&height=66'; }
|
|
2847
|
+
// [v1.20.10] YouTube Music 播放列表 → embed
|
|
2848
|
+
const ymMatch = embedUrl.match(/music\.youtube\.com.*list=([\w-]+)/);
|
|
2849
|
+
if (ymMatch) { embedUrl = 'https://music.youtube.com/embed?list=' + ymMatch[1] + '&layout=full'; }
|
|
2837
2850
|
}
|
|
2838
2851
|
if (isAudio) {
|
|
2839
2852
|
parts.push('<div class="msg-media-embed msg-media-audio">' +
|