feat: kanban session routing for xxm + xiaoguo
This commit is contained in:
+49
-6
@@ -65,6 +65,7 @@ AGENTS = {
|
||||
"http_port": 5806,
|
||||
"gateway": "http://localhost:8645/v1/chat/completions",
|
||||
"session_id": "xmpp-xiaoguo",
|
||||
"kanban_session_id": "xmpp-xiaoguo-kanban",
|
||||
"server": "127.0.0.1",
|
||||
"port": 5222,
|
||||
"muc_rooms": ["coregroup@conference.yoin.fun"],
|
||||
@@ -78,6 +79,7 @@ AGENTS = {
|
||||
"http_port": 5802,
|
||||
"bridge": "chat_bridge", # use local chat_bridge instead of Hermes API
|
||||
"session_id": "ses_xxm_xmpp",
|
||||
"kanban_session_id": "xmpp-xxm-kanban",
|
||||
"server": "192.168.1.246", # LAN direct connect
|
||||
"port": 5222,
|
||||
"muc_rooms": [
|
||||
@@ -130,27 +132,61 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
|
||||
_IS_CHAT_BRIDGE = cfg.get("bridge") == "chat_bridge"
|
||||
_router = None # set only for chat_bridge (xxm)
|
||||
|
||||
# ── Kanban session support ──
|
||||
_CALL_SEQ = 0
|
||||
_KANBAN_SESSION_ID = cfg.get("kanban_session_id", None)
|
||||
|
||||
if _IS_CHAT_BRIDGE:
|
||||
from chat_bridge import SessionBridge
|
||||
from session_router import SessionRouter
|
||||
_bridge = SessionBridge(session_id=cfg["session_id"])
|
||||
_router = SessionRouter(bridge=_bridge, default_session=cfg["session_id"])
|
||||
# Kanban-dedicated bridge + router for separate session
|
||||
_kanban_sid = _KANBAN_SESSION_ID or f"{cfg['session_id']}-kanban"
|
||||
_kanban_bridge = SessionBridge(session_id=_kanban_sid)
|
||||
_kanban_router = SessionRouter(bridge=_kanban_bridge, default_session=_kanban_sid)
|
||||
log(f"LLM: chat_bridge (session={cfg['session_id']})")
|
||||
log(f"Kanban: chat_bridge (session={_kanban_sid})")
|
||||
else:
|
||||
_opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||
log(f"LLM: Hermes API ({cfg['gateway']})")
|
||||
|
||||
|
||||
def _call_llm(content: str, sender: str, is_group: bool = False) -> str:
|
||||
"""Abstract LLM call. Returns raw response text (or empty string)."""
|
||||
def _call_llm(content: str, sender: str, is_group: bool = False,
|
||||
session_id: str | None = None) -> str:
|
||||
"""Abstract LLM call. Returns raw response text (or empty string).
|
||||
If session_id provided and differs from default, route to kanban handler."""
|
||||
if _IS_CHAT_BRIDGE:
|
||||
if session_id and session_id != cfg["session_id"]:
|
||||
# Prepends kanban-handler instructions so LLM knows how to handle
|
||||
kanban_content = (
|
||||
"【看板处理协议】\n"
|
||||
"收到卡片后三步判断:\n"
|
||||
" A) 任务明确 → 直接执行 → 评论结果 + 更新状态\n"
|
||||
" B) 信息不足 → 评论提问 + 设为 blocked\n"
|
||||
" C) 不是我的活 → 评论说明 + 转派(如果能判断)\n"
|
||||
"\n"
|
||||
"汇报规则:\n"
|
||||
" - 任务完成 → 简短 DM 给老莫摘要\n"
|
||||
" - 评论/状态变更 → 不汇报\n"
|
||||
" - 追问/转派 → 不汇报\n"
|
||||
"\n"
|
||||
"可用 API:\n"
|
||||
" curl http://192.168.1.246:5803/api/kanban/t_xxx 查看卡片详情\n"
|
||||
" 更新操作走 Kanban Dashboard UI\n"
|
||||
"\n"
|
||||
"卡片上下文不够?→ 用 session_search 查历史\n"
|
||||
f"---\n{content}"
|
||||
)
|
||||
return _kanban_router.route("xmpp", sender, kanban_content) or ""
|
||||
return _router.route("xmpp", sender, content) or ""
|
||||
else:
|
||||
return _call_hermes_api(content)
|
||||
return _call_hermes_api(content, session_id)
|
||||
|
||||
|
||||
def _call_hermes_api(content: str) -> str:
|
||||
def _call_hermes_api(content: str, session_id: str | None = None) -> str:
|
||||
"""POST to Hermes API, return response text or empty string."""
|
||||
target_sid = session_id or cfg["session_id"]
|
||||
try:
|
||||
payload = json.dumps({
|
||||
"model": "hermes-agent",
|
||||
@@ -159,7 +195,7 @@ def _call_hermes_api(content: str) -> str:
|
||||
req = urllib.request.Request(cfg["gateway"], data=payload, method="POST")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
req.add_header("Authorization", "Bearer hermes123")
|
||||
req.add_header("X-Hermes-Session-Id", cfg["session_id"])
|
||||
req.add_header("X-Hermes-Session-Id", target_sid)
|
||||
result = _opener.open(req, timeout=600)
|
||||
data = json.loads(result.read())
|
||||
reply = data.get("choices", [{}])[0].get("message", {}).get("content", "")
|
||||
@@ -692,6 +728,7 @@ def _handle_group_message(msg):
|
||||
|
||||
def _handle_private_message(msg):
|
||||
"""Process a private chat message."""
|
||||
global _CALL_SEQ
|
||||
if msg["type"] == "groupchat":
|
||||
return
|
||||
msg_id = msg.get("id", "")
|
||||
@@ -708,7 +745,13 @@ def _handle_private_message(msg):
|
||||
return
|
||||
if _check_shutup(body):
|
||||
return
|
||||
raw = _call_llm(body, sender, is_group=False)
|
||||
# ── Kanban routing ──
|
||||
_CALL_SEQ += 1
|
||||
is_kanban = body.startswith('[Kanban]')
|
||||
target_sid = _KANBAN_SESSION_ID if is_kanban else cfg["session_id"]
|
||||
if is_kanban:
|
||||
log(f"📋 看板通知(#{_CALL_SEQ}): {body[:80]}")
|
||||
raw = _call_llm(body, sender, is_group=False, session_id=target_sid)
|
||||
if raw:
|
||||
reply = _extract_response(raw)
|
||||
if reply:
|
||||
|
||||
Reference in New Issue
Block a user