Command Parsing
The OmniLinkEngine is a deterministic command parser that
matches incoming text against predefined templates, extracts typed
variables, and dispatches to registered handler functions. It bridges
the gap between free-form AI output and structured local execution.
Overview
When an AI engine produces a command like open_the_front_door,
the OmniLinkEngine matches it against your registered templates, extracts
the variable values, and calls the appropriate handler. This gives you
deterministic, type-safe command execution on the local side while letting
the AI handle reasoning and decision-making in the cloud.
The engine runs entirely locally — no network calls, no API credits. It is designed to be fast, predictable, and easy to test.
Template Syntax
Templates use a simple format: literal text with typed variable placeholders enclosed in square brackets.
# Format
action_[varname:typename]
# Examples
open_the_[door:door]
set_temp_[temp:temperature]
move_[direction:direction]_[distance:int]_meters
send_message_to_[user:username]
Each placeholder follows the pattern [varname:typename] where:
- varname — The name of the extracted variable. This is the key you use to access the value in your handler.
-
typename — The name of a registered type in the
TypeRegistry. The engine uses the type’s regex pattern to match and capture the value.
Underscore and Space Normalization
The engine normalizes both underscores and spaces before matching. This
means the following inputs all match the template
open_the_[door:door]:
open_the_front_dooropen the front_dooropen the front door
This normalization makes the engine forgiving of formatting differences between AI outputs and human inputs.
TypeRegistry
The TypeRegistry maps type names to regex patterns. When the
engine encounters a [varname:typename] placeholder, it looks
up the type’s pattern and uses it to match the corresponding segment
of the input string.
from omnilink import TypeRegistry
types = TypeRegistry()
# Register an enum-like type with specific allowed values
types.register("door", r"(?:front_door|back_door|garage_door)")
# Register a numeric type
types.register("temperature", r"\d+(?:\.\d+)?")
# Register a direction type
types.register("direction", r"(?:north|south|east|west|up|down)")
# Register a general string identifier
types.register("username", r"[a-zA-Z_][a-zA-Z0-9_]*")
Built-in Type Coercion
For common numeric types, the engine performs automatic coercion:
-
int — Captured values are converted to Python
int. -
float — Captured values are converted to Python
float.
For all other types, captured values are returned as strings. You can perform additional parsing in your handler if needed.
Regex Tips
Type patterns are standard Python regular expressions. Keep these guidelines in mind:
-
Use non-capturing groups
(?:...)for alternation to avoid interfering with the engine’s internal capture groups. -
Be as specific as possible. A pattern like
.*will match too greedily and may consume parts of subsequent template segments. -
Test your patterns independently with Python’s
remodule before registering them.
Creating an Engine
Create an OmniLinkEngine instance by passing a list of
template strings and an optional TypeRegistry:
from omnilink import OmniLinkEngine, TypeRegistry
types = TypeRegistry()
types.register("door", r"(?:front_door|back_door|garage_door)")
types.register("temperature", r"\d+(?:\.\d+)?")
engine = OmniLinkEngine(
[
"open_the_[door:door]",
"close_the_[door:door]",
"set_temp_[temp:temperature]",
"get_status",
],
types=types,
)
The engine compiles all templates into regex patterns at construction
time. Subsequent handle() calls are fast pattern matches
with no compilation overhead.
Handler Registration
Use engine.on_template() to bind a handler function to a
specific template. When the engine matches an incoming command to that
template, your handler is called with an event dictionary.
def handle_open_door(evt):
door = evt["vars"]["door"]
messenger = evt["messenger"]
messenger.info(f"Opening {door}...")
# Your actual door-opening logic here
activate_motor(door)
messenger.success(f"{door} is now open.")
return {"status": "opened", "door": door}
engine.on_template("open_the_[door:door]", handle_open_door)
The Event Object
Every handler receives an evt dictionary with the following
keys:
| Key | Type | Description |
|---|---|---|
vars |
dict |
A dictionary of extracted variable values, keyed by variable name.
For example, evt["vars"]["door"] yields
"front_door".
|
messenger |
AgentFeedback |
A feedback object for sending status messages back to the caller.
Supports .info(), .success(),
.warning(), and .error() methods.
|
template |
string |
The template string that matched, e.g.
"open_the_[door:door]".
|
Testing Commands Locally
Call engine.handle() with a command string to test matching
and handler execution without any cloud connectivity:
result = engine.handle("open the front_door")
print(result)
# {"matched": True, "result": {"status": "opened", "door": "front_door"}}
Result Format
The handle() method returns a dictionary with two keys:
| Key | Type | Description |
|---|---|---|
matched |
bool |
True if the command matched a registered template,
False otherwise.
|
result |
any |
The return value from the matched handler. If no match was found,
this is None.
|
No-Match Handling
When a command does not match any template, the engine returns:
result = engine.handle("fly_to_the_moon")
print(result)
# {"matched": False, "result": None}
You should always check the matched field in your integration
code and handle unrecognized commands gracefully — for example, by
logging a warning or asking the AI to rephrase.
Multiple Templates
An engine can hold any number of templates. The engine tests them in registration order and returns the first match:
engine = OmniLinkEngine(
[
"open_the_[door:door]",
"close_the_[door:door]",
"lock_the_[door:door]",
"unlock_the_[door:door]",
"set_temp_[temp:temperature]",
"get_status",
"emergency_stop",
],
types=types,
)
# Register handlers for each template
engine.on_template("open_the_[door:door]", handle_open)
engine.on_template("close_the_[door:door]", handle_close)
engine.on_template("lock_the_[door:door]", handle_lock)
engine.on_template("unlock_the_[door:door]", handle_unlock)
engine.on_template("set_temp_[temp:temperature]", handle_temp)
engine.on_template("get_status", handle_status)
engine.on_template("emergency_stop", handle_emergency)
Use engine.list_templates() to retrieve all registered
templates at any time:
templates = engine.list_templates()
print(templates)
# ["open_the_[door:door]", "close_the_[door:door]", "lock_the_[door:door]", ...]
Advanced Patterns
Multi-Variable Templates
Templates can contain multiple variable placeholders. The engine extracts
all of them into the vars dictionary:
types.register("axis", r"(?:x|y|z)")
types.register("distance", r"-?\d+(?:\.\d+)?")
engine = OmniLinkEngine(
["move_[axis:axis]_[dist:distance]_mm"],
types=types,
)
def handle_move(evt):
axis = evt["vars"]["axis"]
dist = float(evt["vars"]["dist"])
return {"axis": axis, "distance_mm": dist}
engine.on_template("move_[axis:axis]_[dist:distance]_mm", handle_move)
result = engine.handle("move_x_15.5_mm")
# {"matched": True, "result": {"axis": "x", "distance_mm": 15.5}}
Templates Without Variables
Not every command needs variables. Simple action commands work as plain strings:
engine = OmniLinkEngine(["get_status", "emergency_stop", "reset"])
def handle_status(evt):
return {"temperature": 22.5, "doors": "all_locked"}
engine.on_template("get_status", handle_status)
Complex Regex Types
For more sophisticated matching, register complex regex patterns:
# Match ISO 8601 dates
types.register("date", r"\d{4}-\d{2}-\d{2}")
# Match email-like patterns
types.register("email", r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
# Match hex color codes
types.register("color", r"#[0-9a-fA-F]{6}")
# Match file paths
types.register("filepath", r"[a-zA-Z0-9_/.-]+")
Complete Working Example
Here is a full example that creates a smart-home command engine, registers types and handlers, and processes a sequence of commands:
from omnilink import OmniLinkEngine, TypeRegistry
# 1. Define types
types = TypeRegistry()
types.register("door", r"(?:front_door|back_door|garage_door)")
types.register("device", r"(?:living_room_light|kitchen_light|bedroom_fan|tv)")
types.register("temperature", r"\d+(?:\.\d+)?")
types.register("brightness", r"\d{1,3}")
# 2. Create engine with templates
engine = OmniLinkEngine(
[
"open_the_[door:door]",
"close_the_[door:door]",
"turn_on_[device:device]",
"turn_off_[device:device]",
"set_temp_[temp:temperature]",
"set_brightness_[level:brightness]",
"get_status",
],
types=types,
)
# 3. Define handlers
home_state = {
"doors": {"front_door": "closed", "back_door": "closed", "garage_door": "closed"},
"devices": {"living_room_light": "off", "kitchen_light": "off", "bedroom_fan": "off", "tv": "off"},
"temperature": 22.0,
"brightness": 80,
}
def handle_open(evt):
door = evt["vars"]["door"]
home_state["doors"][door] = "open"
evt["messenger"].success(f"{door} opened.")
return {"door": door, "state": "open"}
def handle_close(evt):
door = evt["vars"]["door"]
home_state["doors"][door] = "closed"
evt["messenger"].success(f"{door} closed.")
return {"door": door, "state": "closed"}
def handle_on(evt):
device = evt["vars"]["device"]
home_state["devices"][device] = "on"
return {"device": device, "state": "on"}
def handle_off(evt):
device = evt["vars"]["device"]
home_state["devices"][device] = "off"
return {"device": device, "state": "off"}
def handle_temp(evt):
temp = float(evt["vars"]["temp"])
home_state["temperature"] = temp
return {"temperature": temp}
def handle_brightness(evt):
level = int(evt["vars"]["level"])
home_state["brightness"] = min(100, max(0, level))
return {"brightness": home_state["brightness"]}
def handle_status(evt):
return home_state
# 4. Register handlers
engine.on_template("open_the_[door:door]", handle_open)
engine.on_template("close_the_[door:door]", handle_close)
engine.on_template("turn_on_[device:device]", handle_on)
engine.on_template("turn_off_[device:device]", handle_off)
engine.on_template("set_temp_[temp:temperature]", handle_temp)
engine.on_template("set_brightness_[level:brightness]", handle_brightness)
engine.on_template("get_status", handle_status)
# 5. Process commands
commands = [
"open the front_door",
"turn on living_room_light",
"set_temp_23.5",
"set_brightness_60",
"get_status",
]
for cmd in commands:
result = engine.handle(cmd)
if result["matched"]:
print(f"OK: {cmd} -> {result['result']}")
else:
print(f"NO MATCH: {cmd}")
Integration with AI Chat
The command engine is most powerful when combined with the AI chat API. The AI engine produces structured commands, and the local engine parses and executes them:
from omnilink.client import OmniLinkClient
client = OmniLinkClient(omni_key="omk_your_key")
# The AI response will contain a command from the agent's availableCommands
response = client.chat(
"It's getting cold, can you warm it up?",
agent_name="home-controller",
engine="g1-engine",
)
# Parse the AI's command output
ai_command = response["command"] # e.g., "set_temp_24"
result = engine.handle(ai_command)
if result["matched"]:
print(f"Executed: {result['result']}")
else:
print(f"Unrecognized command: {ai_command}")
Next Steps
- Agent Connection — Expose your command engine over the HTTP REST bridge.
- Agent Configuration — Configure the AI to produce valid commands.
- OmniLink-lib Reference — Full API reference for the Python library.