myagent-ai 1.23.81 → 1.23.82
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/agent_storage.py +0 -0
- package/data/novnc/lib/base64.js +0 -0
- package/data/novnc/lib/decoders/copyrect.js +0 -0
- package/data/novnc/lib/decoders/hextile.js +0 -0
- package/data/novnc/lib/decoders/jpeg.js +0 -0
- package/data/novnc/lib/decoders/raw.js +0 -0
- package/data/novnc/lib/decoders/rre.js +0 -0
- package/data/novnc/lib/decoders/tight.js +0 -0
- package/data/novnc/lib/decoders/tightpng.js +0 -0
- package/data/novnc/lib/decoders/zrle.js +0 -0
- package/data/novnc/lib/deflator.js +0 -0
- package/data/novnc/lib/des.js +0 -0
- package/data/novnc/lib/display.js +0 -0
- package/data/novnc/lib/encodings.js +0 -0
- package/data/novnc/lib/inflator.js +0 -0
- package/data/novnc/lib/input/domkeytable.js +0 -0
- package/data/novnc/lib/input/fixedkeys.js +0 -0
- package/data/novnc/lib/input/gesturehandler.js +0 -0
- package/data/novnc/lib/input/keyboard.js +0 -0
- package/data/novnc/lib/input/keysym.js +0 -0
- package/data/novnc/lib/input/keysymdef.js +0 -0
- package/data/novnc/lib/input/util.js +0 -0
- package/data/novnc/lib/input/vkeys.js +0 -0
- package/data/novnc/lib/input/xtscancodes.js +0 -0
- package/data/novnc/lib/ra2.js +0 -0
- package/data/novnc/lib/rfb.js +0 -0
- package/data/novnc/lib/util/browser.js +0 -0
- package/data/novnc/lib/util/cursor.js +0 -0
- package/data/novnc/lib/util/element.js +0 -0
- package/data/novnc/lib/util/events.js +0 -0
- package/data/novnc/lib/util/eventtarget.js +0 -0
- package/data/novnc/lib/util/int.js +0 -0
- package/data/novnc/lib/util/logging.js +0 -0
- package/data/novnc/lib/util/md5.js +0 -0
- package/data/novnc/lib/util/strings.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/utils/common.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/adler32.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/constants.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/crc32.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/deflate.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/gzheader.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/inffast.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/inflate.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/inftrees.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/messages.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/trees.js +0 -0
- package/data/novnc/lib/vendor/pako/lib/zlib/zstream.js +0 -0
- package/data/novnc/lib/websock.js +0 -0
- package/myagent/core/agent_storage.py +0 -0
- package/myagent/data/novnc/lib/base64.js +0 -0
- package/myagent/data/novnc/lib/decoders/copyrect.js +0 -0
- package/myagent/data/novnc/lib/decoders/hextile.js +0 -0
- package/myagent/data/novnc/lib/decoders/jpeg.js +0 -0
- package/myagent/data/novnc/lib/decoders/raw.js +0 -0
- package/myagent/data/novnc/lib/decoders/rre.js +0 -0
- package/myagent/data/novnc/lib/decoders/tight.js +0 -0
- package/myagent/data/novnc/lib/decoders/tightpng.js +0 -0
- package/myagent/data/novnc/lib/decoders/zrle.js +0 -0
- package/myagent/data/novnc/lib/deflator.js +0 -0
- package/myagent/data/novnc/lib/des.js +0 -0
- package/myagent/data/novnc/lib/display.js +0 -0
- package/myagent/data/novnc/lib/encodings.js +0 -0
- package/myagent/data/novnc/lib/inflator.js +0 -0
- package/myagent/data/novnc/lib/input/domkeytable.js +0 -0
- package/myagent/data/novnc/lib/input/fixedkeys.js +0 -0
- package/myagent/data/novnc/lib/input/gesturehandler.js +0 -0
- package/myagent/data/novnc/lib/input/keyboard.js +0 -0
- package/myagent/data/novnc/lib/input/keysym.js +0 -0
- package/myagent/data/novnc/lib/input/keysymdef.js +0 -0
- package/myagent/data/novnc/lib/input/util.js +0 -0
- package/myagent/data/novnc/lib/input/vkeys.js +0 -0
- package/myagent/data/novnc/lib/input/xtscancodes.js +0 -0
- package/myagent/data/novnc/lib/ra2.js +0 -0
- package/myagent/data/novnc/lib/rfb.js +0 -0
- package/myagent/data/novnc/lib/util/browser.js +0 -0
- package/myagent/data/novnc/lib/util/cursor.js +0 -0
- package/myagent/data/novnc/lib/util/element.js +0 -0
- package/myagent/data/novnc/lib/util/events.js +0 -0
- package/myagent/data/novnc/lib/util/eventtarget.js +0 -0
- package/myagent/data/novnc/lib/util/int.js +0 -0
- package/myagent/data/novnc/lib/util/logging.js +0 -0
- package/myagent/data/novnc/lib/util/md5.js +0 -0
- package/myagent/data/novnc/lib/util/strings.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/utils/common.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/adler32.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/constants.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/crc32.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/deflate.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/gzheader.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/inffast.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/inflate.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/inftrees.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/messages.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/trees.js +0 -0
- package/myagent/data/novnc/lib/vendor/pako/lib/zlib/zstream.js +0 -0
- package/myagent/data/novnc/lib/websock.js +0 -0
- package/myagent/package.json +1 -1
- package/myagent/web/api_server.py +336 -10
- package/myagent/web/ui/admin/admin-agentchat.js +0 -0
- package/myagent/web/ui/admin/admin-agents.js +0 -0
- package/myagent/web/ui/admin/admin-core.js +0 -0
- package/myagent/web/ui/admin/admin-dashboard.js +0 -0
- package/myagent/web/ui/admin/admin-executor.js +0 -0
- package/myagent/web/ui/admin/admin-files.js +0 -0
- package/myagent/web/ui/admin/admin-llm.js +0 -0
- package/myagent/web/ui/admin/admin-logs.js +0 -0
- package/myagent/web/ui/admin/admin-memory.js +0 -0
- package/myagent/web/ui/admin/admin-org.js +0 -0
- package/myagent/web/ui/admin/admin-permissions.js +0 -0
- package/myagent/web/ui/admin/admin-platforms.js +0 -0
- package/myagent/web/ui/admin/admin-sessions.js +0 -0
- package/myagent/web/ui/admin/admin-skills.js +0 -0
- package/myagent/web/ui/admin/admin-system.js +0 -0
- package/myagent/web/ui/admin/admin-tasks.js +0 -0
- package/myagent/web/ui/chat/chat.css +2 -1
- package/myagent/web/ui/chat/groupchat.js +96 -14
- package/package.json +1 -1
- package/skills/fullstack-dev/SKILL.md +120 -952
- package/skills/pdf/SKILL.md +1 -7
- package/web/api_server.py +308 -0
- package/web/ui/admin/admin-agentchat.js +0 -0
- package/web/ui/admin/admin-agents.js +0 -0
- package/web/ui/admin/admin-core.js +0 -0
- package/web/ui/admin/admin-dashboard.js +0 -0
- package/web/ui/admin/admin-executor.js +0 -0
- package/web/ui/admin/admin-files.js +0 -0
- package/web/ui/admin/admin-llm.js +0 -0
- package/web/ui/admin/admin-logs.js +0 -0
- package/web/ui/admin/admin-memory.js +0 -0
- package/web/ui/admin/admin-org.js +0 -0
- package/web/ui/admin/admin-permissions.js +0 -0
- package/web/ui/admin/admin-platforms.js +0 -0
- package/web/ui/admin/admin-sessions.js +0 -0
- package/web/ui/admin/admin-skills.js +0 -0
- package/web/ui/admin/admin-system.js +0 -0
- package/web/ui/admin/admin-tasks.js +0 -0
- package/web/ui/chat/chat.css +2 -1
- package/web/ui/chat/groupchat.js +96 -14
- package/worklog.md +24 -0
package/core/agent_storage.py
CHANGED
|
File without changes
|
package/data/novnc/lib/base64.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/data/novnc/lib/des.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/data/novnc/lib/ra2.js
CHANGED
|
File without changes
|
package/data/novnc/lib/rfb.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/myagent/package.json
CHANGED
|
@@ -473,7 +473,11 @@ class ApiServer:
|
|
|
473
473
|
# ── 群消息 ──
|
|
474
474
|
r.add_get("/api/groups/{gid}/messages", self.handle_get_group_messages)
|
|
475
475
|
r.add_post("/api/groups/{gid}/messages", self.handle_send_group_message)
|
|
476
|
+
r.add_post("/api/groups/{gid}/messages/stream", self.handle_send_group_message_stream)
|
|
476
477
|
r.add_delete("/api/groups/{gid}/messages", self.handle_clear_group_messages)
|
|
478
|
+
# [v1.23.81] 群聊 session 管理(纯数字 ID,用于 URL 导航恢复)
|
|
479
|
+
r.add_get("/api/groups/{gid}/session", self.handle_get_group_session)
|
|
480
|
+
r.add_get("/api/group-session/{sid}", self.handle_resolve_group_session)
|
|
477
481
|
# [v1.23.37] Agent间私聊记录查询
|
|
478
482
|
r.add_get("/api/agent-chat/pairs", self.handle_get_agent_chat_pairs)
|
|
479
483
|
r.add_get("/api/agent-chat/messages", self.handle_get_agent_chat_messages)
|
|
@@ -4169,9 +4173,7 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4169
4173
|
return web.json_response({"error": str(e)}, status=500)
|
|
4170
4174
|
|
|
4171
4175
|
async def handle_list_sessions(self, request):
|
|
4172
|
-
if not self.core.memory:
|
|
4173
|
-
logger.warning("handle_list_sessions: self.core.memory is None, 无法查询会话")
|
|
4174
|
-
return web.json_response([])
|
|
4176
|
+
if not self.core.memory: return web.json_response([])
|
|
4175
4177
|
agent = request.query.get("agent", "")
|
|
4176
4178
|
# 只统计用户可见的消息数(排除 llm_output 和 conversation_insight)
|
|
4177
4179
|
if agent:
|
|
@@ -4189,13 +4191,6 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4189
4191
|
"AND key NOT IN ('llm_output', 'llm_input', 'tool_result_raw', 'conversation_insight') "
|
|
4190
4192
|
"GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
|
|
4191
4193
|
sessions = [{"id": r["session_id"], "messages": r["cnt"], "last": r["last"]} for r in rows]
|
|
4192
|
-
if not sessions:
|
|
4193
|
-
try:
|
|
4194
|
-
total = self.core.memory._get_conn().execute("SELECT COUNT(*) as cnt FROM memories WHERE category = 'session'").fetchone()
|
|
4195
|
-
total_all = self.core.memory._get_conn().execute("SELECT COUNT(*) as cnt FROM memories").fetchone()
|
|
4196
|
-
logger.debug(f"handle_list_sessions: 无会话. session记录: {total['cnt']}, 总: {total_all['cnt']}, agent: {agent or '(无)'}")
|
|
4197
|
-
except Exception:
|
|
4198
|
-
pass
|
|
4199
4194
|
# 批量获取自定义会话名称
|
|
4200
4195
|
sids = [s["id"] for s in sessions]
|
|
4201
4196
|
name_map = self.core.memory.list_session_names(sids) if sids else {}
|
|
@@ -7981,6 +7976,313 @@ window.addEventListener('beforeunload', function() {{
|
|
|
7981
7976
|
"task_id": task_id,
|
|
7982
7977
|
})
|
|
7983
7978
|
|
|
7979
|
+
async def handle_send_group_message_stream(self, request):
|
|
7980
|
+
"""POST /api/groups/{gid}/messages/stream - SSE 流式群聊
|
|
7981
|
+
|
|
7982
|
+
SSE 事件格式:
|
|
7983
|
+
data: {"type":"agent_start","agent_path":"...","name":"...","avatar":"...","agent_color":"..."}
|
|
7984
|
+
data: {"type":"text_delta","content":"...","agent_path":"..."}
|
|
7985
|
+
data: {"type":"agent_done","agent_path":"..."}
|
|
7986
|
+
data: {"type":"system_message","content":"..."}
|
|
7987
|
+
data: {"type":"done","task_id":"..."}
|
|
7988
|
+
data: {"type":"error","content":"..."}
|
|
7989
|
+
"""
|
|
7990
|
+
import re as _re
|
|
7991
|
+
import time as _time
|
|
7992
|
+
import asyncio
|
|
7993
|
+
|
|
7994
|
+
try:
|
|
7995
|
+
data = await request.json()
|
|
7996
|
+
except Exception:
|
|
7997
|
+
return web.Response(
|
|
7998
|
+
text='data: ' + json.dumps({"type": "error", "content": "invalid JSON"}) + '\n\n',
|
|
7999
|
+
content_type="text/event-stream",
|
|
8000
|
+
)
|
|
8001
|
+
|
|
8002
|
+
gid = request.match_info["gid"]
|
|
8003
|
+
content = data.get("message", "").strip()
|
|
8004
|
+
if not content:
|
|
8005
|
+
return web.Response(
|
|
8006
|
+
text='data: ' + json.dumps({"type": "error", "content": "消息不能为空"}) + '\n\n',
|
|
8007
|
+
content_type="text/event-stream",
|
|
8008
|
+
)
|
|
8009
|
+
|
|
8010
|
+
mgr = self._get_group_manager()
|
|
8011
|
+
group = mgr.get_group(gid)
|
|
8012
|
+
if not group:
|
|
8013
|
+
return web.Response(
|
|
8014
|
+
text='data: ' + json.dumps({"type": "error", "content": "群不存在"}) + '\n\n',
|
|
8015
|
+
content_type="text/event-stream",
|
|
8016
|
+
)
|
|
8017
|
+
|
|
8018
|
+
# 创建 SSE 流式响应
|
|
8019
|
+
response = web.StreamResponse(
|
|
8020
|
+
status=200,
|
|
8021
|
+
headers={
|
|
8022
|
+
"Content-Type": "text/event-stream",
|
|
8023
|
+
"Cache-Control": "no-cache",
|
|
8024
|
+
"Connection": "keep-alive",
|
|
8025
|
+
"X-Accel-Buffering": "no",
|
|
8026
|
+
}
|
|
8027
|
+
)
|
|
8028
|
+
await response.prepare(request)
|
|
8029
|
+
|
|
8030
|
+
client_connected = {"value": True}
|
|
8031
|
+
_write_lock = asyncio.Lock()
|
|
8032
|
+
|
|
8033
|
+
async def safe_write(obj):
|
|
8034
|
+
if not client_connected["value"]:
|
|
8035
|
+
return False
|
|
8036
|
+
try:
|
|
8037
|
+
async with _write_lock:
|
|
8038
|
+
await response.write(("data: " + json.dumps(obj, ensure_ascii=False) + "\n\n").encode())
|
|
8039
|
+
return True
|
|
8040
|
+
except Exception:
|
|
8041
|
+
client_connected["value"] = False
|
|
8042
|
+
return False
|
|
8043
|
+
|
|
8044
|
+
# 1. 保存用户消息
|
|
8045
|
+
from groups.manager import GroupMessage
|
|
8046
|
+
user_msg = GroupMessage(
|
|
8047
|
+
group_id=gid, sender="user", sender_name="用户",
|
|
8048
|
+
sender_avatar="👤", content=content,
|
|
8049
|
+
)
|
|
8050
|
+
mgr.add_message(user_msg)
|
|
8051
|
+
|
|
8052
|
+
# 2. 保存任务记录
|
|
8053
|
+
tp = self._get_task_persistence()
|
|
8054
|
+
from core.utils import generate_id
|
|
8055
|
+
task_id = generate_id("task")
|
|
8056
|
+
tp.save_task(
|
|
8057
|
+
task_id=task_id, description=content[:500],
|
|
8058
|
+
session_id=f"group_{gid}", group_id=gid,
|
|
8059
|
+
agent_path=", ".join(m.agent_path for m in group.members if not m.muted),
|
|
8060
|
+
status="running", metadata={"source": "group_chat", "group_name": group.name},
|
|
8061
|
+
)
|
|
8062
|
+
|
|
8063
|
+
# 3. 解析 @提及
|
|
8064
|
+
at_pattern = _re.compile(r'@(\S+)')
|
|
8065
|
+
at_targets = at_pattern.findall(content)
|
|
8066
|
+
mentioned_agents = set()
|
|
8067
|
+
mentioned_all = False
|
|
8068
|
+
for target in at_targets:
|
|
8069
|
+
target_lower = target.lower()
|
|
8070
|
+
if target_lower in ('all', '所有人', '大家'):
|
|
8071
|
+
mentioned_all = True
|
|
8072
|
+
break
|
|
8073
|
+
for m in group.members:
|
|
8074
|
+
agent_cfg = self._read_agent_config(m.agent_path)
|
|
8075
|
+
agent_name = agent_cfg.get("name", "") if agent_cfg else ""
|
|
8076
|
+
if (target_lower == m.agent_path.lower()
|
|
8077
|
+
or target_lower == agent_name.lower()
|
|
8078
|
+
or (m.nickname and target_lower == m.nickname.lower())):
|
|
8079
|
+
mentioned_agents.add(m.agent_path)
|
|
8080
|
+
|
|
8081
|
+
active_members = [m for m in group.members if not m.muted]
|
|
8082
|
+
if mentioned_all:
|
|
8083
|
+
target_members = active_members
|
|
8084
|
+
elif mentioned_agents:
|
|
8085
|
+
target_members = [m for m in active_members if m.agent_path in mentioned_agents]
|
|
8086
|
+
else:
|
|
8087
|
+
target_members = active_members
|
|
8088
|
+
|
|
8089
|
+
# 4. 串行处理每个 agent,流式输出
|
|
8090
|
+
_chat_msg_pattern = _re.compile(r'__CHAT_AGENT__(.+?)\|(.+?)\|(.+?)__END__')
|
|
8091
|
+
final_responses = []
|
|
8092
|
+
|
|
8093
|
+
for member in target_members:
|
|
8094
|
+
agent_path = member.agent_path
|
|
8095
|
+
agent_cfg = self._read_agent_config(agent_path)
|
|
8096
|
+
|
|
8097
|
+
# 获取显示信息
|
|
8098
|
+
avatar = "🤖"
|
|
8099
|
+
display_name = agent_path
|
|
8100
|
+
agent_color = "var(--accent)"
|
|
8101
|
+
if agent_cfg:
|
|
8102
|
+
avatar = agent_cfg.get("avatar_emoji", "🤖") or "🤖"
|
|
8103
|
+
display_name = agent_cfg.get("name", agent_path)
|
|
8104
|
+
agent_color = agent_cfg.get("avatar_color", "var(--accent)")
|
|
8105
|
+
|
|
8106
|
+
# 通知前端:新 agent 开始回复
|
|
8107
|
+
await safe_write({
|
|
8108
|
+
"type": "agent_start",
|
|
8109
|
+
"agent_path": agent_path,
|
|
8110
|
+
"name": display_name,
|
|
8111
|
+
"avatar": avatar,
|
|
8112
|
+
"agent_color": agent_color,
|
|
8113
|
+
})
|
|
8114
|
+
|
|
8115
|
+
try:
|
|
8116
|
+
model_chain = self._build_model_chain(agent_cfg, agent_path)
|
|
8117
|
+
session_id = f"group_{gid}_{agent_path}"
|
|
8118
|
+
|
|
8119
|
+
_, agent_system_prompt = self._build_agent_chat_context(agent_path, agent_cfg, content)
|
|
8120
|
+
|
|
8121
|
+
# 构建群聊上下文(与 handle_send_group_message 完全一致)
|
|
8122
|
+
member_lines = []
|
|
8123
|
+
for m in group.members:
|
|
8124
|
+
mc = self._read_agent_config(m.agent_path)
|
|
8125
|
+
m_name = mc.get("name", m.agent_path) if mc else m.agent_path
|
|
8126
|
+
m_id = mc.get("id", "") if mc else ""
|
|
8127
|
+
m_desc = mc.get("description", "") if mc else ""
|
|
8128
|
+
role_label = {"owner": "群主", "admin": "管理员"}.get(m.role, "成员")
|
|
8129
|
+
nick = f"(昵称: {m.nickname})" if m.nickname else ""
|
|
8130
|
+
line = f" - {m_name} [ID: {m_id}] ({role_label})"
|
|
8131
|
+
if m_desc: line += f" — {m_desc}"
|
|
8132
|
+
line += nick
|
|
8133
|
+
if m.muted: line += "(已禁言)"
|
|
8134
|
+
member_lines.append(line)
|
|
8135
|
+
|
|
8136
|
+
my_name = agent_cfg.get("name", agent_path) if agent_cfg else agent_path
|
|
8137
|
+
my_role = {"owner": "群主", "admin": "管理员"}.get(member.role, "成员")
|
|
8138
|
+
|
|
8139
|
+
recent_msgs = mgr.get_messages(gid, limit=10)
|
|
8140
|
+
history_lines = []
|
|
8141
|
+
for rm in recent_msgs:
|
|
8142
|
+
rm_name = rm.sender_name or ("用户" if rm.sender == "user" else rm.agent_path)
|
|
8143
|
+
rm_time_str = _time.strftime("%H:%M", _time.localtime(rm.timestamp)) if rm.timestamp else ""
|
|
8144
|
+
history_lines.append(f"[{rm_time_str}] {rm_name}: {rm.content[:200]}")
|
|
8145
|
+
chat_history = "\n".join(reversed(history_lines))
|
|
8146
|
+
|
|
8147
|
+
is_mentioned = mentioned_all or member.agent_path in mentioned_agents
|
|
8148
|
+
at_info = ""
|
|
8149
|
+
if mentioned_all:
|
|
8150
|
+
at_info = "- 本次消息使用了 @所有人,请务必回复。\n"
|
|
8151
|
+
elif mentioned_agents:
|
|
8152
|
+
if is_mentioned:
|
|
8153
|
+
at_info = f"- 本次消息使用了 @{my_name},请务必回复。\n"
|
|
8154
|
+
else:
|
|
8155
|
+
at_info = "- 本次消息使用了 @其他成员,与你无关,请不要回复。\n"
|
|
8156
|
+
else:
|
|
8157
|
+
at_info = "- 本次消息没有 @任何人(广播消息),你可以根据内容决定是否回复。\n"
|
|
8158
|
+
|
|
8159
|
+
group_context = f"""## 群聊上下文
|
|
8160
|
+
|
|
8161
|
+
你正在参与一个群聊,以下是群的详细信息:
|
|
8162
|
+
|
|
8163
|
+
### 群信息
|
|
8164
|
+
- 群名称: {group.name}
|
|
8165
|
+
- 群描述: {group.description or '无'}
|
|
8166
|
+
- 当前发言者: 用户(群聊中的真人用户)
|
|
8167
|
+
|
|
8168
|
+
### 你的身份
|
|
8169
|
+
- 名称: {my_name}
|
|
8170
|
+
- 路径: {agent_path}
|
|
8171
|
+
- 角色: {my_role}{"(已禁言,但仍可接收消息)" if member.muted else ""}
|
|
8172
|
+
|
|
8173
|
+
### 群成员列表(共{len(group.members)}人)
|
|
8174
|
+
{chr(10).join(member_lines)}
|
|
8175
|
+
|
|
8176
|
+
### 沟通规则(重要)
|
|
8177
|
+
1. **@提及机制**: 用户发送消息时可以使用 @名称 来指定回复者
|
|
8178
|
+
- @某个Agent: 只有被@的Agent需要回复
|
|
8179
|
+
- @所有人 / @all: 所有成员都需要回复
|
|
8180
|
+
- 不@任何人(广播): 你自行判断是否需要回复
|
|
8181
|
+
2. **跨Agent私下沟通**: 你可以使用 `myagent-ai chat --agent <ID> --message "消息"` 命令向群内其他Agent发送私下消息。
|
|
8182
|
+
- **必须使用Agent的ID**,不是名字。从上方「群成员列表」中找到目标Agent的 `[ID: xxx]` 就是它的唯一ID
|
|
8183
|
+
- 正确示例: `myagent-ai chat --agent a1b2c3d4e5f6 --message "你好"`
|
|
8184
|
+
- 私下沟通的内容不会直接显示给用户,适合讨论细节、交换数据、协调方案
|
|
8185
|
+
- 当任务需要多个Agent协作时,应该先在群里讨论分工,然后私下沟通具体细节
|
|
8186
|
+
3. **协作分工模式**(复杂任务):
|
|
8187
|
+
- 部长/管理员应主动分析任务,在群里提出分工方案(谁负责什么)
|
|
8188
|
+
- 其他成员应积极响应,认领自己擅长的部分
|
|
8189
|
+
- 分工确定后,各自私下沟通需要协调的细节
|
|
8190
|
+
- 部长/管理员负责在群里汇总进展,向用户汇报阶段性成果和最终结果
|
|
8191
|
+
- 私下沟通的详细过程不需要在群里展示,只汇报关键进展和最终结论
|
|
8192
|
+
{at_info}
|
|
8193
|
+
### 近期群聊记录(最近10条)
|
|
8194
|
+
{chat_history if chat_history else '(暂无历史消息)'}
|
|
8195
|
+
|
|
8196
|
+
### 重要提醒
|
|
8197
|
+
- 你只代表你自己发言,使用第一人称
|
|
8198
|
+
- 不要假装是其他Agent或代替其他Agent回答
|
|
8199
|
+
- 如果问题超出你的能力范围,建议用户@相关专家Agent
|
|
8200
|
+
- 如果需要其他Agent的信息,使用 `myagent-ai chat --agent <ID>` 命令私下沟通(使用Agent的ID,从群成员列表的[ID: xxx]中获取)
|
|
8201
|
+
|
|
8202
|
+
### 能力提醒(关键)
|
|
8203
|
+
- 你拥有完整的工具调用能力(搜索、文件操作、代码执行、图片生成等)
|
|
8204
|
+
- 在群聊中,你应该像在1对1对话中一样主动使用工具来完成用户的请求
|
|
8205
|
+
- 不要因为身处群聊就只做简单文字回复,该用工具时一定要用"""
|
|
8206
|
+
|
|
8207
|
+
if agent_system_prompt:
|
|
8208
|
+
agent_system_prompt += "\n\n" + group_context
|
|
8209
|
+
else:
|
|
8210
|
+
agent_system_prompt = group_context
|
|
8211
|
+
|
|
8212
|
+
dept_context = self._build_dept_context(gid, agent_path)
|
|
8213
|
+
if dept_context:
|
|
8214
|
+
agent_system_prompt += "\n\n" + dept_context
|
|
8215
|
+
|
|
8216
|
+
agent_content = content
|
|
8217
|
+
|
|
8218
|
+
# 调用 LLM 获取回复
|
|
8219
|
+
if model_chain and self.core.llm:
|
|
8220
|
+
response_text = await self._try_model_chain_inner(
|
|
8221
|
+
model_chain, agent_content, session_id,
|
|
8222
|
+
agent_path=agent_path, agent_system_prompt=agent_system_prompt,
|
|
8223
|
+
)
|
|
8224
|
+
else:
|
|
8225
|
+
response_text = await self.core.process_message(agent_content, session_id)
|
|
8226
|
+
|
|
8227
|
+
# 流式输出该 agent 的回复文本(逐块发送)
|
|
8228
|
+
await self._stream_text_chunked(response_text, safe_write, chunk_size=6, delay=0.01)
|
|
8229
|
+
|
|
8230
|
+
# 处理跨 Agent 私聊标记
|
|
8231
|
+
chat_matches = _chat_msg_pattern.findall(response_text)
|
|
8232
|
+
if chat_matches:
|
|
8233
|
+
response_text = _chat_msg_pattern.sub('', response_text).strip()
|
|
8234
|
+
for c_path, c_name, c_msg in chat_matches:
|
|
8235
|
+
c_path_s, c_name_s, c_msg_s = c_path.strip(), c_name.strip(), c_msg.strip()
|
|
8236
|
+
chat_sys_msg = GroupMessage(
|
|
8237
|
+
group_id=gid, sender=agent_path,
|
|
8238
|
+
sender_name=display_name, sender_avatar=avatar,
|
|
8239
|
+
content=f"💬 私下与 {c_name_s} 沟通中...",
|
|
8240
|
+
)
|
|
8241
|
+
mgr.add_message(chat_sys_msg)
|
|
8242
|
+
try:
|
|
8243
|
+
mgr.add_agent_chat(
|
|
8244
|
+
group_id=gid, from_agent=agent_path,
|
|
8245
|
+
from_name=display_name, to_agent=c_path_s,
|
|
8246
|
+
to_name=c_name_s, content=c_msg_s,
|
|
8247
|
+
)
|
|
8248
|
+
except Exception as ce:
|
|
8249
|
+
logger.debug(f"保存私聊记录失败: {ce}")
|
|
8250
|
+
|
|
8251
|
+
# 保存到数据库
|
|
8252
|
+
agent_msg = GroupMessage(
|
|
8253
|
+
group_id=gid, sender="agent",
|
|
8254
|
+
sender_name=display_name, sender_avatar=avatar,
|
|
8255
|
+
content=response_text, agent_path=agent_path,
|
|
8256
|
+
)
|
|
8257
|
+
mgr.add_message(agent_msg)
|
|
8258
|
+
|
|
8259
|
+
final_responses.append({
|
|
8260
|
+
"ok": True, "agent_path": agent_path,
|
|
8261
|
+
"name": display_name, "avatar": avatar,
|
|
8262
|
+
"agent_color": agent_color,
|
|
8263
|
+
"response": response_text,
|
|
8264
|
+
})
|
|
8265
|
+
|
|
8266
|
+
except Exception as e:
|
|
8267
|
+
logger.error(f"群消息处理失败 ({agent_path}): {e}")
|
|
8268
|
+
await safe_write({"type": "text_delta", "content": f"处理失败: {str(e)}", "agent_path": agent_path})
|
|
8269
|
+
final_responses.append({
|
|
8270
|
+
"ok": False, "agent_path": agent_path,
|
|
8271
|
+
"name": agent_path, "avatar": "❌",
|
|
8272
|
+
"response": f"处理失败: {str(e)}",
|
|
8273
|
+
})
|
|
8274
|
+
|
|
8275
|
+
# 通知前端:该 agent 回复完成
|
|
8276
|
+
await safe_write({"type": "agent_done", "agent_path": agent_path})
|
|
8277
|
+
|
|
8278
|
+
# 更新任务状态
|
|
8279
|
+
has_failure = any(not r.get("ok") for r in final_responses)
|
|
8280
|
+
tp.update_task_status(task_id, "failed" if has_failure else "completed", last_message=content[:500])
|
|
8281
|
+
|
|
8282
|
+
# 通知前端:全部完成
|
|
8283
|
+
await safe_write({"type": "done", "task_id": task_id, "group_id": gid})
|
|
8284
|
+
return response
|
|
8285
|
+
|
|
7984
8286
|
async def handle_clear_group_messages(self, request):
|
|
7985
8287
|
"""DELETE /api/groups/{gid}/messages - 清空群消息"""
|
|
7986
8288
|
gid = request.match_info["gid"]
|
|
@@ -7988,6 +8290,30 @@ window.addEventListener('beforeunload', function() {{
|
|
|
7988
8290
|
ok = mgr.clear_messages(gid)
|
|
7989
8291
|
return web.json_response({"ok": ok})
|
|
7990
8292
|
|
|
8293
|
+
# ── [v1.23.81] 群聊 Session API ──
|
|
8294
|
+
|
|
8295
|
+
async def handle_get_group_session(self, request):
|
|
8296
|
+
"""GET /api/groups/{gid}/session - 获取或创建群聊的 session ID(纯数字)"""
|
|
8297
|
+
gid = request.match_info["gid"]
|
|
8298
|
+
mgr = self._get_group_manager()
|
|
8299
|
+
group = mgr.get_group(gid)
|
|
8300
|
+
if not group:
|
|
8301
|
+
return web.json_response({"error": "群不存在"}, status=404)
|
|
8302
|
+
session_id = mgr.get_or_create_group_session(gid)
|
|
8303
|
+
return web.json_response({"session_id": session_id, "group_id": gid})
|
|
8304
|
+
|
|
8305
|
+
async def handle_resolve_group_session(self, request):
|
|
8306
|
+
"""GET /api/group-session/{sid} - 通过 session ID 查找 group_id(用于 URL 恢复)"""
|
|
8307
|
+
sid = request.match_info["sid"]
|
|
8308
|
+
mgr = self._get_group_manager()
|
|
8309
|
+
group_id = mgr.get_group_id_by_session(sid)
|
|
8310
|
+
if not group_id:
|
|
8311
|
+
return web.json_response({"error": "session 不存在"}, status=404)
|
|
8312
|
+
group = mgr.get_group(group_id)
|
|
8313
|
+
if not group:
|
|
8314
|
+
return web.json_response({"error": "群已不存在"}, status=404)
|
|
8315
|
+
return web.json_response({"session_id": sid, "group_id": group_id, "group": self._enrich_group_dict(group.to_dict())})
|
|
8316
|
+
|
|
7991
8317
|
# ── [v1.23.37] Agent间私聊记录 API ──
|
|
7992
8318
|
|
|
7993
8319
|
async def handle_get_agent_chat_pairs(self, request):
|
|
File without changes
|