agent-runtime/agent/types.py
Nico a2bc6347fc v0.13.0: Graph engine, versioned nodes, S3* audit, DB tools, Cytoscape
Architecture:
- Graph engine (engine.py) loads graph definitions, instantiates nodes
- Versioned nodes: input_v1, thinker_v1, output_v1, memorizer_v1, director_v1
- NODE_REGISTRY for dynamic node lookup by name
- Graph API: /api/graph/active, /api/graph/list, /api/graph/switch
- Graph definition: graphs/v1_current.py (7 nodes, 13 edges, 3 edge types)

S3* Audit system:
- Workspace mismatch detection (server vs browser controls)
- Code-without-tools retry (Thinker wrote code but no tool calls)
- Intent-without-action retry (request intent but Thinker only produced text)
- Dashboard feedback: browser sends workspace state on every message
- Sensor continuous comparison on 5s tick

State machines:
- create_machine / add_state / reset_machine / destroy_machine via function calling
- Local transitions (go:) resolve without LLM round-trip
- Button persistence across turns

Database tools:
- query_db tool via pymysql to MariaDB K3s pod (eras2_production)
- Table rendering in workspace (tab-separated parsing)
- Director pre-planning with Opus for complex data requests
- Error retry with corrected SQL

Frontend:
- Cytoscape.js pipeline graph with real-time node animations
- Overlay scrollbars (CSS-only, no reflow)
- Tool call/result trace events
- S3* audit events in trace

Testing:
- 167 integration tests (11 test suites)
- 22 node-level unit tests (test_nodes/)
- Three test levels: node unit, graph integration, scenario

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 00:18:45 +01:00

51 lines
1.5 KiB
Python

"""Message types flowing between nodes."""
from dataclasses import dataclass, field, asdict
@dataclass
class Envelope:
"""What flows between nodes."""
text: str
user_id: str = "anon"
session_id: str = ""
timestamp: str = ""
@dataclass
class InputAnalysis:
"""Structured classification from Input node."""
who: str = "unknown"
language: str = "en"
intent: str = "request" # question | request | social | action | feedback
topic: str = ""
tone: str = "casual" # casual | frustrated | playful | urgent
complexity: str = "simple" # trivial | simple | complex
context: str = ""
@dataclass
class Command:
"""Input node's structured perception of what was heard."""
analysis: InputAnalysis
source_text: str
metadata: dict = field(default_factory=dict)
@property
def instruction(self) -> str:
"""Backward-compatible summary string for logging/thinker."""
a = self.analysis
return f"{a.who} ({a.intent}, {a.tone}): {a.topic}"
@dataclass
class ThoughtResult:
"""Thinker node's output — either a direct answer or tool results."""
response: str
tool_used: str = ""
tool_output: str = ""
actions: list = field(default_factory=list) # [{label, action, payload?}]
state_updates: dict = field(default_factory=dict) # {key: value} from set_state
display_items: list = field(default_factory=list) # [{type, label, value?, style?}] from emit_display
machine_ops: list = field(default_factory=list) # [{op, id, ...}] from machine tools