Skip to content

Actions API

The Zelos SDK Actions API provides a way to define interactive Python functions with rich input validation and metadata. Actions can be decorated with input parameters, validation rules, and UI hints to create dynamic, user-friendly interfaces.

Quick Start

Define a simple action and make it available to the Zelos Agent:

from zelos_sdk import init, action

# Define a simple action
@action("Add Numbers", "Add two numbers together")
@action.input("x", type="number", required=True, description="First number")
@action.input("y", type="number", required=True, description="Second number")
def add(x: float, y: float):
    return x + y

# Make your actions available to the Zelos Agent, block until Ctrl+C
init(actions=True, block=True)

Running the script above results in this Action in the Zelos App:

Add Action

Example Add Action

Core Components

@action Decorator

The @action decorator transforms regular Python functions into rich, interactive actions with metadata and validation.

from zelos_sdk.actions import action

@action("Process Data", "Process incoming sensor data")
@action.input("data", type="object", required=True, description="Raw sensor data")
@action.input("threshold", type="number", default=100, description="Alert threshold")
def process_sensor_data(data: dict, threshold: float = 100):
    processed = {
        "timestamp": data.get("timestamp"),
        "values": [v for v in data.get("values", []) if v < threshold],
        "alerts": [v for v in data.get("values", []) if v >= threshold]
    }
    return processed

Input Field Types

Actions support rich input validation with multiple field types:

@action("Configure System", "Configure system settings")
@action.input("name", type="text", required=True, min_length=3, max_length=50)
@action.input("port", type="number", minimum=1, maximum=65535, default=8080)
@action.input("enabled", type="boolean", default=True)
@action.input("mode", type="select", choices=["dev", "staging", "prod"], required=True)
def configure_system(name: str, port: int = 8080, enabled: bool = True, mode: str = "dev"):
    return {
        "configuration": {
            "name": name,
            "port": port,
            "enabled": enabled,
            "mode": mode
        },
        "status": "configured"
    }

Dynamic Input Fields

Create inputs that change based on other field values:

def get_available_models(category: str):
    models = {
        "car": ["sedan", "suv", "truck"],
        "bike": ["road", "mountain", "hybrid"]
    }
    return models.get(category, [])

@action("Select Vehicle", "Choose a vehicle type and model")
@action.input("category", type="select", choices=["car", "bike"], required=True)
@action.input("model", type="select", choices=get_available_models,
              depends_on="category", required=True)
def select_vehicle(category: str, model: str):
    return {"selected": f"{category} - {model}"}

Class-Based Actions

Organize related actions within classes:

from zelos_sdk.actions import action

class DeviceManager:
    def __init__(self):
        self.devices = {}

    @action("Start Device", "Start a device by ID")
    @action.input("device_id", type="text", required=True)
    def start_device(self, device_id: str):
        self.devices[device_id] = {"status": "running"}
        return {"device_id": device_id, "status": "started"}

    @action("Get Device Status", "Get current status of a device")
    @action.input("device_id", type="text", required=True)
    def get_device_status(self, device_id: str):
        return self.devices.get(device_id, {"status": "unknown"})

# Create instance to make actions available
device_manager = DeviceManager()

ActionsRegistry

Registries are used to collect actions. The default global registry is actions_registry.

The actions_registry discovers available actions by scanning the environment or by manual registration:

from zelos_sdk import actions_registry

# Register individual functions
actions_registry.register(add_numbers, name="math")

# Register class instances with all their actions
actions_registry.register(device_manager, name="devices")

# List all registered actions
namespaces = actions_registry.list_namespaces()
print(namespaces)  # {"math": ["add_numbers"], "devices": ["start_device", "get_device_status"]}

# Get specific action metadata
action_obj = actions_registry.get_action("math", "add_numbers")
schema = action_obj.to_schema()  # JSON Schema for validation

ActionsClient

The ActionsClient can be used to serve actions to the Zelos Agent and/or list, execute actions. See Using Custom Registries with ActionsClient

JSON Schema Generation

Actions automatically generate JSON Schema for validation and UI generation:

# Get the action object
action_obj = actions_registry.get_action("math", "add_numbers")

# Generate JSON Schema
schema = action_obj.to_schema()
print(schema)
# {
#   "title": "Add Numbers",
#   "description": "Add two numbers together",
#   "type": "object",
#   "properties": {
#     "x": {"type": "number", "title": "X", "description": "First number"},
#     "y": {"type": "number", "title": "Y", "description": "Second number"}
#   },
#   "required": ["x", "y"]
# }

# Generate UI Schema for form rendering
ui_schema = action_obj.to_ui_schema()

Making Actions Available

There are two ways to make your actions available to the Zelos Agent:

Using the Global Registry with init()

The init() function with actions=True is the simplest approach:

from zelos_sdk import init
from zelos_sdk.actions import action

@action("Process Data", "Process some data")
@action.input("value", type="number", required=True)
def process(value: float):
    return {"processed": value * 2}

# Make actions available to the Zelos Agent
init(actions=True)

Using Custom Registries with ActionsClient

For more control or isolated services, create your own ActionsClient with a custom registry:

from zelos_sdk.actions import action, ActionsRegistry, ActionsClient

@action("Process Data", "Process some data")
@action.input("value", type="number", required=True)
def process(value: float):
    return {"processed": value * 2}

# Create custom registry and register actions
custom_registry = ActionsRegistry()
custom_registry.register(process, name="data_processor")

# Create client and serve with custom registry
client = ActionsClient()
client.serve("my_service", actions_registry=custom_registry)

Multiple Actions in Custom Registries

You can register multiple actions in a custom registry:

from zelos_sdk.actions import action, ActionsRegistry, ActionsClient

@action("Add", "Add two numbers")
@action.input("a", type="number", required=True)
@action.input("b", type="number", required=True)
def add(a: float, b: float):
    return {"result": a + b}

@action("Multiply", "Multiply two numbers")
@action.input("x", type="number", required=True)
@action.input("y", type="number", required=True)
def multiply(x: float, y: float):
    return {"result": x * y}

# Create a custom registry for specific actions
math_registry = ActionsRegistry()
math_registry.register(add, name="math")
math_registry.register(multiply, name="math")

# Create client and serve with custom registry
client = ActionsClient()
client.serve("math_service", actions_registry=math_registry)

Class-Based Service Organization

Organize related actions into services. You can use the global registry, custom registries, or both:

from zelos_sdk import init, action, actions_registry


class MotorController:
    @action("Set Speed", "Set motor speed")
    @action.input("motor_id", type="text", required=True)
    @action.input("rpm", type="number", minimum=0, maximum=3000, required=True)
    def set_speed(self, motor_id: str, rpm: float):
        return {"motor_id": motor_id, "speed_set": rpm, "status": "running"}


motor_controller = MotorController()

# Option 1: Use global registry
actions_registry.register(motor_controller, name="motor_controller")
init(actions=True)

# Option 2: Use custom registry
from zelos_sdk.actions import ActionsRegistry, ActionsClient
custom_registry = ActionsRegistry()
custom_registry.register(motor_controller, name="motor_controller")
client = ActionsClient()
client.serve("motor_service", actions_registry=custom_registry)

Action Execution

Execute actions directly with validation:

# Execute action directly
result = action_obj.execute(x=5, y=3)
print(result)  # {"result": 8, "operation": "addition"}

# Validation happens automatically
try:
    action_obj.execute(x="invalid")  # Will raise ValidationError
except ValidationError as e:
    print(f"Validation failed: {e}")

Module Documentation

Module Description
Advanced Usage Deep dives into field types, validation, and complex patterns

The actions system provides a powerful foundation for creating interactive, validated Python functions that can be used in various contexts including web UIs, CLI tools, and automated systems.