Eras Expert domain context: - Full Heizkostenabrechnung business model (Kunde>Objekte>Nutzeinheiten>Geraete) - Known PK/FK mappings: kunden.Kundennummer, objekte.KundenID, etc. - Correct JOIN example in SCHEMA prompt - PA knows domain hierarchy for better job formulation Iterative plan-execute in ExpertNode: - DESCRIBE queries execute first, results injected into re-plan - Re-plan uses actual column names from DESCRIBE - Eliminates "Unknown column" errors on first query Frontend: - Node inspector: per-node cards with model, tokens, progress, last event - Graph switcher buttons in top bar - Clear button in top bar - Nodes panel 300px wide - WS reconnect on 1006 (deploy) without showing login - Model info emitted on context HUD events Domain context test: 21/21 (hierarchy, JOINs, FK, PA job quality) Default graph: v4-eras Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
35 lines
1.2 KiB
Python
35 lines
1.2 KiB
Python
"""Base Node class with context management."""
|
|
|
|
import logging
|
|
|
|
from ..llm import estimate_tokens, fit_context
|
|
|
|
log = logging.getLogger("runtime")
|
|
|
|
|
|
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):
|
|
# Always include model on context events so frontend knows what model each node uses
|
|
if event == "context" and self.model:
|
|
data["model"] = self.model
|
|
await self.send_hud({"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
|