Advanced Runtimes: Lua & Prolog#
Parent document: YAML Reference Related: Node Specification Epic: DOC-002 (YAML Reference Modularization)
Overview#
TEA supports alternative runtimes for specific use cases:
Lua: Cross-runtime compatibility with the Rust implementation
Prolog: Neurosymbolic AI workflows combining neural network outputs with symbolic reasoning
Both runtimes are sandboxed for security and have timeout protection.
Table of Contents#
Lua Runtime#
Enabling Lua#
engine = YAMLEngine(lua_enabled=True)
Installation:
pip install 'the_edge_agent[lua]'
# or
pip install lupa>=2.0
Syntax Markers#
Explicit marker (recommended):
- name: process_lua
run: |
-- lua
local result = {}
result.value = state.value * 2
result.message = state.name .. "!"
return result
Auto-Detection#
Lua code is auto-detected when it contains Lua-specific syntax:
localkeyword (variable declaration)then/endkeywords (control flow)elseifkeyword (Lua uses elseif, Python uses elif)..operator (string concatenation)
- name: auto_detected_lua
run: |
local count = state.count + 1
local doubled = state.count * 2
return {count = count, doubled = doubled}
Lua Sandbox#
For security, the following Lua globals are removed:
os- Operating system accessio- File I/O operationsdebug- Debugging facilitiesloadfile,dofile- File loading
Safe libraries remain available: string, math, table, pairs, ipairs, type, tostring, tonumber.
Timeout Protection#
Lua code execution has a configurable timeout (default: 30 seconds):
engine = YAMLEngine(lua_enabled=True, lua_timeout=10.0) # 10 second timeout
Cross-Runtime Compatibility#
Lua code in YAML agents runs identically in both Python and Rust TEA implementations, enabling portable agents.
Lua Version Compatibility (LuaJIT 2.1 vs Lua 5.4):
The Python implementation uses LuaJIT 2.1 (via lupa) while the Rust implementation uses Lua 5.4 (via mlua). For cross-runtime compatibility, use the portable subset of Lua syntax.
Feature |
LuaJIT 2.1 (Python) |
Lua 5.4 (Rust) |
Portable Alternative |
|---|---|---|---|
Integer division |
|
|
Use |
Bitwise ops |
|
|
Avoid bitwise; use math |
Const variables |
Not supported |
|
Use |
Close variables |
Not supported |
|
Avoid |
UTF-8 library |
Not built-in |
|
Use |
Warning system |
Not available |
|
Avoid |
Portable Syntax Examples (works in both):
-- State access
local count = state.count + 1
-- Conditionals (ternary style)
local result = state.value > 5 and "high" or "low"
-- Table creation
return { count = count, status = "done" }
-- Loops
for i, v in ipairs(state.items) do
-- process
end
-- String and math operations
local upper = string.upper(state.name)
local avg = math.floor(total / count)
Syntax to Avoid (Lua 5.4 only - will fail in Python):
-- Integer division operator (Lua 5.4 only)
local quotient = 17 // 5 -- Use: math.floor(17/5)
-- Bitwise operators (Lua 5.4 only)
local flags = a & b | c -- Use: bit.band/bit.bor in LuaJIT
-- Const/close attributes (Lua 5.4 only)
local x <const> = 10 -- Use: local x = 10
Prolog Runtime#
Enabling Prolog#
Python:
engine = YAMLEngine(prolog_enabled=True)
Rust:
# Build with Prolog feature
cargo build --features prolog
# Run agent with Prolog feature
cargo run --features prolog -- run my-agent.yaml
# Prolog requires SWI-Prolog 9.1+ system installation:
# Ubuntu/Debian: sudo apt install swi-prolog swi-prolog-nox
# macOS: brew install swi-prolog
# Windows: choco install swi-prolog
Important: In Rust TEA, Prolog is an optional feature requiring:
The
--features prologflag during build/runSWI-Prolog 9.1+ installed on the system
If Prolog feature is not enabled, nodes with language: prolog will fail with a clear error message explaining how to enable it.
Installation (Python):
# Install janus-swi Python binding (requires SWI-Prolog 9.1+)
pip install 'the_edge_agent[prolog]'
# or directly
pip install janus-swi
# Install SWI-Prolog 9.1+ system dependency
# Ubuntu/Debian (PPA recommended for 9.1+):
sudo apt-add-repository ppa:swi-prolog/stable
sudo apt update
sudo apt install swi-prolog
# macOS:
brew install swi-prolog
# Verify version (must be 9.1+):
swipl --version
# Windows: Download from https://www.swi-prolog.org/download/stable
Installation (Rust):
# Build with Prolog feature
cargo build --features prolog
# SWI-Prolog 9.1+ must be installed system-wide:
# Ubuntu/Debian:
sudo apt-add-repository ppa:swi-prolog/stable
sudo apt update
sudo apt install swi-prolog swi-prolog-nox
# macOS:
brew install swi-prolog
# Fedora:
sudo dnf install pl
# Windows: Download from https://www.swi-prolog.org/download/stable
# or: choco install swi-prolog
Module Pre-Loading#
Both Python and Rust runtimes automatically pre-load common modules at initialization:
lists- List manipulation predicates (member/2,append/3,reverse/2, etc.)clpfd- Finite domain constraints (no:- use_moduleneeded!)apply- Higher-order predicates (maplist/2,include/3, etc.)aggregate- Aggregation predicates (aggregate_all/3, etc.)
This means CLP(FD) constraints and list predicates work immediately in both Python and Rust without explicit module imports. YAML agents using these predicates are fully portable across runtimes.
State Interface#
Explicit marker (recommended):
- name: process_prolog
run: |
% prolog
state(value, V),
V2 is V * 2,
return(result, V2).
Language attribute (explicit):
- name: process_prolog
language: prolog
run: |
state(value, V),
V2 is V + 10,
return(result, V2).
Explicit type in run config:
- name: compute
run:
type: prolog
code: |
state(value, V),
V2 is V * 3,
return(result, V2).
Auto-detection (heuristic): Prolog code is auto-detected when it contains Prolog-specific syntax:
:-rule operator or directivestate(orreturn(predicates (TEA convention)assertz,findall,forall,aggregate_allpredicatesCLP(FD) operators (
#=,#<,#>,in)use_moduledirective
Access state via state/2 predicate and set return values via return/2:
% Read state["value"] into V
state(value, V),
% Compute result
Result is V * 2,
% Set state["doubled"] = Result
return(doubled, Result).
Rust Runtime Limitation: The return/2 predicate is recognized by auto-detection but does not currently update the state in the Rust runtime due to swipl-rs crate constraints. Workarounds for Rust:
Use Lua nodes for state manipulation
Use CLP(FD) constraints where labeled values can be extracted
Use Prolog purely for validation/logic checks (success/failure)
The Python runtime (janus-swi) fully supports return/2 for state updates.
CLP(FD) Constraint Solving#
Prolog integration includes support for CLP(FD) finite domain constraints. The clpfd module is pre-loaded automatically—no explicit import needed:
- name: solve_constraints
language: prolog
run: |
% No :- use_module(library(clpfd)) needed - it's pre-loaded!
X in 1..10,
Y in 1..10,
X + Y #= 15,
X #< Y,
label([X, Y]),
return(x, X),
return(y, Y).
Prolog Sandbox#
For security, the sandbox restricts:
File I/O operations (
open/3,read/1,write/1)Shell execution (
shell/1,process_create/3)Network access
Safe predicates remain available: arithmetic, list operations, findall, aggregate_all, CLP(FD), etc.
Timeout Protection#
Prolog code execution has a configurable timeout (default: 30 seconds):
engine = YAMLEngine(prolog_enabled=True, prolog_timeout=10.0) # 10 second timeout
Runtime Comparison Table#
Feature |
Python (janus-swi) |
Rust (swipl-rs) |
|---|---|---|
Bindings |
janus-swi (official) |
swipl-rs (community) |
SWI-Prolog Version |
9.1+ required |
9.1+ recommended |
|
Full support |
Limited (doesn’t update state) |
Module pre-loading |
Auto (clpfd, lists, apply, aggregate) |
Auto (clpfd, lists, apply, aggregate) |
|
Full support |
Full support |
Sandbox |
Default enabled |
Default enabled |
Timeout protection |
30s default |
30s default |
Thread safety |
Thread-local predicates |
RwLock + state caching |
Best Practice for Portable Agents:
Use
state/2for reading input values (works in both)Use Prolog for logic/validation that succeeds or fails
For state updates: use Lua nodes or Python
run:blocksCLP(FD) predicates work without explicit imports in both runtimes
Migration from pyswip (TEA-PY-005): The Python runtime was migrated from pyswip to janus-swi for:
Proper timeout exception handling (no segfaults)
Native directive support via
consult()Full CLP(FD) module loading
Official SWI-Prolog 9.1+ bindings
Neurosymbolic AI Use Cases#
Validate LLM outputs with logical rules
Constraint-based reasoning for scheduling, planning
Knowledge graph queries with Datalog-style rules
Formal verification of neural network predictions
Ontology reasoning with OWL/RDF-style inference
Example: LLM + Prolog Validation#
nodes:
- name: generate
uses: llm.call
with:
model: gpt-4
messages:
- role: user
content: "Generate a valid schedule for {{ state.constraints }}"
output: llm_response
- name: validate_with_prolog
language: prolog
run: |
% Define scheduling constraints
valid_schedule(Start, End) :-
Start >= 9, % No earlier than 9 AM
End =< 17, % No later than 5 PM
End > Start. % End after start
% Parse and validate LLM output
state(llm_response, Response),
% Extract times from response (simplified)
Start = 10, End = 14,
(valid_schedule(Start, End)
-> return(valid, true), return(schedule, [Start, End])
; return(valid, false), return(error, 'Invalid schedule')).
See Also#
Node Specification - All execution methods
examples/prolog/neurosymbolic/- Example agentsNavigation & Flow - Workflow routing
Actions Overview - Built-in action reference