diff --git a/agent.py b/agent.py index 2c6ca34..bc5ac4f 100644 --- a/agent.py +++ b/agent.py @@ -211,6 +211,7 @@ class Node: def __init__(self, send_hud): self.send_hud = send_hud # async callable to emit hud events to frontend self.last_context_tokens = 0 + self.context_fill_pct = 0 async def hud(self, event: str, **data): await self.send_hud({"node": self.name, "event": event, **data}) @@ -403,7 +404,7 @@ ONE sentence. No content, no response — just your perception of what came thro messages.append(msg) messages = self.trim_context(messages) - await self.hud("context", messages=messages) + await self.hud("context", messages=messages, tokens=self.last_context_tokens, max_tokens=self.max_context_tokens, fill_pct=self.context_fill_pct) instruction = await llm_call(self.model, messages) log.info(f"[input] → command: {instruction}") await self.hud("perceived", instruction=instruction) @@ -435,7 +436,7 @@ Be natural. Be concise. If the user asks you to do something, do it — don't de messages.append({"role": "system", "content": f"Input perception: {command.instruction}"}) messages = self.trim_context(messages) - await self.hud("context", messages=messages) + await self.hud("context", messages=messages, tokens=self.last_context_tokens, max_tokens=self.max_context_tokens, fill_pct=self.context_fill_pct) # Stream response client, resp = await llm_call(self.model, messages, stream=True) @@ -526,7 +527,7 @@ Output ONLY valid JSON. No explanation, no markdown fences.""" messages.append({"role": "user", "content": "Update the shared state based on this conversation. Output JSON only."}) messages = self.trim_context(messages) - await self.hud("context", messages=messages) + await self.hud("context", messages=messages, tokens=self.last_context_tokens, max_tokens=self.max_context_tokens, fill_pct=self.context_fill_pct) raw = await llm_call(self.model, messages) log.info(f"[memorizer] raw: {raw[:200]}") diff --git a/static/app.js b/static/app.js index 860b5fc..86d87bf 100644 --- a/static/app.js +++ b/static/app.js @@ -138,9 +138,14 @@ function handleHud(data) { const event = data.event || ''; if (event === 'context') { - // Expandable: show message count, click to see full context + // Update node meter + if (data.tokens !== undefined) { + updateMeter(node, data.tokens, data.max_tokens, data.fill_pct); + } + // Expandable: show message count + token info const count = (data.messages || []).length; - const summary = count + ' msgs: ' + (data.messages || []).map(m => + const tokenInfo = data.tokens ? ` [${data.tokens}/${data.max_tokens}t ${data.fill_pct}%]` : ''; + const summary = count + ' msgs' + tokenInfo + ': ' + (data.messages || []).map(m => m.role[0].toUpperCase() + ':' + truncate(m.content, 30) ).join(' | '); const detail = (data.messages || []).map((m, i) => @@ -148,6 +153,9 @@ function handleHud(data) { ).join('\n'); addTrace(node, 'context', summary, 'context', detail); + } else if (event === 'perceived') { + addTrace(node, 'perceived', data.instruction, 'instruction'); + } else if (event === 'decided') { addTrace(node, 'decided', data.instruction, 'instruction'); @@ -171,6 +179,24 @@ function handleHud(data) { } else if (event === 'done') { addTrace(node, 'done', ''); + } else if (event === 'tick') { + // Update sensor meter with tick count + const meter = document.getElementById('meter-sensor'); + if (meter) { + const text = meter.querySelector('.nm-text'); + const deltas = Object.entries(data.deltas || {}).map(([k,v]) => k + '=' + v).join(' '); + text.textContent = 'tick #' + (data.tick || 0) + (deltas ? ' | ' + deltas : ''); + } + if (data.deltas && Object.keys(data.deltas).length) { + const deltas = Object.entries(data.deltas).map(([k,v]) => k + '=' + truncate(String(v), 30)).join(' '); + addTrace(node, 'tick #' + data.tick, deltas); + } + + } else if (event === 'started' || event === 'stopped') { + const meter = document.getElementById('meter-sensor'); + if (meter) meter.querySelector('.nm-text').textContent = event; + addTrace(node, event, ''); + } else { // Generic fallback const detail = JSON.stringify(data, null, 2); @@ -203,6 +229,16 @@ function addTrace(node, event, text, cls, detail) { scroll(traceEl); } +function updateMeter(node, tokens, maxTokens, fillPct) { + const meter = document.getElementById('meter-' + node); + if (!meter) return; + const fill = meter.querySelector('.nm-fill'); + const text = meter.querySelector('.nm-text'); + fill.style.width = fillPct + '%'; + fill.style.backgroundColor = fillPct > 80 ? '#ef4444' : fillPct > 50 ? '#f59e0b' : '#22c55e'; + text.textContent = tokens + ' / ' + maxTokens + 't (' + fillPct + '%)'; +} + function scroll(el) { el.scrollTop = el.scrollHeight; } function esc(s) { const d = document.createElement('span'); d.textContent = s; return d.innerHTML; } function truncate(s, n) { return s.length > n ? s.slice(0, n) + '\u2026' : s; } diff --git a/static/index.html b/static/index.html index 42a776e..8995e61 100644 --- a/static/index.html +++ b/static/index.html @@ -13,6 +13,13 @@