myagent-ai 1.10.4 → 1.10.5
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
CHANGED
|
@@ -420,7 +420,7 @@ class MainAgent(BaseAgent):
|
|
|
420
420
|
if all_tool_outputs:
|
|
421
421
|
messages.append(Message(
|
|
422
422
|
role="user",
|
|
423
|
-
content=f"[上一轮工具执行结果汇总]\n{truncate_str(all_tool_outputs,
|
|
423
|
+
content=f"[上一轮工具执行结果汇总]\n{truncate_str(all_tool_outputs, 30000)}"
|
|
424
424
|
))
|
|
425
425
|
all_tool_outputs = ""
|
|
426
426
|
|
|
@@ -700,20 +700,59 @@ class MainAgent(BaseAgent):
|
|
|
700
700
|
|
|
701
701
|
# 发送工具结果事件
|
|
702
702
|
# 提取实际输出:SkillResult 有 output/message/data,ExecResult 有 stdout/stderr
|
|
703
|
+
def _format_data_for_llm(data):
|
|
704
|
+
"""将结构化 data 格式化为 LLM 可读的文本"""
|
|
705
|
+
if data is None:
|
|
706
|
+
return ""
|
|
707
|
+
if isinstance(data, str):
|
|
708
|
+
return data
|
|
709
|
+
if isinstance(data, dict):
|
|
710
|
+
# 搜索结果列表格式 (web_search)
|
|
711
|
+
results = data.get("results")
|
|
712
|
+
if isinstance(results, list):
|
|
713
|
+
lines = []
|
|
714
|
+
for i, r in enumerate(results, 1):
|
|
715
|
+
title = r.get("title", "")
|
|
716
|
+
url = r.get("url", "")
|
|
717
|
+
snippet = r.get("snippet", "")
|
|
718
|
+
lines.append(f"{i}. {title}\n URL: {url}\n {snippet}")
|
|
719
|
+
return "\n".join(lines)
|
|
720
|
+
# 网页内容格式 (web_read)
|
|
721
|
+
if "url" in data and "content" in data:
|
|
722
|
+
title = data.get("title", "")
|
|
723
|
+
content = data.get("content", "")
|
|
724
|
+
lines = [f"标题: {title}", f"URL: {data['url']}", f"内容:\n{content}"]
|
|
725
|
+
return "\n".join(lines)
|
|
726
|
+
# 通用 dict: key-value 格式
|
|
727
|
+
parts = []
|
|
728
|
+
for k, v in data.items():
|
|
729
|
+
if k == "results":
|
|
730
|
+
continue # 已在上面处理
|
|
731
|
+
parts.append(f"{k}: {v}")
|
|
732
|
+
return "\n".join(parts) if parts else str(data)
|
|
733
|
+
if isinstance(data, list):
|
|
734
|
+
return "\n".join(str(item) for item in data)
|
|
735
|
+
return str(data)
|
|
736
|
+
|
|
703
737
|
def _extract_tool_output(tr):
|
|
704
738
|
"""从工具结果中提取实际输出文本"""
|
|
739
|
+
# 优先使用 output 字段 (技能明确设置的输出)
|
|
705
740
|
out = tr.get("output", "")
|
|
706
741
|
if out:
|
|
707
742
|
return out
|
|
743
|
+
# 如果 output 为空,尝试智能格式化 data 字段
|
|
744
|
+
data = tr.get("data")
|
|
745
|
+
if data is not None:
|
|
746
|
+
formatted = _format_data_for_llm(data)
|
|
747
|
+
if formatted:
|
|
748
|
+
return formatted
|
|
749
|
+
# 降级到 message / stdout / error
|
|
708
750
|
out = tr.get("message", "")
|
|
709
751
|
if out:
|
|
710
752
|
return out
|
|
711
753
|
out = tr.get("stdout", "")
|
|
712
754
|
if out:
|
|
713
755
|
return out
|
|
714
|
-
data = tr.get("data")
|
|
715
|
-
if data is not None:
|
|
716
|
-
return str(data) if not isinstance(data, str) else data
|
|
717
756
|
return tr.get("error", "")
|
|
718
757
|
|
|
719
758
|
tool_output_text = _extract_tool_output(tool_result)
|
|
@@ -745,16 +784,18 @@ class MainAgent(BaseAgent):
|
|
|
745
784
|
need_callback = True
|
|
746
785
|
|
|
747
786
|
output_str = tool_output_text
|
|
787
|
+
# 搜索和网页读取类工具允许更长的输出
|
|
788
|
+
_max_output = 6000 if tool_name in ("web_search", "web_read", "url_read") else 3000
|
|
748
789
|
tool_outputs_parts.append(
|
|
749
790
|
f"### {before_call}\n"
|
|
750
791
|
f"**工具**: {tool_name}\n"
|
|
751
792
|
f"**结果**: {'成功' if tool_result.get('success') else '失败'}\n"
|
|
752
|
-
f"{truncate_str(output_str,
|
|
793
|
+
f"{truncate_str(output_str, _max_output)}\n"
|
|
753
794
|
)
|
|
754
795
|
|
|
755
796
|
conversation_history.append(Message(
|
|
756
797
|
role="assistant",
|
|
757
|
-
content=f"执行工具 {tool_name}:\n{truncate_str(output_str,
|
|
798
|
+
content=f"执行工具 {tool_name}:\n{truncate_str(output_str, _max_output)}",
|
|
758
799
|
))
|
|
759
800
|
conversation_history.append(Message(
|
|
760
801
|
role="user",
|
package/package.json
CHANGED
|
Binary file
|
package/web/api_server.py
CHANGED
|
@@ -2357,6 +2357,9 @@ class ApiServer:
|
|
|
2357
2357
|
pass
|
|
2358
2358
|
return web.json_response({**agent_info, "sessions": sessions})
|
|
2359
2359
|
|
|
2360
|
+
# Internal keys that should not appear in chat history UI
|
|
2361
|
+
_HIDDEN_KEYS = {"llm_output", "tool_call", "tool_result"}
|
|
2362
|
+
|
|
2360
2363
|
async def handle_get_messages(self, request):
|
|
2361
2364
|
sid = request.match_info["sid"]
|
|
2362
2365
|
if not self.core.memory: return web.json_response([])
|
|
@@ -2364,6 +2367,8 @@ class ApiServer:
|
|
|
2364
2367
|
offset = int(request.query.get("offset", 0))
|
|
2365
2368
|
entries = self.core.memory.get_conversation(sid, limit=limit + offset)
|
|
2366
2369
|
entries = entries[offset:]
|
|
2370
|
+
# Filter out internal entries (LLM raw output, tool calls/results)
|
|
2371
|
+
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
2367
2372
|
return web.json_response([{"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""} for e in entries])
|
|
2368
2373
|
|
|
2369
2374
|
async def handle_get_messages_query(self, request):
|
|
@@ -2376,6 +2381,8 @@ class ApiServer:
|
|
|
2376
2381
|
offset = int(request.query.get("offset", 0))
|
|
2377
2382
|
entries = self.core.memory.get_conversation(sid, limit=limit + offset)
|
|
2378
2383
|
entries = entries[offset:]
|
|
2384
|
+
# Filter out internal entries (LLM raw output, tool calls/results)
|
|
2385
|
+
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
2379
2386
|
return web.json_response([{"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""} for e in entries])
|
|
2380
2387
|
|
|
2381
2388
|
async def handle_delete_session(self, request):
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -1765,9 +1765,14 @@ async function selectSession(id) {
|
|
|
1765
1765
|
const loaded = (Array.isArray(data) ? data : []).filter(function(m) {
|
|
1766
1766
|
return m && (m.role === 'user' || m.role === 'assistant' || m.role === 'tool');
|
|
1767
1767
|
}).map(function(m) {
|
|
1768
|
+
var content = (m.content != null) ? String(m.content) : '';
|
|
1769
|
+
// Strip XML tags from assistant messages (backend may store raw LLM XML output)
|
|
1770
|
+
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
1771
|
+
content = (typeof _stripXmlTags === 'function') ? _stripXmlTags(content) : content;
|
|
1772
|
+
}
|
|
1768
1773
|
return {
|
|
1769
1774
|
role: m.role || 'assistant',
|
|
1770
|
-
content:
|
|
1775
|
+
content: content,
|
|
1771
1776
|
time: m.time || m.created_at || '',
|
|
1772
1777
|
key: m.key || '',
|
|
1773
1778
|
};
|
|
@@ -1824,9 +1829,14 @@ async function loadMoreMessages() {
|
|
|
1824
1829
|
const loaded = data.filter(function(m) {
|
|
1825
1830
|
return m && (m.role === 'user' || m.role === 'assistant' || m.role === 'tool');
|
|
1826
1831
|
}).map(function(m) {
|
|
1832
|
+
var content = (m.content != null) ? String(m.content) : '';
|
|
1833
|
+
// Strip XML tags from assistant messages (backend may store raw LLM XML output)
|
|
1834
|
+
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
1835
|
+
content = (typeof _stripXmlTags === 'function') ? _stripXmlTags(content) : content;
|
|
1836
|
+
}
|
|
1827
1837
|
return {
|
|
1828
1838
|
role: m.role || 'assistant',
|
|
1829
|
-
content:
|
|
1839
|
+
content: content,
|
|
1830
1840
|
time: m.time || m.created_at || '',
|
|
1831
1841
|
};
|
|
1832
1842
|
});
|
|
@@ -305,22 +305,25 @@ function updateStreamingMessage(msgIdx) {
|
|
|
305
305
|
const container = document.getElementById('messagesInner');
|
|
306
306
|
if (!container) return;
|
|
307
307
|
|
|
308
|
-
// Find
|
|
309
|
-
|
|
308
|
+
// Find the streaming message row by counting ALL message rows (not just assistant)
|
|
309
|
+
// msgIdx is the global index in state.messages, so we count all rows to match
|
|
310
|
+
const allRows = container.querySelectorAll('.message-row');
|
|
310
311
|
let targetRow = null;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
assistantCount++;
|
|
315
|
-
if (assistantCount === msgIdx + 1) {
|
|
312
|
+
let rowCount = 0;
|
|
313
|
+
for (const row of allRows) {
|
|
314
|
+
if (rowCount === msgIdx) {
|
|
316
315
|
targetRow = row;
|
|
317
316
|
break;
|
|
318
317
|
}
|
|
318
|
+
rowCount++;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
321
|
// Fallback: if we can't find by count, use last assistant row
|
|
322
|
-
if (!targetRow
|
|
323
|
-
|
|
322
|
+
if (!targetRow) {
|
|
323
|
+
const assistantRows = container.querySelectorAll('.message-row.assistant');
|
|
324
|
+
if (assistantRows.length > 0) {
|
|
325
|
+
targetRow = assistantRows[assistantRows.length - 1];
|
|
326
|
+
}
|
|
324
327
|
}
|
|
325
328
|
|
|
326
329
|
if (!targetRow) {
|
|
@@ -343,12 +346,32 @@ function updateStreamingMessage(msgIdx) {
|
|
|
343
346
|
}
|
|
344
347
|
}
|
|
345
348
|
if (msg.reasoning) {
|
|
346
|
-
// Count words/chars during streaming for live counter
|
|
347
349
|
const reasoningLen = msg.reasoning.length;
|
|
348
350
|
const reasoningWordCount = msg.streaming
|
|
349
351
|
? '<span class="thought-word-count">' + reasoningLen + ' 字</span>'
|
|
350
352
|
: '';
|
|
351
|
-
|
|
353
|
+
if (reasoningDetails) {
|
|
354
|
+
// Incremental update: only update word count and badge, append new text to content
|
|
355
|
+
const label = reasoningDetails.querySelector('.thought-label');
|
|
356
|
+
if (label) label.innerHTML = '模型推理过程' + reasoningWordCount;
|
|
357
|
+
const badge = reasoningDetails.querySelector('.thought-badge');
|
|
358
|
+
if (badge) badge.textContent = msg.streaming ? '推理中...' : '已完成';
|
|
359
|
+
// Incremental text append for streaming (avoid full markdown rebuild)
|
|
360
|
+
const thoughtContent = reasoningDetails.querySelector('.thought-content');
|
|
361
|
+
if (thoughtContent && msg.streaming) {
|
|
362
|
+
const prevLen = reasoningDetails._lastReasoningLen || 0;
|
|
363
|
+
if (msg.reasoning.length > prevLen) {
|
|
364
|
+
const newText = msg.reasoning.substring(prevLen);
|
|
365
|
+
thoughtContent.insertAdjacentHTML('beforeend', renderMarkdown(newText));
|
|
366
|
+
reasoningDetails._lastReasoningLen = msg.reasoning.length;
|
|
367
|
+
}
|
|
368
|
+
} else if (thoughtContent && !msg.streaming) {
|
|
369
|
+
// Final render once streaming stops
|
|
370
|
+
thoughtContent.innerHTML = renderMarkdown(msg.reasoning);
|
|
371
|
+
reasoningDetails._lastReasoningLen = msg.reasoning.length;
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
const reasoningHtml = `<details class="thought-block ${msg.streaming ? 'streaming' : ''}" ${msg.streaming ? 'open' : ''}>
|
|
352
375
|
<summary>
|
|
353
376
|
<span class="thought-icon">💡</span>
|
|
354
377
|
<span class="thought-label">模型推理过程${reasoningWordCount}</span>
|
|
@@ -356,10 +379,10 @@ function updateStreamingMessage(msgIdx) {
|
|
|
356
379
|
</summary>
|
|
357
380
|
<div class="thought-content">${renderMarkdown(msg.reasoning)}</div>
|
|
358
381
|
</details>`;
|
|
359
|
-
if (reasoningDetails) {
|
|
360
|
-
reasoningDetails.outerHTML = reasoningHtml;
|
|
361
|
-
} else {
|
|
362
382
|
contentArea.insertAdjacentHTML('afterbegin', reasoningHtml);
|
|
383
|
+
// Set initial length tracking
|
|
384
|
+
const newBlock = contentArea.querySelector(':scope > .thought-block');
|
|
385
|
+
if (newBlock) newBlock._lastReasoningLen = msg.reasoning.length;
|
|
363
386
|
}
|
|
364
387
|
}
|
|
365
388
|
|
|
@@ -378,7 +401,26 @@ function updateStreamingMessage(msgIdx) {
|
|
|
378
401
|
const thoughtWordCount = msg.streaming
|
|
379
402
|
? '<span class="thought-word-count">' + thoughtLen + ' 字</span>'
|
|
380
403
|
: '';
|
|
381
|
-
|
|
404
|
+
if (thoughtBlock) {
|
|
405
|
+
// Incremental update for thought block too
|
|
406
|
+
const label = thoughtBlock.querySelector('.thought-label');
|
|
407
|
+
if (label) label.innerHTML = 'Agent 思考过程' + thoughtWordCount;
|
|
408
|
+
const badge = thoughtBlock.querySelector('.thought-badge');
|
|
409
|
+
if (badge) badge.textContent = msg.streaming ? '思考中...' : '已完成';
|
|
410
|
+
const thoughtContent = thoughtBlock.querySelector('.thought-content');
|
|
411
|
+
if (thoughtContent && msg.streaming) {
|
|
412
|
+
const prevLen = thoughtBlock._lastThoughtLen || 0;
|
|
413
|
+
if (msg.thought.length > prevLen) {
|
|
414
|
+
const newText = msg.thought.substring(prevLen);
|
|
415
|
+
thoughtContent.insertAdjacentHTML('beforeend', renderMarkdown(newText));
|
|
416
|
+
thoughtBlock._lastThoughtLen = msg.thought.length;
|
|
417
|
+
}
|
|
418
|
+
} else if (thoughtContent && !msg.streaming) {
|
|
419
|
+
thoughtContent.innerHTML = renderMarkdown(msg.thought);
|
|
420
|
+
thoughtBlock._lastThoughtLen = msg.thought.length;
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
const thoughtHtml = `<details class="thought-block ${msg.streaming ? 'streaming' : ''}" ${msg.streaming ? 'open' : ''}>
|
|
382
424
|
<summary>
|
|
383
425
|
<span class="thought-icon">💭</span>
|
|
384
426
|
<span class="thought-label">Agent 思考过程${thoughtWordCount}</span>
|
|
@@ -386,16 +428,14 @@ function updateStreamingMessage(msgIdx) {
|
|
|
386
428
|
</summary>
|
|
387
429
|
<div class="thought-content">${renderMarkdown(msg.thought)}</div>
|
|
388
430
|
</details>`;
|
|
389
|
-
if (thoughtBlock) {
|
|
390
|
-
thoughtBlock.outerHTML = thoughtHtml;
|
|
391
|
-
} else {
|
|
392
|
-
// Insert after reasoning block if exists, otherwise at beginning
|
|
393
431
|
const existingReasoning = contentArea.querySelectorAll('.thought-block');
|
|
394
432
|
if (existingReasoning.length > 0) {
|
|
395
433
|
existingReasoning[existingReasoning.length - 1].insertAdjacentHTML('afterend', thoughtHtml);
|
|
396
434
|
} else {
|
|
397
435
|
contentArea.insertAdjacentHTML('afterbegin', thoughtHtml);
|
|
398
436
|
}
|
|
437
|
+
const newBlock = contentArea.querySelectorAll('.thought-block');
|
|
438
|
+
if (newBlock.length > 0) newBlock[newBlock.length - 1]._lastThoughtLen = msg.thought.length;
|
|
399
439
|
}
|
|
400
440
|
}
|
|
401
441
|
|
|
@@ -417,7 +457,26 @@ function updateStreamingMessage(msgIdx) {
|
|
|
417
457
|
const v2WordCount = msg.streaming
|
|
418
458
|
? '<span class="thought-word-count">' + v2Len + ' 字</span>'
|
|
419
459
|
: '';
|
|
420
|
-
|
|
460
|
+
if (v2ReasoningBlock) {
|
|
461
|
+
// Incremental update for V2 reasoning block
|
|
462
|
+
const label = v2ReasoningBlock.querySelector('.thought-label');
|
|
463
|
+
if (label) label.innerHTML = 'V2 推理过程' + v2WordCount;
|
|
464
|
+
const badge = v2ReasoningBlock.querySelector('.thought-badge');
|
|
465
|
+
if (badge) badge.textContent = msg.streaming ? '推理中...' : '已完成';
|
|
466
|
+
const thoughtContent = v2ReasoningBlock.querySelector('.thought-content');
|
|
467
|
+
if (thoughtContent && msg.streaming) {
|
|
468
|
+
const prevLen = v2ReasoningBlock._lastV2Len || 0;
|
|
469
|
+
if (msg._v2Reasoning.length > prevLen) {
|
|
470
|
+
const newText = msg._v2Reasoning.substring(prevLen);
|
|
471
|
+
thoughtContent.insertAdjacentHTML('beforeend', renderMarkdown(newText));
|
|
472
|
+
v2ReasoningBlock._lastV2Len = msg._v2Reasoning.length;
|
|
473
|
+
}
|
|
474
|
+
} else if (thoughtContent && !msg.streaming) {
|
|
475
|
+
thoughtContent.innerHTML = renderMarkdown(msg._v2Reasoning);
|
|
476
|
+
v2ReasoningBlock._lastV2Len = msg._v2Reasoning.length;
|
|
477
|
+
}
|
|
478
|
+
} else if (!msg.thought) {
|
|
479
|
+
const v2Html = `<details class="thought-block ${msg.streaming ? 'streaming' : ''}" ${msg.streaming ? 'open' : ''}>
|
|
421
480
|
<summary>
|
|
422
481
|
<span class="thought-icon">🧠</span>
|
|
423
482
|
<span class="thought-label">V2 推理过程${v2WordCount}</span>
|
|
@@ -425,10 +484,9 @@ function updateStreamingMessage(msgIdx) {
|
|
|
425
484
|
</summary>
|
|
426
485
|
<div class="thought-content">${renderMarkdown(msg._v2Reasoning)}</div>
|
|
427
486
|
</details>`;
|
|
428
|
-
if (v2ReasoningBlock) {
|
|
429
|
-
v2ReasoningBlock.outerHTML = v2Html;
|
|
430
|
-
} else if (!msg.thought) {
|
|
431
487
|
contentArea.insertAdjacentHTML('afterbegin', v2Html);
|
|
488
|
+
const newBlock = contentArea.querySelector(':scope > .thought-block');
|
|
489
|
+
if (newBlock) newBlock._lastV2Len = msg._v2Reasoning.length;
|
|
432
490
|
}
|
|
433
491
|
}
|
|
434
492
|
}
|