myagent-ai 1.47.21 → 1.47.23
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 +75 -47
- package/package.json +1 -1
- package/web/ui/chat/chat_main.js +0 -8
- package/web/ui/chat/flow_engine.js +2 -27
package/agents/main_agent.py
CHANGED
|
@@ -499,27 +499,6 @@ class MainAgent(BaseAgent):
|
|
|
499
499
|
except Exception as e:
|
|
500
500
|
logger.debug(f"V2 SSE 事件发送失败 ({event_type}): {e}")
|
|
501
501
|
|
|
502
|
-
def _try_extract_partial_response(self, llm_raw: str) -> str:
|
|
503
|
-
"""[v1.47.21] 从不完整的 LLM 输出中提取纯文本回复。
|
|
504
|
-
|
|
505
|
-
完全依赖原生 tool_calling,不再解析 XML 格式。
|
|
506
|
-
仅做简单的 XML 标签清理(兜底,防止模型意外输出 XML)。
|
|
507
|
-
"""
|
|
508
|
-
if not llm_raw:
|
|
509
|
-
return ""
|
|
510
|
-
|
|
511
|
-
import re
|
|
512
|
-
# 去除所有 XML 标签
|
|
513
|
-
cleaned = re.sub(r"<[^>]+>", "", llm_raw).strip()
|
|
514
|
-
cleaned = re.sub(r"^(reasoning|assistant)\s*", "", cleaned, flags=re.IGNORECASE).strip()
|
|
515
|
-
# 跳过工具执行状态文本
|
|
516
|
-
if cleaned and len(cleaned) > 5 and not re.match(
|
|
517
|
-
r"^(执行工具|调用工具|Running tool|Calling tool)", cleaned, re.IGNORECASE
|
|
518
|
-
):
|
|
519
|
-
return cleaned
|
|
520
|
-
|
|
521
|
-
return ""
|
|
522
|
-
|
|
523
502
|
async def _merge_duplicate_memory(
|
|
524
503
|
self,
|
|
525
504
|
old_memory,
|
|
@@ -964,16 +943,77 @@ class MainAgent(BaseAgent):
|
|
|
964
943
|
_reasoning_parts.append(delta_text)
|
|
965
944
|
await self._emit_v2_event("v2_reasoning", {"content": delta_text}, stream_callback)
|
|
966
945
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
946
|
+
# 可重试的 LLM 调用(最多 5 次)
|
|
947
|
+
_max_llm_retries = 5
|
|
948
|
+
_llm_retry_count = 0
|
|
949
|
+
response = None
|
|
950
|
+
|
|
951
|
+
while _llm_retry_count < _max_llm_retries:
|
|
952
|
+
try:
|
|
953
|
+
if stream_response and self.llm:
|
|
954
|
+
response = await self._call_llm_stream(
|
|
955
|
+
messages,
|
|
956
|
+
tools=tools if tools else None,
|
|
957
|
+
text_delta_callback=text_delta_callback,
|
|
958
|
+
reasoning_delta_callback=_reasoning_delta_cb,
|
|
959
|
+
stream_response=stream_response,
|
|
960
|
+
)
|
|
961
|
+
else:
|
|
962
|
+
response = await self._call_llm(messages, tools=tools if tools else None)
|
|
963
|
+
except Exception as _llm_exc:
|
|
964
|
+
# 异常类错误 → 判断是否可重试
|
|
965
|
+
_llm_exc_str = str(_llm_exc).lower()
|
|
966
|
+
_is_retryable = any(kw in _llm_exc_str for kw in (
|
|
967
|
+
"connection", "timeout", "timed out",
|
|
968
|
+
"429", "500", "502", "503", "504",
|
|
969
|
+
"rate_limit", "rate limit", "overloaded", "capacity",
|
|
970
|
+
"network", "eof",
|
|
971
|
+
))
|
|
972
|
+
_llm_retry_count += 1
|
|
973
|
+
if _is_retryable and _llm_retry_count < _max_llm_retries:
|
|
974
|
+
_delay = 2.0 * (2 ** (_llm_retry_count - 1)) # 2s, 4s, 8s, 16s
|
|
975
|
+
logger.warning(
|
|
976
|
+
f"[{task_id}] LLM 调用异常 (第 {_llm_retry_count}/{_max_llm_retries} 次),"
|
|
977
|
+
f"{_delay:.0f}s 后重试: {_llm_exc}"
|
|
978
|
+
)
|
|
979
|
+
await self._emit_v2_event("v2_reasoning", {
|
|
980
|
+
"content": f"⏳ 网络不稳定,正在重试 ({_llm_retry_count}/{_max_llm_retries})..."
|
|
981
|
+
}, stream_callback)
|
|
982
|
+
await asyncio.sleep(_delay)
|
|
983
|
+
continue
|
|
984
|
+
else:
|
|
985
|
+
# 不可重试 或 已达上限 → 包装为失败 response
|
|
986
|
+
logger.error(f"[{task_id}] LLM 调用异常 (已重试 {_llm_retry_count} 次): {_llm_exc}")
|
|
987
|
+
response = type("LLMResponse", (), {"success": False, "error": str(_llm_exc)})()
|
|
988
|
+
break
|
|
989
|
+
|
|
990
|
+
# 没有异常 → 检查 response.success
|
|
991
|
+
if response.success:
|
|
992
|
+
break # 成功 → 跳出重试循环
|
|
993
|
+
|
|
994
|
+
# response.success == False 但没抛异常 → 判断错误是否可重试
|
|
995
|
+
_llm_error = (response.error or "").lower()
|
|
996
|
+
_is_retryable = any(kw in _llm_error for kw in (
|
|
997
|
+
"connection", "timeout", "timed out",
|
|
998
|
+
"429", "500", "502", "503", "504",
|
|
999
|
+
"rate_limit", "rate limit", "overloaded", "capacity",
|
|
1000
|
+
"network", "eof",
|
|
1001
|
+
))
|
|
1002
|
+
_llm_retry_count += 1
|
|
1003
|
+
if _is_retryable and _llm_retry_count < _max_llm_retries:
|
|
1004
|
+
_delay = 2.0 * (2 ** (_llm_retry_count - 1))
|
|
1005
|
+
logger.warning(
|
|
1006
|
+
f"[{task_id}] LLM 返回失败 (第 {_llm_retry_count}/{_max_llm_retries} 次),"
|
|
1007
|
+
f"{_delay:.0f}s 后重试: {response.error}"
|
|
1008
|
+
)
|
|
1009
|
+
await self._emit_v2_event("v2_reasoning", {
|
|
1010
|
+
"content": f"⏳ 网络不稳定,正在重试 ({_llm_retry_count}/{_max_llm_retries})..."
|
|
1011
|
+
}, stream_callback)
|
|
1012
|
+
await asyncio.sleep(_delay)
|
|
1013
|
+
continue
|
|
1014
|
+
else:
|
|
1015
|
+
# 不可重试 或 已达上限 → 保持失败 response,退出循环
|
|
1016
|
+
break
|
|
977
1017
|
|
|
978
1018
|
# LLM 调用失败处理
|
|
979
1019
|
if not response.success:
|
|
@@ -1003,8 +1043,8 @@ class MainAgent(BaseAgent):
|
|
|
1003
1043
|
await self._emit_v2_event("v2_reasoning", {"content": _vision_skip_msg}, stream_callback)
|
|
1004
1044
|
break
|
|
1005
1045
|
|
|
1006
|
-
#
|
|
1007
|
-
error_msg = f"LLM
|
|
1046
|
+
# 其他错误(含已耗尽重试次数的连接错误)
|
|
1047
|
+
error_msg = f"LLM 调用失败 (已重试 {_llm_retry_count} 次): {_llm_error}"
|
|
1008
1048
|
context.working_memory["final_response"] = error_msg
|
|
1009
1049
|
await self._emit_v2_event("v2_reasoning", {"content": error_msg}, stream_callback)
|
|
1010
1050
|
break
|
|
@@ -1180,20 +1220,8 @@ class MainAgent(BaseAgent):
|
|
|
1180
1220
|
continue
|
|
1181
1221
|
|
|
1182
1222
|
else:
|
|
1183
|
-
#
|
|
1184
|
-
|
|
1185
|
-
raw_content = (response.content or "").strip()
|
|
1186
|
-
|
|
1187
|
-
# 如果模型意外输出了 XML 标签,清理掉
|
|
1188
|
-
import re as _re_clean
|
|
1189
|
-
if raw_content.startswith("<") and "</" in raw_content:
|
|
1190
|
-
# 清除 XML 标签,提取纯文本
|
|
1191
|
-
cleaned = _re_clean.sub(r'<[^>]+>', '', raw_content).strip()
|
|
1192
|
-
if cleaned:
|
|
1193
|
-
raw_content = cleaned
|
|
1194
|
-
logger.info(f"[{task_id}] 清理了 LLM 输出中的 XML 标签")
|
|
1195
|
-
|
|
1196
|
-
reply_text = raw_content
|
|
1223
|
+
# 没有原生工具调用 → 纯文本回复,完全依赖 tool_calling
|
|
1224
|
+
reply_text = (response.content or "").strip()
|
|
1197
1225
|
logger.info(f"[{task_id}] 无工具调用,任务完成 (reply长度={len(reply_text)})")
|
|
1198
1226
|
|
|
1199
1227
|
if not reply_text:
|
package/package.json
CHANGED
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -2999,11 +2999,7 @@ async function selectSession(id) {
|
|
|
2999
2999
|
return m && (m.role === 'user' || m.role === 'assistant' || m.role === 'tool');
|
|
3000
3000
|
}).map(function(m) {
|
|
3001
3001
|
var content = (m.content != null) ? String(m.content) : '';
|
|
3002
|
-
// [v1.47.21] 清理意外输出的 XML 标签(完全依赖 tool_calling)
|
|
3003
3002
|
var mkey = (m.key || '').toLowerCase();
|
|
3004
|
-
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
3005
|
-
content = content.replace(/<[^>]+>/g, ' ').replace(/\s{2,}/g, ' ').trim();
|
|
3006
|
-
}
|
|
3007
3003
|
var mapped = {
|
|
3008
3004
|
role: m.role || 'assistant',
|
|
3009
3005
|
content: content,
|
|
@@ -3108,10 +3104,6 @@ async function loadMoreMessages() {
|
|
|
3108
3104
|
}).map(function(m) {
|
|
3109
3105
|
var content = (m.content != null) ? String(m.content) : '';
|
|
3110
3106
|
var mkey = (m.key || '').toLowerCase();
|
|
3111
|
-
// [v1.47.21] 清理意外输出的 XML 标签
|
|
3112
|
-
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
3113
|
-
content = content.replace(/<[^>]+>/g, ' ').replace(/\s{2,}/g, ' ').trim();
|
|
3114
|
-
}
|
|
3115
3107
|
var mapped = {
|
|
3116
3108
|
role: m.role || 'assistant',
|
|
3117
3109
|
content: content,
|
|
@@ -398,10 +398,6 @@ async function pollChatHistory() {
|
|
|
398
398
|
}).map(function(m) {
|
|
399
399
|
var content = (m.content != null) ? String(m.content) : '';
|
|
400
400
|
var mkey = (m.key || '').toLowerCase();
|
|
401
|
-
// [v1.47.21] 清理意外输出的 XML 标签(完全依赖 tool_calling,不再解析 XML)
|
|
402
|
-
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
403
|
-
content = content.replace(/<[^>]+>/g, ' ').replace(/\s{2,}/g, ' ').trim();
|
|
404
|
-
}
|
|
405
401
|
var mapped = {
|
|
406
402
|
role: m.role || 'assistant',
|
|
407
403
|
content: content,
|
|
@@ -416,7 +412,7 @@ async function pollChatHistory() {
|
|
|
416
412
|
if (m._media && m._media.length > 0) mapped._media = m._media;
|
|
417
413
|
return mapped;
|
|
418
414
|
});
|
|
419
|
-
|
|
415
|
+
|
|
420
416
|
// 检测是否有新消息
|
|
421
417
|
var newCount = loaded.length;
|
|
422
418
|
if (newCount === _lastKnownMessageCount && !state.isGenerating) return; // 无变化且非生成中,跳过
|
|
@@ -472,10 +468,6 @@ async function forceRefreshHistory() {
|
|
|
472
468
|
}).map(function(m) {
|
|
473
469
|
var content = (m.content != null) ? String(m.content) : '';
|
|
474
470
|
var mkey = (m.key || '').toLowerCase();
|
|
475
|
-
// [v1.47.21] 清理意外输出的 XML 标签
|
|
476
|
-
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
477
|
-
content = content.replace(/<[^>]+>/g, ' ').replace(/\s{2,}/g, ' ').trim();
|
|
478
|
-
}
|
|
479
471
|
var mapped = {
|
|
480
472
|
role: m.role || 'assistant',
|
|
481
473
|
content: content,
|
|
@@ -1114,16 +1106,6 @@ function _showFinishNotification(text) {
|
|
|
1114
1106
|
setTimeout(function() { if (overlay.parentNode) overlay.remove(); }, 5000);
|
|
1115
1107
|
}
|
|
1116
1108
|
|
|
1117
|
-
/**
|
|
1118
|
-
* [v1.47.21] Strip XML tags from text — simple regex cleanup for accidental XML output.
|
|
1119
|
-
* No longer parses <output>/<reply>/<toolstocal> — fully relies on native tool_calling.
|
|
1120
|
-
*/
|
|
1121
|
-
function _stripXmlTags(xml) {
|
|
1122
|
-
if (!xml) return '';
|
|
1123
|
-
return xml.replace(/<[^>]+>/g, ' ').replace(/</g, '<').replace(/>/g, '>')
|
|
1124
|
-
.replace(/&/g, '&').replace(/\s{2,}/g, ' ').trim();
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
1109
|
// ══════════════════════════════════════════════════════
|
|
1128
1110
|
// ── V2 Content Assembler (V2 内容组装) ──
|
|
1129
1111
|
// ══════════════════════════════════════════════════════
|
|
@@ -1148,14 +1130,7 @@ function _assembleV2Content(msg, msgParts) {
|
|
|
1148
1130
|
if (msg._askUser && msg._askUser.trim()) {
|
|
1149
1131
|
return msg._askUser.trim();
|
|
1150
1132
|
}
|
|
1151
|
-
// Priority 4:
|
|
1152
|
-
if (msg._v2RawXml && msg._v2RawXml.trim()) {
|
|
1153
|
-
var strippedText = _stripXmlTags(msg._v2RawXml);
|
|
1154
|
-
if (strippedText && strippedText.trim()) {
|
|
1155
|
-
return strippedText.trim();
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
// Priority 5: raw content from message (server-stored response)
|
|
1133
|
+
// Priority 4: raw content from message (server-stored response)
|
|
1159
1134
|
if (msg.content && msg.content.trim() && msg.content !== '(无回复)') {
|
|
1160
1135
|
return msg.content.trim();
|
|
1161
1136
|
}
|