feat(M2): Web AI 控制台——工具页新增子分栏:模型/Key/设备前端可配置(app_meta 存储,key 打码回显),指令执行 + 步骤流轮询(含截图缩略)+ 回答展示;Agent 加 on_step 回调、后台线程单实例运行

This commit is contained in:
2026-09-04 12:54:17 +08:00
parent 02776b12b8
commit fea5a57fc3
6 changed files with 287 additions and 2 deletions
+83
View File
@@ -41,6 +41,89 @@ async function loadToolsDevices(force){
status.textContent = '共 '+_clipDevices.length+' 台设备';
}
// ================== AI 控制台(模型配置 + 指令执行 + 进度轮询) ==================
let _agentPoll = null;
function loadAgentConfig(){
apiGet('/api/agent/config').then(r=>{
if(!r||!r.ok)return;
const b=document.getElementById('agent-api-base');
if(!b)return; // 面板未渲染
b.value=r.api_base||'https://api.deepseek.com';
document.getElementById('agent-model').value=r.model||'';
document.getElementById('agent-default-serial').value=r.default_serial||'';
const hint=document.getElementById('agent-key-hint');
hint.textContent=r.api_key_masked?('已配置 '+r.api_key_masked):'未配置 Key';
});
}
function saveAgentConfig(){
const body={api_base:document.getElementById('agent-api-base').value.trim(),
model:document.getElementById('agent-model').value.trim(),
default_serial:document.getElementById('agent-default-serial').value.trim()};
const key=document.getElementById('agent-api-key').value.trim();
if(key)body.api_key=key;
apiPost('/api/agent/config',body).then(r=>{
if(r&&r.ok){showToast('配置已保存','success');loadAgentConfig();}
else showToast('保存失败: '+((r&&r.error)||''),'error');
});
}
function runAgent(){
const prompt=document.getElementById('agent-prompt').value.trim();
if(!prompt){showToast('请输入指令','error');return;}
apiPost('/api/agent/run',{prompt}).then(r=>{
if(r&&r.ok){
showToast('Agent 已启动','success');
document.getElementById('agent-status').textContent='运行中...';
document.getElementById('btn-agent-run').disabled=true;
document.getElementById('agent-answer-wrap').style.display='none';
startAgentPoll();
}else showToast('启动失败: '+((r&&r.error)||''),'error');
});
}
function startAgentPoll(){
if(_agentPoll)clearInterval(_agentPoll);
_agentPoll=setInterval(pollAgent,2000);
pollAgent();
}
function stopAgentPoll(){
if(_agentPoll){clearInterval(_agentPoll);_agentPoll=null;}
}
function pollAgent(){
apiGet('/api/agent/status').then(r=>{
if(!r||!r.ok)return;
const st=document.getElementById('agent-status');
if(r.state==='running'){
st.textContent='运行中... ('+(r.steps||[]).length+' 步)';
}else{
st.textContent=r.state==='done'?'完成':'失败';
document.getElementById('btn-agent-run').disabled=false;
stopAgentPoll();
}
// 步骤流(含截图缩略)
const steps=document.getElementById('agent-steps');
const html=(r.steps||[]).map(s=>{
const args=esc(s.args||'');
const img=s.image
?'<img src="data:image/jpeg;base64,'+s.image+'" style="max-width:120px;border-radius:6px;margin-top:4px;display:block">':'';
return '<div style="padding:6px 0;border-bottom:1px solid var(--ink-line)">'
+'<code style="color:var(--teal)">'+esc(s.tool||'')+'</code> '
+'<span class="text-muted">'+args+'</span>'+img+'</div>';
}).join('')||'';
steps.innerHTML=html;
// 最终回答
if(r.answer){
const wrap=document.getElementById('agent-answer-wrap');
wrap.style.display='block';
document.getElementById('agent-answer').textContent=r.answer;
}
if(r.error){
st.textContent='错误: '+r.error;
document.getElementById('btn-agent-run').disabled=false;
stopAgentPoll();
}
});
}
// ================== 设备自动发现(扫描 → 待连接池 → 确认) ==================
function loadDiscovery(){
apiGet('/api/devices/discovery').then(r=>{