simvx.core.ai.agent_node

Frame-loop driver that ticks any :class:Brain from a node’s on_update.

This is the dependency-free half of the AI driving layer: it ticks any core Brain (a hand-rolled behaviour tree, state machine, utility scorer, or the LLM brain in simvx.ai) and never imports simvx.ai or httpx. Classical brains are first-class here with zero LLM dependency.

Three primitives, smallest to largest:

  • func:

    build_ai_context - the free helper that snapshots a node + tree into an :class:AIContext for one tick.

    • class:

      BrainRunner - owns brain + blackboard + tick_interval; its

      meth:

      BrainRunner.step ticks at a cadence, passing the elapsed since the last tick (an accumulator), not the raw frame dt, so brains see correct deltas regardless of tick rate. Usable from any existing Node subclass.

      • class:

        AgentNode - the canonical batteries-included node that builds a Blackboard in on_ready, calls runner.step in on_update, and tears the brain down in on_exit_tree.

The driver calls exactly one method per tick: brain.tick(ctx). It never calls perceive / decide / act directly.

Module Contents

Classes

BrainRunner

Drive a :class:Brain at a fixed cadence from any node’s per-frame update.

AgentNode

A node that drives one :class:Brain every frame (or at tick_interval).

Functions

build_ai_context

Snapshot a live in-tree node into an :class:AIContext for one tick.

API

simvx.core.ai.agent_node.build_ai_context(node: simvx.core.node.Node, dt: float, blackboard: simvx.core.ai.blackboard.Blackboard) simvx.core.ai.brain.AIContext[source]

Snapshot a live in-tree node into an :class:AIContext for one tick.

agent is the node, world is its :class:SceneTree, and now is the tree’s accumulated sim clock. Must be called while the node is in the tree (node.tree is None before on_enter_tree); that is a programming error, raised eagerly rather than silently feeding the brain now=0.

class simvx.core.ai.agent_node.BrainRunner(brain: simvx.core.ai.brain.Brain, blackboard: simvx.core.ai.blackboard.Blackboard, *, tick_interval: float = 0.0)[source]

Drive a :class:Brain at a fixed cadence from any node’s per-frame update.

tick_interval == 0 ticks every frame (the classical authoritative cadence). tick_interval > 0 ticks every tick_interval seconds, and the dt the brain sees is the time elapsed since the previous tick (the accumulator), not the raw frame dt.

Initialization

step(node: simvx.core.node.Node, dt: float) None[source]

Advance the cadence by dt and tick the brain if it is due.

class simvx.core.ai.agent_node.AgentNode(*, brain: simvx.core.ai.brain.Brain | None = None, squad: simvx.core.ai.blackboard.Blackboard | None = None, **kwargs: Any)[source]

Bases: simvx.core.node.Node

A node that drives one :class:Brain every frame (or at tick_interval).

Set self.brain (in __init__ or before the node enters the tree). The node builds its :class:Blackboard in on_ready (optionally a child() of a squad board), creates a :class:BrainRunner, ticks it in on_update, and tears the brain down in on_exit_tree. With self.brain = None it is an inert node, so subclasses can set the brain conditionally.

Initialization

tick_interval

‘Property(…)’

on_ready() None[source]
on_update(dt: float) None[source]
on_exit_tree() None[source]
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property update_mode: simvx.core.descriptors.UpdateMode
property visible: bool
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
get_node_or_none(path: str) simvx.core.node.Node | None
find(target, *, direct: bool = False)
find_all(target, *, direct: bool = False)
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
on_enter_tree() None
on_fixed_update(dt: float) None
on_draw(renderer) None
on_picked(event: simvx.core.events.InputEvent) None
on_unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None
property app
property tree: simvx.core.scene_tree.SceneTree
property physics
property physics_2d
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()