This repository has been archived on 2026-04-03. You can view files and clone it, but cannot push or open issues or pull requests.
Nico ae2338e70a Implement NodePool + contextvar HUD for Phase 2 shared nodes
- agent/node_pool.py: NodePool class creates shared stateless node instances,
  excludes stateful roles (sensor, memorizer, ui)
- agent/nodes/base.py: _current_hud contextvar for per-task HUD isolation,
  Node.hud() checks contextvar first, falls back to instance callback
- 15/15 engine tests green (4 new Phase 2 tests pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:36:46 +02:00

41 lines
1.5 KiB
Python

"""Base Node class with context management."""
import contextvars
import logging
from ..llm import estimate_tokens, fit_context
log = logging.getLogger("runtime")
# Per-task HUD callback — set by FrameEngine/Runtime before calling shared nodes.
# Isolates HUD events between concurrent sessions (asyncio.Task-scoped).
_current_hud = contextvars.ContextVar('send_hud', default=None)
class Node:
name: str = "node"
model: str | None = None
max_context_tokens: int = 4000
def __init__(self, send_hud):
self.send_hud = send_hud
self.last_context_tokens = 0
self.context_fill_pct = 0
async def hud(self, event: str, **data):
# Use task-scoped HUD if set (shared node pool), else instance callback
hud_fn = _current_hud.get() or self.send_hud
if event == "context" and self.model:
data["model"] = self.model
await hud_fn({"node": self.name, "event": event, **data})
def trim_context(self, messages: list[dict]) -> list[dict]:
"""Fit messages within this node's token budget."""
before = len(messages)
result = fit_context(messages, self.max_context_tokens)
self.last_context_tokens = sum(estimate_tokens(m["content"]) for m in result)
self.context_fill_pct = int(100 * self.last_context_tokens / self.max_context_tokens)
if before != len(result):
log.info(f"[{self.name}] context trimmed: {before} -> {len(result)} msgs, {self.context_fill_pct}% fill")
return result