webAI: Guide to Building Custom Elements

11 minute read

My Image

Overview

In the webAI platform, an Element is the basic building block of an AI pipeline. Think of it as a modular unit that either generates data, transforms it, or routes it further down the workflow. Elements can emit frames, process text, run models, or even orchestrate I/O across multiple data streams. They enable organizations to solve real business problems such as automating customer support, enriching knowledge management systems, accelerating content generation, or integrating AI-driven insights directly into business applications. For example, an Element might connect to a CRM to streamline sales workflows, ingest enterprise documents for retrieval-augmented generation, or process video feeds in real time for compliance and monitoring.

To standardize how developers create, configure, and connect these elements, the Element SDK provides a lightweight but powerful scaffolding layer. The ElementSDK provides the necessary boilerplate for making your custom code usable in the webAI platform.

What is an Element?

An Element is a building block used to create workflows or Flows in Navigator. There are two types of Elements: Built-in and Custom. Built-in Elements are provided through the official Element Registry and are part of the webAI platform. Custom elements are built using python and the ElementSDK.

Elements are Python packages that define the following: Inputs Outputs Settings Requirements Run Function

An Element can have an input, an output, both, or neither. I/O Example Input Receives data from another element Output Sends data to another element Input & Output Processes input, performs a transformation, and passes the result to another element None Stand-alone element (e.g., for model training or dataset generation)

Element Scaffolding

Every element follows a predictable project structure:

root-folder/
├── element_name/
│   ├── __init__.py   
│   ├── element.py   
├── setup.py          
└── requirements.txt  

Element Settings

Element settings are configurable parameters users can adjust at runtime in Navigator. The SDK supports a variety of settings: NumberSetting, TextSetting, BoolSetting.

Example:

class Settings(ElementSettings):
    color = TextSetting(...)
    delay = NumberSetting[int](...)

BoolSetting

bool_setting = BoolSetting(
   name="bool_setting",
   display_name="A Dropdown Toggle",
   default=True,
   description="Whether Dropdown should be displayed",
   required=False,
)

NumberSetting

number_setting = NumberSetting[int](
   name="number",
   display_name="A Number",
   description="This is a number",
   default=10,
   min_value=1,
   step=1,
)
dropdown_setting = TextSetting(
   name="dropdown_setting",
   display_name="Dropdown Setting",
   default="item1",
   description="An Item",
   valid_values=[
      "item1",
      "item2",
      "item3",
   ],
   hints=["dropdown"],
   required=False,
)

Hints

Some settings support hints that modify their rendered appearance in Navigator:

Class Hint Description
TextSetting dropdown Renders a select box populated with valid_values
TextSetting folder_path Renders a folder picker

Element Inputs and Outputs

Elements communicate through strongly-typed slots. Types include Frame, TextFrame, ImageFrame, MLXFrame, and Preview.

Example:

class Inputs(ElementInputs):
    default = Input[Frame]()

class Outputs(ElementOutputs):
    default = Output[Frame]()

Element Run Function

At the core of every element lies its run function, an asynchronous coroutine that drives the element’s execution. The Element SDK is designed around a continuous streaming model, meaning elements remain active and process data in real time. If your workflow requires batching or sequential execution, the recommended approach is to keep all elements running until the final element completes its task. Once any element terminates, the WebAI Runtime will automatically shut down the remaining connected elements to ensure a clean and consistent pipeline state.

Example:

async def run(self, process: Process):
    settings, outputs = process.settings, process.outputs
    self.reset_frame(settings.color.value)
    self.delay = settings.delay.value

    while True:
        await outputs.default.send(self.frame)
        await asyncio.sleep(self.delay / 1000)

Input Handling

When your element consumes data, you need a frame receiver function. Example relay:

async def frame_receiver(self, _: str, frame: Frame):
    await self.frame_queue.put(frame)

async def run(self, process: Process):
    outputs = process.outputs
    while True:
        frame = await self.frame_queue.get()
        await outputs.default.send(frame)
        self.frame_queue.task_done()

Create Helloworld Element

hello-world/
├── hello_world/
│   ├── __init__.py         
│   └── element.py       
├── requirements.txt
└── setup.py

Directory Structure

webAI elements require a defined directory structure.

$ mkdir -p helloworld/helloworld

Element Definition

Elements are defined by creating a setup.py under the element director where both element name and version are required. Optionally, if you are using poetry you can create a pyproject.toml.

$ vi helloworld/setup.py
from setuptools import find_packages, setup

setup(
    name="hello_world",
    version="0.1.0",
    packages=find_packages(),
)

Element Dependencies

Dependencies can be managed using a requirements.txt file.

$ vi helloworld/requirements.txt
--extra-index-url https://gitlab.com/api/v4/projects/49121232/packages/pypi/simple
webai_element_sdk==0.10.*

Element Boilerplate

Element settings goes in the element/element/element.py.

$ vi helloworld/helloworld/element.py

from webai_element_sdk.element.settings import ElementSettings, TextSetting

class Settings(ElementSettings):
    greeting = TextSetting(
        name="greeting",
        display_name="Greeting",
        default="Hello, World!",
        character_limit=100,
    )

Element code goes in the element/element/init.py.

$ vi helloworld/helloworld/__init__.py

import asyncio
from webai_element_sdk.element import CreateElement
from webai_element_sdk.process import Process, ProcessMetadata

from .element import Settings

class HelloWorld:
    async def run(self, process: Process):
        settings = process.settings
        await process.agent_comms.log("HelloWorld element started")

        try:
            while True:
                await process.agent_comms.log(settings.greeting.value)
                await asyncio.sleep(1)
        except asyncio.CancelledError:
            await process.agent_comms.log("HelloWorld element shutting down")
            raise


hello_world = HelloWorld()

process = CreateElement(
    Process(
        settings=Settings(),
        metadata=ProcessMetadata(
            id="helloworld-0001",
            name="hello_world",
            displayName="Hello World",
            version="0.1.0",
            description="Simplest element: logs a configurable greeting every second.",
        ),
        run_func=hello_world.run,
    )
)

Validate Python Environment

$ cd helloworld
python -c "import sys; print(sys.executable)"
/Users/ktenzer/python/webai-element-helloworld/helloworld/venv/bin/python

Set Interpreter in Visual Studio Code

{
  "python.analysis.extraPaths": [
    "/Users/ktenzer/python/helloworld/helloworld/venv/lib/python3.12/site-packages"
  ]
}

Import Custom Element

Once we have put together our custom element, we can import it into Navigator.

$ /Applications/Navigator.app/Contents/Resources/support/builder import helloworld

Jul 31 01:02:50.206 INF Using data directory path=/Users/keith.tenzer/.webai
Jul 31 01:02:50.206 INF Packaging source folder into .zip...
Jul 31 01:02:50.206 INF Generating publish.json file for element...
Jul 31 01:02:50.477 INF Python setup status=success
Jul 31 01:02:50.565 INF running command commandLabel=generate command="[/Users/keith.tenzer/.webai/elements/generator/.venv/bin/python3 -um webai_element_sdk generate --path /Users/keith.tenzer/python/elements/helloworld]"
Jul 31 01:02:50.735 INF Generated publish.json successfully.
Jul 31 01:02:50.745 INF Importing element .zip package...
Jul 31 01:02:50.751 INF Successfully imported element variant id=de8288be-f0bb-563d-a299-1e81d329744a
Jul 31 01:02:50.751 INF Cleaning up .zip package...
Jul 31 01:02:50.751 INF Element imported successfully.

Element Versioning

Elements are versioned by bumping the version in the __init__.py and setup.py followed by side-loading using the import command.

process = CreateElement(
    Process(
        settings=Settings(),
        metadata=ProcessMetadata(
            id="helloworld-0001",
            name="hello_world",
            displayName="Hello World",
            version="0.1.1",
            description="Simplest element: logs a configurable greeting every second.",
        ),
        run_func=hello_world.run,
    )
)
/Applications/Navigator.app/Contents/Resources/support/builder import helloworld

Configure Custom Element

Drop element into a Canvas in Navigator. Helloworld

Tip: Use the Initiator element to create test frames and validate your element.

Once a Custom Element is added to the canvas in Navigator it will show up as a package under ~/.webai/elements/elements folder.

Tip: It can be helpful to debug and test your Custom Elements directly inside the deployed package.

Testing

Using the webAI CLI tail log for debugging and testing elements.

$ tail -f ~/.webai/logs/runtime.log
Aug 21 11:13:51.381 INF installer/environment.go:157 Installing element dependencies label=helloworld:0.1.0 variantID=7cf75c48-3475-5fc6-b89c-837b5c9a986e
Aug 21 11:13:55.003 INF installer/environment.go:176 Installing element label=helloworld:0.1.0 variantID=7cf75c48-3475-5fc6-b89c-837b5c9a986e

Tip: To explore deployed WebAI elements, import your ~/.webai folder into VS Code. Inside elements/elements, you’ll find the source code for any element you’ve deployed to a Navigator Canvas. Just deploy an element, open its __init__.py, and you’ll have example code ready to use.

Next Steps

Now that you have completed helloworld see if you can integrate a Custom Element into a webAI Flow it is time to connect multiple Elements and build AI pipelines. In the below example Github repo, you will see how to create a MultiModal RAG inference and ingest pipeline using the Element SDK and webAI platform. Simply follow the steps outlined in the repo and explore the code.

https://github.com/ktenzer/webai-multimodal-rag

(c) 2025 Keith Tenzer