myagent-ai 1.15.0 → 1.15.2
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 +4 -1
- package/memory/manager.py +8 -4
- package/package.json +1 -1
- package/web/api_server.py +9 -4
- package/web/ui/chat/chat_main.js +68 -28
package/agents/main_agent.py
CHANGED
|
@@ -835,6 +835,7 @@ class MainAgent(BaseAgent):
|
|
|
835
835
|
"v2_tool_start",
|
|
836
836
|
{"tool": {
|
|
837
837
|
"toolname": tool_name,
|
|
838
|
+
"beforecalltext": before_call,
|
|
838
839
|
"parms": truncate_str(parms, 500),
|
|
839
840
|
"timeout": timeout,
|
|
840
841
|
"callback": should_callback,
|
|
@@ -1028,10 +1029,12 @@ class MainAgent(BaseAgent):
|
|
|
1028
1029
|
|
|
1029
1030
|
# 保存工具调用到会话记忆
|
|
1030
1031
|
if self.memory:
|
|
1032
|
+
# 构建工具调用记录,包含 beforecalltext 作为标题
|
|
1033
|
+
tool_call_title = before_call if before_call else f"调用工具: {tool_name}"
|
|
1031
1034
|
self.memory.add_session(
|
|
1032
1035
|
session_id=context.session_id,
|
|
1033
1036
|
role="assistant",
|
|
1034
|
-
content=f"调用工具: {tool_name}\n参数: {truncate_str(parms, 1000)}",
|
|
1037
|
+
content=f"{tool_call_title}\n调用工具: {tool_name}\n参数: {truncate_str(parms, 1000)}",
|
|
1035
1038
|
key="tool_call",
|
|
1036
1039
|
importance=0.4,
|
|
1037
1040
|
)
|
package/memory/manager.py
CHANGED
|
@@ -290,12 +290,16 @@ class MemoryManager:
|
|
|
290
290
|
return self._insert(entry)
|
|
291
291
|
|
|
292
292
|
def get_conversation(self, session_id, limit=500, include_roles=None) -> List[MemoryEntry]:
|
|
293
|
-
"""获取对话历史(session 分类中 role 非空的条目),按时间正序排列。
|
|
293
|
+
"""获取对话历史(session 分类中 role 非空的条目),按时间正序排列。
|
|
294
|
+
|
|
295
|
+
只排除纯内部审计条目(llm_output 原始LLM输出、conversation_insight 记忆提炼)。
|
|
296
|
+
tool_call 和 tool_result 会返回给前端以展示完整的工具调用过程。
|
|
297
|
+
"""
|
|
294
298
|
conn = self._get_conn()
|
|
295
|
-
#
|
|
296
|
-
sql = """SELECT * FROM memories
|
|
299
|
+
# 只排除纯内部审计条目,保留 tool_call/tool_result 供前端展示
|
|
300
|
+
sql = """SELECT * FROM memories
|
|
297
301
|
WHERE session_id = ? AND category = 'session' AND role != ''
|
|
298
|
-
AND key NOT IN ('llm_output', '
|
|
302
|
+
AND key NOT IN ('llm_output', 'conversation_insight')
|
|
299
303
|
ORDER BY created_at ASC LIMIT ?"""
|
|
300
304
|
rows = conn.execute(sql, (session_id, limit)).fetchall()
|
|
301
305
|
entries = [MemoryEntry.from_row(row) for row in rows]
|
package/package.json
CHANGED
package/web/api_server.py
CHANGED
|
@@ -2443,16 +2443,21 @@ class ApiServer:
|
|
|
2443
2443
|
async def handle_list_sessions(self, request):
|
|
2444
2444
|
if not self.core.memory: return web.json_response([])
|
|
2445
2445
|
agent = request.query.get("agent", "")
|
|
2446
|
+
# 只统计用户可见的消息数(排除 llm_output 和 conversation_insight)
|
|
2446
2447
|
if agent:
|
|
2447
2448
|
prefix = f"{agent}_"
|
|
2448
2449
|
rows = self.core.memory._get_conn().execute(
|
|
2449
2450
|
"SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories "
|
|
2450
|
-
"WHERE category = 'session' AND
|
|
2451
|
+
"WHERE category = 'session' AND role != '' "
|
|
2452
|
+
"AND key NOT IN ('llm_output', 'conversation_insight') "
|
|
2453
|
+
"AND session_id LIKE ? GROUP BY session_id ORDER BY last DESC LIMIT 100",
|
|
2451
2454
|
(prefix + "%",)).fetchall()
|
|
2452
2455
|
else:
|
|
2453
2456
|
rows = self.core.memory._get_conn().execute(
|
|
2454
2457
|
"SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories "
|
|
2455
|
-
"WHERE category = 'session'
|
|
2458
|
+
"WHERE category = 'session' AND role != '' "
|
|
2459
|
+
"AND key NOT IN ('llm_output', 'conversation_insight') "
|
|
2460
|
+
"GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
|
|
2456
2461
|
sessions = [{"id": r["session_id"], "messages": r["cnt"], "last": r["last"]} for r in rows]
|
|
2457
2462
|
# 批量获取自定义会话名称
|
|
2458
2463
|
sids = [s["id"] for s in sessions]
|
|
@@ -2513,7 +2518,7 @@ class ApiServer:
|
|
|
2513
2518
|
offset = int(request.query.get("offset", 0))
|
|
2514
2519
|
entries = self.core.memory.get_conversation(sid, limit=limit + offset)
|
|
2515
2520
|
entries = entries[offset:]
|
|
2516
|
-
# Filter out internal entries (LLM raw output
|
|
2521
|
+
# Filter out internal entries (LLM raw output only)
|
|
2517
2522
|
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
2518
2523
|
return web.json_response([{"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""} for e in entries])
|
|
2519
2524
|
|
|
@@ -2527,7 +2532,7 @@ class ApiServer:
|
|
|
2527
2532
|
offset = int(request.query.get("offset", 0))
|
|
2528
2533
|
entries = self.core.memory.get_conversation(sid, limit=limit + offset)
|
|
2529
2534
|
entries = entries[offset:]
|
|
2530
|
-
# Filter out internal entries (LLM raw output
|
|
2535
|
+
# Filter out internal entries (LLM raw output only)
|
|
2531
2536
|
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
2532
2537
|
return web.json_response([{"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""} for e in entries])
|
|
2533
2538
|
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -1854,8 +1854,10 @@ async function selectSession(id) {
|
|
|
1854
1854
|
return m && (m.role === 'user' || m.role === 'assistant' || m.role === 'tool');
|
|
1855
1855
|
}).map(function(m) {
|
|
1856
1856
|
var content = (m.content != null) ? String(m.content) : '';
|
|
1857
|
-
// Strip XML tags from assistant messages
|
|
1858
|
-
|
|
1857
|
+
// Strip XML tags from assistant messages that are raw LLM output (key is empty or llm_output)
|
|
1858
|
+
// Do NOT strip tool_call messages — their content has a specific format (beforecalltext\n调用工具:...)
|
|
1859
|
+
var mkey = (m.key || '').toLowerCase();
|
|
1860
|
+
if (m.role === 'assistant' && mkey !== 'tool_call' && content && content.trim().startsWith('<')) {
|
|
1859
1861
|
content = (typeof _stripXmlTags === 'function') ? _stripXmlTags(content) : content;
|
|
1860
1862
|
}
|
|
1861
1863
|
return {
|
|
@@ -1923,8 +1925,8 @@ async function loadMoreMessages() {
|
|
|
1923
1925
|
return m && (m.role === 'user' || m.role === 'assistant' || m.role === 'tool');
|
|
1924
1926
|
}).map(function(m) {
|
|
1925
1927
|
var content = (m.content != null) ? String(m.content) : '';
|
|
1926
|
-
|
|
1927
|
-
if (m.role === 'assistant' && content && content.trim().startsWith('<')) {
|
|
1928
|
+
var mkey = (m.key || '').toLowerCase();
|
|
1929
|
+
if (m.role === 'assistant' && mkey !== 'tool_call' && content && content.trim().startsWith('<')) {
|
|
1928
1930
|
content = (typeof _stripXmlTags === 'function') ? _stripXmlTags(content) : content;
|
|
1929
1931
|
}
|
|
1930
1932
|
return {
|
|
@@ -1937,8 +1939,8 @@ async function loadMoreMessages() {
|
|
|
1937
1939
|
|
|
1938
1940
|
// Group consecutive non-user messages for the loaded batch
|
|
1939
1941
|
const grouped = groupHistoryMessages(loaded);
|
|
1940
|
-
//
|
|
1941
|
-
state.messages = grouped.concat(state.messages);
|
|
1942
|
+
// Prepend to existing messages, then RE-GROUP the boundary to merge split groups
|
|
1943
|
+
state.messages = groupHistoryMessages(grouped.concat(state.messages));
|
|
1942
1944
|
state._msgLoadOffset += loaded.length;
|
|
1943
1945
|
state._msgLoadTotal = state.messages.length;
|
|
1944
1946
|
|
|
@@ -2102,6 +2104,43 @@ async function clearCurrentChat() {
|
|
|
2102
2104
|
// tool_result→ role="tool", key="tool_result"
|
|
2103
2105
|
//
|
|
2104
2106
|
// Result: user → [assistant { text → tool_call → tool_result → text → ... }] → user
|
|
2107
|
+
|
|
2108
|
+
// 解析 tool_call 消息内容,兼容新旧格式
|
|
2109
|
+
// 新格式: "beforecalltext\n调用工具: xxx\n参数: yyy"
|
|
2110
|
+
// 旧格式: "调用工具: xxx\n参数: yyy"
|
|
2111
|
+
function parseToolCallContent(content) {
|
|
2112
|
+
if (!content) return { title: '调用工具', toolName: '', params: '' };
|
|
2113
|
+
var lines = content.split('\n');
|
|
2114
|
+
var title = '';
|
|
2115
|
+
var toolName = '';
|
|
2116
|
+
var params = '';
|
|
2117
|
+
// 查找 "调用工具:" 行
|
|
2118
|
+
for (var li = 0; li < lines.length; li++) {
|
|
2119
|
+
var line = lines[li].trim();
|
|
2120
|
+
var m = line.match(/^调用工具:\s*(\S+)/);
|
|
2121
|
+
if (m) {
|
|
2122
|
+
toolName = m[1];
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
// 查找 "参数:" 行
|
|
2127
|
+
for (var li2 = 0; li2 < lines.length; li2++) {
|
|
2128
|
+
var line2 = lines[li2].trim();
|
|
2129
|
+
var m2 = line2.match(/^参数:\s*([\s\S]*)/);
|
|
2130
|
+
if (m2) {
|
|
2131
|
+
params = m2[1].trim();
|
|
2132
|
+
break;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
// 标题: 如果第一行不是"调用工具:",则使用第一行作为 beforecalltext 标题
|
|
2136
|
+
if (lines[0] && !lines[0].trim().startsWith('调用工具:')) {
|
|
2137
|
+
title = lines[0].trim();
|
|
2138
|
+
} else {
|
|
2139
|
+
title = toolName ? ('调用工具: ' + toolName) : content.substring(0, 100);
|
|
2140
|
+
}
|
|
2141
|
+
return { title: title, toolName: toolName, params: params };
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2105
2144
|
function groupHistoryMessages(messages) {
|
|
2106
2145
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
2107
2146
|
|
|
@@ -2122,14 +2161,18 @@ function groupHistoryMessages(messages) {
|
|
|
2122
2161
|
|
|
2123
2162
|
// Process the first assistant message
|
|
2124
2163
|
if (msg.key === 'tool_call') {
|
|
2125
|
-
|
|
2164
|
+
// 新格式: beforecalltext\n调用工具: xxx\n参数: yyy
|
|
2165
|
+
// 旧格式: 调用工具: xxx\n参数: yyy
|
|
2166
|
+
var _tcParts = msg._parsedToolCall || parseToolCallContent(msg.content);
|
|
2167
|
+
msg._parsedToolCall = _tcParts; // 缓存
|
|
2126
2168
|
parts.push({
|
|
2127
2169
|
type: 'exec',
|
|
2128
2170
|
data: {
|
|
2129
2171
|
id: _evtId++,
|
|
2130
2172
|
type: 'tool_call',
|
|
2131
|
-
title:
|
|
2132
|
-
tool_name: toolName,
|
|
2173
|
+
title: _tcParts.title,
|
|
2174
|
+
tool_name: _tcParts.toolName,
|
|
2175
|
+
params: _tcParts.params || undefined,
|
|
2133
2176
|
status: 'done',
|
|
2134
2177
|
}
|
|
2135
2178
|
});
|
|
@@ -2168,18 +2211,17 @@ function groupHistoryMessages(messages) {
|
|
|
2168
2211
|
});
|
|
2169
2212
|
i++;
|
|
2170
2213
|
} else if (next.role === 'assistant' && next.key === 'tool_call') {
|
|
2171
|
-
//
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
const toolParams = paramsMatch ? paramsMatch[1].trim() : '';
|
|
2214
|
+
// 新格式: beforecalltext\n调用工具: xxx\n参数: yyy
|
|
2215
|
+
var _tcParts2 = next._parsedToolCall || parseToolCallContent(next.content);
|
|
2216
|
+
next._parsedToolCall = _tcParts2;
|
|
2175
2217
|
parts.push({
|
|
2176
2218
|
type: 'exec',
|
|
2177
2219
|
data: {
|
|
2178
2220
|
id: _evtId++,
|
|
2179
2221
|
type: 'tool_call',
|
|
2180
|
-
title:
|
|
2181
|
-
tool_name: toolName,
|
|
2182
|
-
params:
|
|
2222
|
+
title: _tcParts2.title,
|
|
2223
|
+
tool_name: _tcParts2.toolName,
|
|
2224
|
+
params: _tcParts2.params || undefined,
|
|
2183
2225
|
status: 'done',
|
|
2184
2226
|
}
|
|
2185
2227
|
});
|
|
@@ -2215,17 +2257,16 @@ function groupHistoryMessages(messages) {
|
|
|
2215
2257
|
const isCall = msg.key === 'tool_call';
|
|
2216
2258
|
|
|
2217
2259
|
if (isCall) {
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
const toolParams = paramsMatch ? paramsMatch[1].trim() : '';
|
|
2260
|
+
var _tcParts3 = msg._parsedToolCall || parseToolCallContent(msg.content);
|
|
2261
|
+
msg._parsedToolCall = _tcParts3;
|
|
2221
2262
|
parts.push({
|
|
2222
2263
|
type: 'exec',
|
|
2223
2264
|
data: {
|
|
2224
2265
|
id: _evtId++,
|
|
2225
2266
|
type: 'tool_call',
|
|
2226
|
-
title:
|
|
2227
|
-
tool_name: toolName,
|
|
2228
|
-
params:
|
|
2267
|
+
title: _tcParts3.title,
|
|
2268
|
+
tool_name: _tcParts3.toolName,
|
|
2269
|
+
params: _tcParts3.params || undefined,
|
|
2229
2270
|
status: 'done',
|
|
2230
2271
|
}
|
|
2231
2272
|
});
|
|
@@ -2254,17 +2295,16 @@ function groupHistoryMessages(messages) {
|
|
|
2254
2295
|
while (i < messages.length) {
|
|
2255
2296
|
const next = messages[i];
|
|
2256
2297
|
if (next.role === 'assistant' && next.key === 'tool_call') {
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
const toolParams = paramsMatch ? paramsMatch[1].trim() : '';
|
|
2298
|
+
var _tcParts4 = next._parsedToolCall || parseToolCallContent(next.content);
|
|
2299
|
+
next._parsedToolCall = _tcParts4;
|
|
2260
2300
|
parts.push({
|
|
2261
2301
|
type: 'exec',
|
|
2262
2302
|
data: {
|
|
2263
2303
|
id: _evtId++,
|
|
2264
2304
|
type: 'tool_call',
|
|
2265
|
-
title:
|
|
2266
|
-
tool_name: toolName,
|
|
2267
|
-
params:
|
|
2305
|
+
title: _tcParts4.title,
|
|
2306
|
+
tool_name: _tcParts4.toolName,
|
|
2307
|
+
params: _tcParts4.params || undefined,
|
|
2268
2308
|
status: 'done',
|
|
2269
2309
|
}
|
|
2270
2310
|
});
|