Ai-OPs
ai-ops.com
Docs
/
Components
/

Building Components

Building Components

The Koios Component Builder lets you build custom component types in Python, package them as libraries, and upload them to Koios for real-time execution. This page is a high-level overview of the workflow. The full reference — field types, files, pin layout, dependencies, stacks, and the security audit — is in Component Builder SDK.

Overview

A component is a Python class that defines typed inputs, outputs, and configuration fields. The component engine calls your execute() method on every scan cycle, passing in the latest input values and reading back your outputs.

from koios_component_builder import Component, Input, Output

class TemperatureConverter(Component):
    """Converts Celsius to Fahrenheit."""

    celsius: Input[float] = Input(default=0.0, description="Temperature in Celsius")
    fahrenheit: Output[float] = Output(default=32.0, description="Temperature in Fahrenheit")

    def execute(self) -> None:
        self.fahrenheit = self.celsius * 9 / 5 + 32

When deployed to Koios, this component appears on the canvas with a celsius input port and a fahrenheit output port. Wire a tag to the input, wire the output to another tag or component, and the conversion runs automatically at the environment's scan rate.

Development Workflow

1. Write          2. Package         3. Upload          4. Wire & Run
   Component  ───►   .kcl file  ───►   to Koios    ───►   on Canvas
   (Python)        (CLI tool)        (drag & drop)      (visual editor)
  1. Write your component classes using the SDK's base classes and field descriptors
  2. Package them into a library using the koios-component-builder CLI tool
  3. Upload the .kcl file through the Koios UI at Components > Libraries
  4. Wire instances on an environment canvas and enable the environment

What You Can Build

Component TypeDescription
Data processingScaling, filtering, moving averages, unit conversion
Control logicPID controllers, setpoint management, cascade loops
Decision logicState machines, alarm rules, threshold detection, latches
AnalyticsRolling statistics, trend analysis, anomaly detection
Custom protocolsProprietary device adapters, data format converters

Key Concepts

Fields

Components declare their interface using typed field descriptors:

Field TypePurpose
InputReceives data from a wired tag, another component, or a manual value
OutputProduces data that can be wired to tags or other components
ConfigStatic settings configured once per instance (numbers, text, booleans, dropdowns)
FileConfigA file the operator uploads for that instance — a model, a lookup table, a calibration blob
HistoryInputProvides on-demand access to a tag's historical time-series data

File Fields (koios-component-builder 1.3+)

Some components need a file rather than a value — a trained model, a lookup table, a calibration curve. Declare a FileConfig and Koios renders an upload control on the instance's Configuration tab. At runtime your component receives a handle to the file that instance's operator uploaded, so two instances of the same component can run different models.

from koios_component_builder import Component, FileConfig, Input, Output


class Scorer(Component):
    reading: Input[float] = Input(default=0.0)
    score: Output[float] = Output(default=0.0)

    model_file: FileConfig = FileConfig(
        description="Trained ONNX model",
        extensions=[".onnx", ".tflite"],
        max_bytes=200 * 1024 * 1024,
        required=True,
    )

    def setup(self) -> None:
        import onnxruntime

        self.session = onnxruntime.InferenceSession(str(self.model_file.path))

    def execute(self) -> None:
        self.score = float(self.session.run(None, {"x": [[self.reading]]})[0])

Load the file in setup() rather than execute(). setup() runs once, and Koios re-runs it after an operator replaces or reverts the file, so a swap takes effect without restarting anything.

required decides what happens when no file has been uploaded, so you never write that check yourself:

SettingBehavior
required=TrueThe instance fails with a specific message before setup() runs. Your code can assume the file is there.
required=FalseThe field is None. Guard it with if self.field:.

The handle exposes path, name, suffix, size_bytes, content_type, sha256, uploaded_at and version, plus read_bytes(), read_text(), exists() and a read-only open(). Read through the handle rather than the built-in open — it keeps your component clear of the security review tier that direct file access falls into.

Koios enforces extensions and max_bytes when the operator uploads, not just in the browser, so your declaration holds regardless of how the file was sent. max_bytes is optional: leave it off and a platform-wide ceiling applies, and a value above that ceiling is reduced to it — a field can lower the cap but never raise it. mime_types only filters the file picker, since a browser can report anything and many model formats have no registered type.

Koios never executes an uploaded file. It stores the bytes and hands them to the component that asked for them.

State

Components can maintain internal state between execution cycles. Instance variables set in __init__ or during execute() persist across cycles, useful for integrators, moving averages, edge detectors, and any logic that depends on previous values.

Libraries

Components are organized into libraries. A library is a named, versioned collection of component types. When you upload a new version of an existing library, Koios offers a migration flow that maps existing instances to the updated component definitions.

A library's name must start with a lowercase letter and may otherwise contain lowercase letters, digits, hyphens, and underscores. The builder rejects anything else when the library class is defined, so a name problem surfaces while you are writing the library rather than at upload.

Metadata

Each component can declare visual metadata (icon, category, canvas width) that controls how it appears on the canvas. The SDK provides a catalog of icons and categories to choose from.

Pin Layout (koios-component-builder 1.1+)

By default, pins appear on the canvas in declaration order. To control ordering and grouping, declare inputs_layout and outputs_layout on the component's Meta class:

from koios_component_builder import Component, Input, Output, Gap

class PIDController(Component):
    sensor: Input[float] = Input(default=0.0)
    setpoint: Input[float] = Input(default=0.0)
    enable: Input[bool] = Input(default=True)
    output: Output[float] = Output(default=0.0)

    class Meta:
        inputs_layout = ["sensor", "setpoint", Gap(), "enable"]
        outputs_layout = ["output"]

Gap() (or Gap(size=2) for wider spacing) inserts visual separation between pins. The layout you declare is the component-type default. Users can override it per instance on the canvas.

Meta.inputs_layout and Meta.outputs_layout replace the older FieldDescriptor(order=...) convention, which is deprecated as of koios-component-builder 1.1 (manifest sdk_base_version 3) and scheduled for removal in 2.0.

Wire Contract

The koios_component_builder.wire_contract module exposes the platform's canonical pin type vocabulary (int, float, bool, str, list, dict, plus the HistoryProvider sentinel) and the coercion rules that the canvas and engine enforce. Use it if you need to validate wire compatibility outside the platform (for example, in unit tests for a custom component library) so your assertions match what Koios actually allows.

Getting Started

For the full development guide — installation, field types, configuration, files, historical inputs, packaging, and local testing — see Component Builder SDK.

The builder documentation includes:

  • Installation and setup instructions
  • Complete API reference for all field types and base classes
  • CLI reference for packaging and exporting libraries

Koios ships with the Core Library — 40+ ready-made components covering math, logic, statistics, timers, counters, signal processing, and alarms — which is a good reference for how components are written.

What's Next