Intelligent Agent Router: LLM-powered natural language routing with suggestion UI
This commit is contained in:
@@ -95,6 +95,20 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;b
|
||||
.bridge-bar .bridge-dot.offline{background:var(--red)}
|
||||
.bridge-bar .bridge-actions{display:flex;gap:.5rem}
|
||||
|
||||
/* Router */
|
||||
.router-bar{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:1rem 1.25rem;margin-bottom:1.5rem;display:flex;gap:.75rem;align-items:center}
|
||||
.router-bar input{flex:1;padding:.6rem .85rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.9rem;outline:none}
|
||||
.router-bar input:focus{border-color:var(--accent)}
|
||||
.router-bar input::placeholder{color:var(--text-dim)}
|
||||
.router-bar button{padding:.6rem 1.25rem;background:var(--accent);color:#fff;border:none;border-radius:6px;font-size:.9rem;cursor:pointer;white-space:nowrap}
|
||||
.router-bar button:hover{background:var(--accent-hover)}
|
||||
.router-bar button:disabled{opacity:.5;cursor:not-allowed}
|
||||
.router-result{background:var(--surface);border:1px solid var(--accent);border-radius:10px;padding:1.25rem;margin-bottom:1.5rem;display:none}
|
||||
.router-result .rr-action{font-size:.75rem;text-transform:uppercase;color:var(--accent);font-weight:600;letter-spacing:.04em;margin-bottom:.4rem}
|
||||
.router-result .rr-agent{font-size:1.05rem;font-weight:600;margin-bottom:.5rem}
|
||||
.router-result .rr-reasoning{color:var(--text-dim);font-size:.85rem;margin-bottom:1rem;line-height:1.5}
|
||||
.router-result .rr-actions{display:flex;gap:.5rem}
|
||||
|
||||
.empty-state{text-align:center;padding:3rem;color:var(--text-dim)}
|
||||
.empty-state h3{margin-bottom:.5rem;color:var(--text)}
|
||||
.time-ago{color:var(--text-dim)}
|
||||
@@ -114,6 +128,11 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;b
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="router-bar">
|
||||
<input type="text" id="router-input" placeholder="What do you need? Ask the router..." onkeydown="if(event.key==='Enter')askRouter()">
|
||||
<button id="router-btn" onclick="askRouter()">Ask</button>
|
||||
</div>
|
||||
<div class="router-result" id="router-result"></div>
|
||||
<div id="bridge-bar"></div>
|
||||
<div class="section-header">
|
||||
<h2>My Agents</h2>
|
||||
@@ -361,6 +380,78 @@ async function enableAgent(catalogId,name){
|
||||
else{const err=await res.json();alert(err.detail||'Failed to enable agent')}
|
||||
}
|
||||
|
||||
// --- Router ---
|
||||
async function askRouter(){
|
||||
const input=document.getElementById('router-input');
|
||||
const btn=document.getElementById('router-btn');
|
||||
const resultDiv=document.getElementById('router-result');
|
||||
const text=input.value.trim();
|
||||
if(!text)return;
|
||||
|
||||
btn.disabled=true;btn.textContent='Thinking...';
|
||||
resultDiv.style.display='none';
|
||||
|
||||
try{
|
||||
const res=await fetch(API+'/api/router',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({request:text})});
|
||||
if(res.status===401){location.href='/login';return}
|
||||
const r=await res.json();
|
||||
|
||||
if(r.error){
|
||||
resultDiv.innerHTML=`<div class="rr-action">Error</div><div class="rr-reasoning">${r.reasoning||r.error}</div>`;
|
||||
resultDiv.style.display='block';
|
||||
btn.disabled=false;btn.textContent='Ask';
|
||||
return;
|
||||
}
|
||||
|
||||
const action=r.action||'info';
|
||||
const actionLabels={run_existing:'Run Agent',create_and_run:'Create & Run',configure:'Update Config',info:'Info',not_possible:'Not Available'};
|
||||
|
||||
let actionsHtml='';
|
||||
if(action==='run_existing'||action==='create_and_run'||action==='configure'){
|
||||
actionsHtml=`<div class="rr-actions">
|
||||
<button class="btn-save" onclick="acceptRoute(${r.route_id})">Run It</button>
|
||||
<button class="btn-secondary" onclick="rejectRoute(${r.route_id})">Dismiss</button>
|
||||
</div>`;
|
||||
} else if(action==='info'){
|
||||
actionsHtml=`<div class="rr-actions"><button class="btn-secondary" onclick="dismissRouter()">OK</button></div>`;
|
||||
} else {
|
||||
actionsHtml=`<div class="rr-actions"><button class="btn-secondary" onclick="dismissRouter()">OK</button></div>`;
|
||||
}
|
||||
|
||||
resultDiv.innerHTML=`
|
||||
<div class="rr-action">${actionLabels[action]||action}</div>
|
||||
<div class="rr-agent">${r.agent_name||''}</div>
|
||||
<div class="rr-reasoning">${r.reasoning||''}</div>
|
||||
${actionsHtml}`;
|
||||
resultDiv.style.display='block';
|
||||
}catch(e){
|
||||
resultDiv.innerHTML=`<div class="rr-action">Error</div><div class="rr-reasoning">Connection error: ${e}</div>`;
|
||||
resultDiv.style.display='block';
|
||||
}
|
||||
btn.disabled=false;btn.textContent='Ask';
|
||||
}
|
||||
|
||||
async function acceptRoute(routeId){
|
||||
const resultDiv=document.getElementById('router-result');
|
||||
resultDiv.querySelector('.rr-actions').innerHTML='<span style="color:var(--green)">Running...</span>';
|
||||
const res=await fetch(API+'/api/router/'+routeId+'/accept',{method:'POST'});
|
||||
if(res.ok){
|
||||
const data=await res.json();
|
||||
resultDiv.querySelector('.rr-actions').innerHTML=`<span style="color:var(--green)">${data.message||'Done'}</span>`;
|
||||
setTimeout(()=>{dismissRouter();refresh()},3000);
|
||||
}
|
||||
}
|
||||
|
||||
async function rejectRoute(routeId){
|
||||
await fetch(API+'/api/router/'+routeId+'/reject',{method:'POST'});
|
||||
dismissRouter();
|
||||
}
|
||||
|
||||
function dismissRouter(){
|
||||
document.getElementById('router-result').style.display='none';
|
||||
document.getElementById('router-input').value='';
|
||||
}
|
||||
|
||||
// --- LLM Settings ---
|
||||
async function showLLMSettings(){
|
||||
const res=await fetch(API+'/api/me/llm');
|
||||
|
||||
Reference in New Issue
Block a user