mirror of
https://mirror.skon.top/github.com/cft0808/edict
synced 2026-04-21 13:20:34 +08:00
feat: Agent 在线状态检测 + 唤醒 + 前置校验
server.py: - 新增 get_agents_status(): 检测 Gateway 进程/probe + 各 Agent 状态 - 读取 sessions.json 判断最后活跃时间 → running/idle/offline/unconfigured - 检测 openclaw-agent 进程、workspace 是否存在 - Gateway HTTP probe (ws://127.0.0.1:18789) - 新增 wake_agent(): 通过 openclaw agent --agent 发送唤醒消息 - 新增 /api/agents-status GET 端点 - 新增 /api/agent-wake POST 端点 - dispatch_to_agent 前置 Gateway 存活检查 dashboard.html: - 省部调度 Tab 新增 Agent 状态面板 - 12个 Agent 卡片,实时显示在线状态 (绿/黄/灰/红) - Gateway 状态指示器 - 单个唤醒按钮 + 全部唤醒按钮 - 运行中/待命/离线/未配置 汇总统计 - 切换到省部调度 Tab 自动加载状态 - 下旨前前置检测 Gateway 是否在线,不在线时警告
This commit is contained in:
@@ -361,6 +361,37 @@
|
||||
.toast.ok{border-color:#2ecc8a55;background:#0a1a10}.toast.err{border-color:#ff527055;background:#200a10}
|
||||
@keyframes tin{from{transform:translateX(40px);opacity:0}to{transform:translateX(0);opacity:1}}
|
||||
|
||||
/* ══ AGENT STATUS PANEL ══ */
|
||||
.as-panel{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:14px 18px;margin-bottom:16px}
|
||||
.as-header{display:flex;align-items:center;gap:10px;margin-bottom:12px}
|
||||
.as-title{font-size:13px;font-weight:700;color:var(--fg)}
|
||||
.as-gw{font-size:11px;padding:3px 10px;border-radius:999px;margin-left:auto}
|
||||
.as-gw.ok{background:#0a2018;border:1px solid #2ecc8a44;color:var(--ok)}
|
||||
.as-gw.err{background:#200a10;border:1px solid #ff527044;color:var(--danger)}
|
||||
.as-gw.warn{background:#201a08;border:1px solid #f5c84244;color:var(--warn)}
|
||||
.as-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:8px}
|
||||
.as-card{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:10px;text-align:center;cursor:pointer;transition:border-color .15s,background .15s;position:relative}
|
||||
.as-card:hover{border-color:var(--acc);background:#0a1228}
|
||||
.as-card .as-emoji{font-size:22px;margin-bottom:3px}
|
||||
.as-card .as-label{font-size:12px;font-weight:700;color:var(--fg)}
|
||||
.as-card .as-role{font-size:10px;color:var(--muted)}
|
||||
.as-card .as-status{font-size:10px;margin-top:4px}
|
||||
.as-card .as-time{font-size:9px;color:var(--muted);margin-top:2px}
|
||||
.as-card .as-dot{position:absolute;top:6px;right:6px;width:8px;height:8px;border-radius:50%}
|
||||
.as-dot.running{background:#2ecc8a;box-shadow:0 0 6px #2ecc8a88;animation:pulse 1.5s infinite}
|
||||
.as-dot.idle{background:#4a5568}
|
||||
.as-dot.offline{background:#ff5270;animation:pulse 1.2s infinite}
|
||||
.as-dot.unconfigured{background:#6b7280}
|
||||
.as-wake-btn{font-size:10px;padding:2px 8px;border-radius:6px;border:1px solid var(--acc);color:var(--acc);background:transparent;cursor:pointer;margin-top:6px;transition:background .15s}
|
||||
.as-wake-btn:hover{background:var(--acc);color:#fff}
|
||||
.as-wake-btn:disabled{opacity:.4;cursor:not-allowed}
|
||||
.as-refresh{font-size:11px;padding:4px 12px;border-radius:8px;border:1px solid var(--line);color:var(--muted);background:transparent;cursor:pointer;transition:background .15s}
|
||||
.as-refresh:hover{background:var(--panel2);color:var(--fg)}
|
||||
.as-wake-all{font-size:11px;padding:4px 12px;border-radius:8px;border:1px solid var(--warn);color:var(--warn);background:transparent;cursor:pointer;transition:background .15s;margin-left:6px}
|
||||
.as-wake-all:hover{background:var(--warn);color:#fff}
|
||||
.as-summary{font-size:11px;color:var(--muted);display:flex;gap:12px;margin-top:10px;padding-top:8px;border-top:1px solid var(--line)}
|
||||
.as-summary span{display:flex;align-items:center;gap:4px}
|
||||
|
||||
/* ══ OFFICIALS ══ */
|
||||
/* activity bar */
|
||||
.off-activity{display:flex;align-items:center;gap:8px;padding:8px 14px;background:#0a1228;border:1px solid #1a2a4a;border-radius:10px;margin-bottom:14px;font-size:12px;flex-wrap:wrap}
|
||||
@@ -623,6 +654,7 @@
|
||||
|
||||
<!-- ══ 省部调度 ══ -->
|
||||
<div class="panel" id="panel-monitor">
|
||||
<div id="agent-status-panel"></div>
|
||||
<div style="font-size:12px;color:var(--muted);margin-bottom:14px">各省部当前承接旨意与执行状态 · 每5秒自动刷新</div>
|
||||
<div class="duty-grid" id="duty-grid"></div>
|
||||
</div>
|
||||
@@ -922,6 +954,96 @@ function renderEdicts(tasks){
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/* ══ AGENT STATUS PANEL ══ */
|
||||
let _agentsStatusData = null;
|
||||
let _agentsStatusLoading = false;
|
||||
|
||||
async function loadAgentsStatus(){
|
||||
if(_agentsStatusLoading) return;
|
||||
_agentsStatusLoading = true;
|
||||
try{
|
||||
_agentsStatusData = await fetchJ(API+'/agents-status');
|
||||
renderAgentsStatus(_agentsStatusData);
|
||||
}catch(e){
|
||||
document.getElementById('agent-status-panel').innerHTML=
|
||||
'<div class="as-panel"><div style="color:var(--danger);font-size:12px">⚠️ 无法获取 Agent 状态</div></div>';
|
||||
}finally{
|
||||
_agentsStatusLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function renderAgentsStatus(data){
|
||||
if(!data||!data.ok) return;
|
||||
const gw = data.gateway||{};
|
||||
const agents = data.agents||[];
|
||||
// 跳过 main(和 taizi 重复)
|
||||
const filtered = agents.filter(a=>a.id!=='main');
|
||||
const running = filtered.filter(a=>a.status==='running').length;
|
||||
const idle = filtered.filter(a=>a.status==='idle').length;
|
||||
const offline = filtered.filter(a=>a.status==='offline').length;
|
||||
const unconf = filtered.filter(a=>a.status==='unconfigured').length;
|
||||
const gwCls = gw.probe?'ok':gw.alive?'warn':'err';
|
||||
|
||||
document.getElementById('agent-status-panel').innerHTML=`
|
||||
<div class="as-panel">
|
||||
<div class="as-header">
|
||||
<span class="as-title">🔌 Agent 在线状态</span>
|
||||
<span class="as-gw ${gwCls}">Gateway: ${esc(gw.status||'未知')}</span>
|
||||
<button class="as-refresh" onclick="loadAgentsStatus()" title="刷新状态">🔄 刷新</button>
|
||||
${offline+unconf>0?`<button class="as-wake-all" onclick="wakeAllAgents()" title="唤醒所有离线Agent">⚡ 全部唤醒</button>`:''}
|
||||
</div>
|
||||
<div class="as-grid">
|
||||
${filtered.map(a=>{
|
||||
const dotCls = a.status;
|
||||
const canWake = a.status!=='running' && a.status!=='unconfigured' && gw.alive;
|
||||
return `<div class="as-card" title="${esc(a.role)} · ${esc(a.statusLabel)}">
|
||||
<div class="as-dot ${dotCls}"></div>
|
||||
<div class="as-emoji">${a.emoji}</div>
|
||||
<div class="as-label">${esc(a.label)}</div>
|
||||
<div class="as-role">${esc(a.role)}</div>
|
||||
<div class="as-status">${esc(a.statusLabel)}</div>
|
||||
${a.lastActive?`<div class="as-time">⏰ ${esc(a.lastActive)}</div>`:'<div class="as-time">无活动记录</div>'}
|
||||
${canWake?`<button class="as-wake-btn" onclick="event.stopPropagation();wakeAgent('${a.id}',this)">⚡ 唤醒</button>`:''}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
<div class="as-summary">
|
||||
<span><span class="as-dot running" style="position:static;width:8px;height:8px"></span> ${running} 运行中</span>
|
||||
<span><span class="as-dot idle" style="position:static;width:8px;height:8px"></span> ${idle} 待命</span>
|
||||
${offline?`<span><span class="as-dot offline" style="position:static;width:8px;height:8px"></span> ${offline} 离线</span>`:''}
|
||||
${unconf?`<span><span class="as-dot unconfigured" style="position:static;width:8px;height:8px"></span> ${unconf} 未配置</span>`:''}
|
||||
<span style="margin-left:auto;font-size:10px;color:var(--muted)">检测于 ${(data.checkedAt||'').substring(11,19)}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function wakeAgent(agentId, btn){
|
||||
if(btn){ btn.disabled=true; btn.textContent='⏳ 唤醒中...'; }
|
||||
try{
|
||||
const r = await postJ(API+'/agent-wake', {agentId});
|
||||
toast(r.message||'唤醒指令已发出','ok');
|
||||
// 30秒后自动刷新状态
|
||||
setTimeout(()=>loadAgentsStatus(), 30000);
|
||||
}catch(e){
|
||||
toast('唤醒失败: '+e.message,'err');
|
||||
if(btn){ btn.disabled=false; btn.textContent='⚡ 唤醒'; }
|
||||
}
|
||||
}
|
||||
|
||||
async function wakeAllAgents(){
|
||||
if(!_agentsStatusData) return;
|
||||
const toWake = (_agentsStatusData.agents||[]).filter(a=>
|
||||
a.id!=='main' && a.status!=='running' && a.status!=='unconfigured'
|
||||
);
|
||||
if(!toWake.length){ toast('所有 Agent 均已在线','ok'); return; }
|
||||
toast(`正在唤醒 ${toWake.length} 个 Agent...`,'ok',5000);
|
||||
for(const a of toWake){
|
||||
try{ await postJ(API+'/agent-wake', {agentId: a.id}); }catch(e){}
|
||||
}
|
||||
toast(`${toWake.length} 个唤醒指令已发出,30秒后刷新状态`,'ok',5000);
|
||||
setTimeout(()=>loadAgentsStatus(), 30000);
|
||||
}
|
||||
|
||||
/* ══ DEPT MONITOR ══ */
|
||||
function renderMonitor(tasks){
|
||||
const DEPTS = [
|
||||
@@ -2261,6 +2383,7 @@ document.querySelectorAll('.tab').forEach(t=>t.addEventListener('click',()=>{
|
||||
document.getElementById('panel-'+activeTab).classList.add('active');
|
||||
if(['models','skills'].includes(activeTab))loadAgentConfig();
|
||||
if(activeTab==='officials')loadOfficials();
|
||||
if(activeTab==='monitor')loadAgentsStatus();
|
||||
if(activeTab==='morning')loadMorning();
|
||||
if(activeTab==='sessions')renderSessions(liveStatus?liveStatus.tasks:[]);
|
||||
if(activeTab==='memorials')renderMemorials();
|
||||
@@ -2617,6 +2740,16 @@ async function executeTemplate(e, tplId){
|
||||
const cmd = buildCommand(tplId);
|
||||
if(!cmd.trim()){ toast('请填写必填参数','err'); return; }
|
||||
const t = TEMPLATES.find(x=>x.id===tplId);
|
||||
|
||||
// 前置校验:检测 Gateway 和关键 Agent 是否在线
|
||||
try{
|
||||
const st = await fetchJ(API+'/agents-status');
|
||||
if(st.ok && st.gateway && !st.gateway.alive){
|
||||
toast('⚠️ Gateway 未启动,任务将无法派发!请先运行 openclaw gateway start','err',6000);
|
||||
if(!confirm('Gateway 未启动,任务创建后将无法自动派发。是否仍然继续?')) return;
|
||||
}
|
||||
}catch(ex){/* 检测失败不阻塞创建 */}
|
||||
|
||||
// Show the generated command in a confirmation
|
||||
if(!confirm(`确认下旨?\n\n${cmd.substring(0,200)}${cmd.length>200?'…':''}`)) return;
|
||||
// 通过 API 创建真实任务
|
||||
|
||||
@@ -325,6 +325,10 @@ def handle_create_task(title, org='中书省', official='中书令', priority='n
|
||||
# 发送给 main (太子) 而不是 zhongshu,让太子走正常流程分拣→中书省
|
||||
def dispatch_to_agent():
|
||||
try:
|
||||
# 前置检查 Gateway 是否在线
|
||||
if not _check_gateway_alive():
|
||||
log.warning(f'⚠️ {task_id} 派发跳过: Gateway 未启动')
|
||||
return
|
||||
msg = (
|
||||
f'📜 皇上新旨意(已录入看板,请直接处理)\n'
|
||||
f'任务ID: {task_id}\n'
|
||||
@@ -390,6 +394,204 @@ def handle_review_action(task_id, action, comment=''):
|
||||
return {'ok': True, 'message': f'{task_id} {label}'}
|
||||
|
||||
|
||||
# ══ Agent 在线状态检测 ══
|
||||
|
||||
_AGENT_DEPTS = [
|
||||
{'id':'main', 'label':'太子', 'emoji':'🤴', 'role':'太子', 'rank':'储君'},
|
||||
{'id':'taizi', 'label':'太子', 'emoji':'🤴', 'role':'太子', 'rank':'储君'},
|
||||
{'id':'zhongshu','label':'中书省','emoji':'📜', 'role':'中书令', 'rank':'正一品'},
|
||||
{'id':'menxia', 'label':'门下省','emoji':'🔍', 'role':'侍中', 'rank':'正一品'},
|
||||
{'id':'shangshu','label':'尚书省','emoji':'📮', 'role':'尚书令', 'rank':'正一品'},
|
||||
{'id':'hubu', 'label':'户部', 'emoji':'💰', 'role':'户部尚书', 'rank':'正二品'},
|
||||
{'id':'libu', 'label':'礼部', 'emoji':'📝', 'role':'礼部尚书', 'rank':'正二品'},
|
||||
{'id':'bingbu', 'label':'兵部', 'emoji':'⚔️', 'role':'兵部尚书', 'rank':'正二品'},
|
||||
{'id':'xingbu', 'label':'刑部', 'emoji':'⚖️', 'role':'刑部尚书', 'rank':'正二品'},
|
||||
{'id':'gongbu', 'label':'工部', 'emoji':'🔧', 'role':'工部尚书', 'rank':'正二品'},
|
||||
{'id':'libu_hr', 'label':'吏部', 'emoji':'👔', 'role':'吏部尚书', 'rank':'正二品'},
|
||||
{'id':'zaochao', 'label':'钦天监','emoji':'📰', 'role':'朝报官', 'rank':'正三品'},
|
||||
]
|
||||
|
||||
|
||||
def _check_gateway_alive():
|
||||
"""检测 Gateway 进程是否在运行。"""
|
||||
try:
|
||||
result = subprocess.run(['pgrep', '-f', 'openclaw-gateway'],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _check_gateway_probe():
|
||||
"""通过 HTTP probe 检测 Gateway 是否响应。"""
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
resp = urlopen('http://127.0.0.1:18789/', timeout=3)
|
||||
return resp.status == 200
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _get_agent_session_status(agent_id):
|
||||
"""读取 Agent 的 sessions.json 获取活跃状态。
|
||||
返回: (last_active_ts_ms, session_count, is_busy)
|
||||
"""
|
||||
sessions_file = OCLAW_HOME / 'agents' / agent_id / 'sessions' / 'sessions.json'
|
||||
if not sessions_file.exists() and agent_id == 'taizi':
|
||||
sessions_file = OCLAW_HOME / 'agents' / 'main' / 'sessions' / 'sessions.json'
|
||||
if not sessions_file.exists():
|
||||
return 0, 0, False
|
||||
try:
|
||||
data = json.loads(sessions_file.read_text())
|
||||
if not isinstance(data, dict):
|
||||
return 0, 0, False
|
||||
session_count = len(data)
|
||||
last_ts = 0
|
||||
for v in data.values():
|
||||
ts = v.get('updatedAt', 0)
|
||||
if isinstance(ts, (int, float)) and ts > last_ts:
|
||||
last_ts = ts
|
||||
now_ms = int(datetime.datetime.now().timestamp() * 1000)
|
||||
age_ms = now_ms - last_ts if last_ts else 9999999999
|
||||
is_busy = age_ms <= 2 * 60 * 1000 # 2分钟内视为正在工作
|
||||
return last_ts, session_count, is_busy
|
||||
except Exception:
|
||||
return 0, 0, False
|
||||
|
||||
|
||||
def _check_agent_process(agent_id):
|
||||
"""检测是否有该 Agent 的 openclaw-agent 进程正在运行。"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['pgrep', '-f', f'openclaw.*--agent.*{agent_id}'],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _check_agent_workspace(agent_id):
|
||||
"""检查 Agent 工作空间是否存在。"""
|
||||
ws = OCLAW_HOME / f'workspace-{agent_id}'
|
||||
return ws.is_dir()
|
||||
|
||||
|
||||
def get_agents_status():
|
||||
"""获取所有 Agent 的在线状态。
|
||||
返回各 Agent 的:
|
||||
- status: 'running' | 'idle' | 'offline' | 'unconfigured'
|
||||
- lastActive: 最后活跃时间
|
||||
- sessions: 会话数
|
||||
- hasWorkspace: 工作空间是否存在
|
||||
- processAlive: 是否有进程在运行
|
||||
"""
|
||||
gateway_alive = _check_gateway_alive()
|
||||
gateway_probe = _check_gateway_probe() if gateway_alive else False
|
||||
|
||||
agents = []
|
||||
seen_ids = set()
|
||||
for dept in _AGENT_DEPTS:
|
||||
aid = dept['id']
|
||||
if aid in seen_ids:
|
||||
continue
|
||||
seen_ids.add(aid)
|
||||
|
||||
has_workspace = _check_agent_workspace(aid)
|
||||
last_ts, sess_count, is_busy = _get_agent_session_status(aid)
|
||||
process_alive = _check_agent_process(aid)
|
||||
|
||||
# 状态判定
|
||||
if not has_workspace:
|
||||
status = 'unconfigured'
|
||||
status_label = '❌ 未配置'
|
||||
elif not gateway_alive:
|
||||
status = 'offline'
|
||||
status_label = '🔴 Gateway 离线'
|
||||
elif process_alive or is_busy:
|
||||
status = 'running'
|
||||
status_label = '🟢 运行中'
|
||||
elif last_ts > 0:
|
||||
now_ms = int(datetime.datetime.now().timestamp() * 1000)
|
||||
age_ms = now_ms - last_ts
|
||||
if age_ms <= 10 * 60 * 1000: # 10分钟内
|
||||
status = 'idle'
|
||||
status_label = '🟡 待命'
|
||||
elif age_ms <= 3600 * 1000: # 1小时内
|
||||
status = 'idle'
|
||||
status_label = '⚪ 空闲'
|
||||
else:
|
||||
status = 'idle'
|
||||
status_label = '⚪ 休眠'
|
||||
else:
|
||||
status = 'idle'
|
||||
status_label = '⚪ 无记录'
|
||||
|
||||
# 格式化最后活跃时间
|
||||
last_active_str = None
|
||||
if last_ts > 0:
|
||||
try:
|
||||
last_active_str = datetime.datetime.fromtimestamp(
|
||||
last_ts / 1000
|
||||
).strftime('%m-%d %H:%M')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
agents.append({
|
||||
'id': aid,
|
||||
'label': dept['label'],
|
||||
'emoji': dept['emoji'],
|
||||
'role': dept['role'],
|
||||
'status': status,
|
||||
'statusLabel': status_label,
|
||||
'lastActive': last_active_str,
|
||||
'lastActiveTs': last_ts,
|
||||
'sessions': sess_count,
|
||||
'hasWorkspace': has_workspace,
|
||||
'processAlive': process_alive,
|
||||
})
|
||||
|
||||
return {
|
||||
'ok': True,
|
||||
'gateway': {
|
||||
'alive': gateway_alive,
|
||||
'probe': gateway_probe,
|
||||
'status': '🟢 运行中' if gateway_probe else ('🟡 进程在但无响应' if gateway_alive else '🔴 未启动'),
|
||||
},
|
||||
'agents': agents,
|
||||
'checkedAt': now_iso(),
|
||||
}
|
||||
|
||||
|
||||
def wake_agent(agent_id, message=''):
|
||||
"""唤醒指定 Agent,发送一条心跳/唤醒消息。"""
|
||||
if not _SAFE_NAME_RE.match(agent_id):
|
||||
return {'ok': False, 'error': f'agent_id 非法: {agent_id}'}
|
||||
if not _check_agent_workspace(agent_id):
|
||||
return {'ok': False, 'error': f'{agent_id} 工作空间不存在,请先配置'}
|
||||
if not _check_gateway_alive():
|
||||
return {'ok': False, 'error': 'Gateway 未启动,请先运行 openclaw gateway start'}
|
||||
|
||||
# 确定实际 agent id(taizi 用 main)
|
||||
runtime_id = 'main' if agent_id == 'taizi' else agent_id
|
||||
msg = message or f'🔔 系统心跳检测 — 请回复 OK 确认在线。当前时间: {now_iso()}'
|
||||
|
||||
def do_wake():
|
||||
try:
|
||||
cmd = ['openclaw', 'agent', '--agent', runtime_id, '-m', msg, '--timeout', '120']
|
||||
log.info(f'🔔 唤醒 {agent_id} ({runtime_id})...')
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=130)
|
||||
if result.returncode == 0:
|
||||
log.info(f'✅ {agent_id} 已唤醒')
|
||||
else:
|
||||
log.warning(f'⚠️ {agent_id} 唤醒失败: {result.stderr[:200]}')
|
||||
except Exception as e:
|
||||
log.warning(f'⚠️ {agent_id} 唤醒异常: {e}')
|
||||
threading.Thread(target=do_wake, daemon=True).start()
|
||||
|
||||
return {'ok': True, 'message': f'{agent_id} 唤醒指令已发出,约10-30秒后生效'}
|
||||
|
||||
|
||||
# ══ Agent 实时活动读取 ══
|
||||
|
||||
# 状态 → agent_id 映射
|
||||
@@ -1201,6 +1403,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.send_json({'ok': False, 'error': 'task_id required'}, 400)
|
||||
else:
|
||||
self.send_json(get_task_activity(task_id))
|
||||
elif p == '/api/agents-status':
|
||||
self.send_json(get_agents_status())
|
||||
elif p.startswith('/api/agent-activity/'):
|
||||
agent_id = p.replace('/api/agent-activity/', '')
|
||||
if not agent_id or not _SAFE_NAME_RE.match(agent_id):
|
||||
@@ -1355,6 +1559,16 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.send_json(result)
|
||||
return
|
||||
|
||||
if p == '/api/agent-wake':
|
||||
agent_id = body.get('agentId', '').strip()
|
||||
message = body.get('message', '').strip()
|
||||
if not agent_id:
|
||||
self.send_json({'ok': False, 'error': 'agentId required'}, 400)
|
||||
return
|
||||
result = wake_agent(agent_id, message)
|
||||
self.send_json(result)
|
||||
return
|
||||
|
||||
if p == '/api/set-model':
|
||||
agent_id = body.get('agentId', '').strip()
|
||||
model = body.get('model', '').strip()
|
||||
|
||||
Reference in New Issue
Block a user