+26
-37
@@ -12,6 +12,7 @@ import {
|
||||
synthesizeFromAgents,
|
||||
} from './agents.js'
|
||||
import { chartIdsFromToolLog, mountChartEmbeds } from './chart-embed.js'
|
||||
import { createSwarmPanel } from './swarm-ui.js'
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
@@ -269,41 +270,12 @@ export function createQvacView(opts) {
|
||||
})
|
||||
}
|
||||
|
||||
/** @type {Map<string, HTMLElement>} */
|
||||
const agentChipEls = new Map()
|
||||
const swarm = createSwarmPanel(opts.els.agentBar, {
|
||||
onStop: () => stopInference(),
|
||||
})
|
||||
|
||||
function clearAgentBar() {
|
||||
const bar = opts.els.agentBar
|
||||
if (!bar) return
|
||||
bar.innerHTML = ''
|
||||
bar.classList.add('hidden')
|
||||
agentChipEls.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ id: string, label: string, status: string, summary?: string, error?: string }} ev
|
||||
*/
|
||||
function paintAgentEvent(ev) {
|
||||
const bar = opts.els.agentBar
|
||||
if (!bar) return
|
||||
bar.classList.remove('hidden')
|
||||
let chip = agentChipEls.get(ev.id)
|
||||
if (!chip) {
|
||||
chip = document.createElement('span')
|
||||
chip.className = 'qvac-agent-chip'
|
||||
chip.dataset.agent = ev.id
|
||||
bar.appendChild(chip)
|
||||
agentChipEls.set(ev.id, chip)
|
||||
}
|
||||
chip.dataset.status = ev.status
|
||||
const tip = ev.summary || ev.error || ''
|
||||
chip.title = tip
|
||||
chip.textContent =
|
||||
ev.status === 'start'
|
||||
? `⟳ ${ev.label}`
|
||||
: ev.status === 'error'
|
||||
? `✕ ${ev.label}`
|
||||
: `✓ ${ev.label}`
|
||||
swarm.hide()
|
||||
}
|
||||
|
||||
/** Finish onboarding in tools-only mode (no model download). */
|
||||
@@ -810,6 +782,7 @@ export function createQvacView(opts) {
|
||||
async function stopInference() {
|
||||
if (!busy) return
|
||||
setStatus('Stopping…', 'busy')
|
||||
if (swarm.isOpen?.()) swarm.fail('Stopped by user')
|
||||
try {
|
||||
await engine.stop?.()
|
||||
} catch (err) {
|
||||
@@ -855,27 +828,43 @@ export function createQvacView(opts) {
|
||||
opts.isConnected?.() &&
|
||||
!engine.isAborted?.()
|
||||
) {
|
||||
setStatus('Spinning up agents…', 'busy')
|
||||
setStatus('Multi-agent swarm…', 'busy')
|
||||
if (streamBody) {
|
||||
streamBody.innerHTML = formatMdLite('_Dispatching multi-agent investigation…_')
|
||||
streamBody.innerHTML = formatMdLite(
|
||||
'_Swarm is running — watch specialists above while tools gather live data…_'
|
||||
)
|
||||
}
|
||||
const pack = await runSubAgents({
|
||||
tools,
|
||||
query: text,
|
||||
maxAgents: prefs.maxSubAgents,
|
||||
isAborted: () => Boolean(engine.isAborted?.()),
|
||||
onPlan: (specs) => {
|
||||
swarm.begin(
|
||||
specs.map((s) => ({
|
||||
id: s.id,
|
||||
label: s.label,
|
||||
description: s.description,
|
||||
})),
|
||||
text
|
||||
)
|
||||
},
|
||||
onAgent: (ev) => {
|
||||
if (engine.isAborted?.()) return
|
||||
paintAgentEvent(ev)
|
||||
setStatus(`Agent: ${ev.label}…`, 'busy')
|
||||
swarm.update(ev)
|
||||
if (ev.status === 'start' || ev.status === 'running') {
|
||||
setStatus(`${ev.label}…`, 'busy')
|
||||
}
|
||||
},
|
||||
})
|
||||
if (engine.isAborted?.()) {
|
||||
swarm.fail('Stopped')
|
||||
acc = '_(Stopped during multi-agent investigation.)_'
|
||||
paintAssistant(streamBody, acc, false)
|
||||
setStatus('Stopped', 'warn')
|
||||
return
|
||||
}
|
||||
swarm.complete('All agents done — synthesizing answer…')
|
||||
completeOpts.agentBriefing = pack.contextText
|
||||
completeOpts.agentFallbackText = synthesizeFromAgents(text, pack)
|
||||
completeOpts.agentToolCalls = pack.agents.map((a) => ({
|
||||
|
||||
Reference in New Issue
Block a user