Skip to main content

Koios Component Builder

SDK and CLI for building component libraries for the Koios platform. Components are reusable logic blocks with typed inputs and outputs that get wired together and executed by the Koios Component Engine at configurable scan rates.

Installation

pip install koios-component-builder

Requires Python 3.12+.

Quick Start

1. Write a component

# my_library/math_ops.py
from koios_component_builder import (
    Component,
    ComponentCategory,
    ComponentIcon,
    Input,
    Output,
)


class SimpleAdder(Component):
    """A component that adds two numbers."""

    class Meta:
        icon = ComponentIcon.SUM
        category = ComponentCategory.MATH

    a: Input[float] = Input(default=0.0, description="First number")
    b: Input[float] = Input(default=0.0, description="Second number")
    result: Output[float] = Output(default=0.0, description="Sum of a and b")

    def execute(self) -> None:
        self.result = self.a + self.b

2. Define a library

# my_library/__init__.py
from koios_component_builder import ComponentLibrary
from .math_ops import SimpleAdder, Multiplier


class MyLibrary(ComponentLibrary):
    """My custom component library."""

    name = "my-library"
    major = 1
    minor = 0
    patch = 0
    description = "Custom math components"

    components = [SimpleAdder, Multiplier]


__all__ = ["MyLibrary", "SimpleAdder", "Multiplier"]

3. Export

koios-component-builder export my_library/

This creates a .kcl (Koios Component Library) package in dist/ that can be uploaded to Koios.

Component Lifecycle

When deployed to Koios, each execution cycle:

  1. Field Injection — Input and config values are set on the component
  2. Setup — setup() runs once before the first execute() (if overridden)
  3. Execute — execute() runs with current input values
  4. Output Propagation — Output values are sent to wired destinations

Your component implements setup() (optional) and execute() — the engine handles all wiring and data flow.

setup() — One-Time Initialization

Override setup() for expensive work that should only happen once, such as loading models, creating registries, or parsing configuration. All field values are available when setup() runs.

If setup() raises an exception, the component is marked FAILED and retried on the next cycle.

class UnitConverter(Component):
    """Converts Fahrenheit to Celsius using pint."""

    class Meta:
        icon = ComponentIcon.TRANSFORM
        category = ComponentCategory.TRANSFORM

    value: Input[float] = Input(default=0.0, description="Temperature in °F")
    decimals: NumberConfig = NumberConfig(
        default=2, min_value=0, max_value=6, description="Decimal places"
    )
    result: Output[float] = Output(default=0.0, description="Temperature in °C")

    def setup(self) -> None:
        from pint import UnitRegistry
        self._ureg = UnitRegistry()  # ~80ms — only runs once

    def execute(self) -> None:
        temp = self._ureg.Quantity(self.value, self._ureg.degF)
        self.result = round(temp.to(self._ureg.degC).magnitude, int(self.decimals))

For resources shared across all instances of the same component class, use a class-level guard instead:

class MyComponent(Component):
    def execute(self) -> None:
        if not hasattr(MyComponent, "_shared_model"):
            MyComponent._shared_model = load_model()
        self.result = MyComponent._shared_model.predict(self.input)

Field Types

Input / Output

from koios_component_builder import Component, Input, Output

class ExampleComponent(Component):
    # Supported types: float, int, bool, str, list, dict
    temperature: Input[float] = Input(default=0.0, description="Temperature in Celsius")
    count: Input[int] = Input(default=0, description="Item count")
    enabled: Input[bool] = Input(default=True, description="Enable processing")
    mode: Input[str] = Input(default="auto", description="Operating mode")

    alarm: Output[bool] = Output(default=False, description="High temperature alarm")
    status: Output[str] = Output(default="ok", description="Current status")

    def execute(self) -> None:
        if self.enabled and self.temperature > 100:
            self.alarm = True
            self.status = "overtemp"
        else:
            self.alarm = False
            self.status = "ok"

Config Fields

Config fields are set when the component instance is created in the Koios UI and remain constant during execution. They appear as configuration controls on the component node.

from koios_component_builder import (
    Component, Input, Output,
    NumberConfig, StringConfig, ChoiceConfig, BoolConfig,
)

class ConfigurableComponent(Component):
    # Numeric with min/max validation
    threshold: NumberConfig = NumberConfig(
        default=75.0, min_value=0.0, max_value=100.0,
        description="Alert threshold"
    )

    # Dropdown with predefined options
    mode: ChoiceConfig = ChoiceConfig(
        default="average", choices=["average", "median", "max"],
        description="Calculation mode"
    )

    # Text with optional regex validation
    label: StringConfig = StringConfig(
        default="Sensor", description="Display label"
    )

    # Boolean toggle
    verbose: BoolConfig = BoolConfig(
        default=False, description="Enable verbose output"
    )

    value: Input[float] = Input(default=0.0)
    alert: Output[bool] = Output(default=False)

    def execute(self) -> None:
        self.alert = self.value > self.threshold

File Fields

FileConfig renders as an upload control in the Koios UI. At runtime the component receives a ComponentFile handle — not a raw path — pointing at the file the operator uploaded for that instance. Each instance has its own file, and uploads are versioned, so an operator can swap a model and revert.

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"],   # leading dot optional, case ignored
        mime_types=["application/octet-stream"],
        max_bytes=200 * 1024 * 1024,
        required=True,
    )

    def setup(self) -> None:
        # required=True, so Koios guarantees the file is here before setup runs
        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(), not execute() — setup() runs once, and Koios re-runs it after an operator replaces the file, so a swap takes effect without a restart.

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

Behavior
required=True Koios fails the instance with a specific message before setup() runs. Component code can assume the file is present.
required=False The field is None. Guard with if self.field:.

Handle API — path, name, suffix, size_bytes, content_type, sha256, uploaded_at, version, plus read_bytes(), read_text(), open() and exists(). Reading through the handle rather than calling the open builtin keeps your component clear of the security audit's file-I/O review tier.

class Lookup(Component):
    table: FileConfig = FileConfig(extensions=[".csv"])  # optional

    def setup(self) -> None:
        self.rows = self.table.read_text().splitlines() if self.table else []

Koios enforces the constraints server-side, so your declaration holds no matter what uploads the file. Extension is the deterministic gate; MIME only filters the file dialog, since a browser can claim anything and .onnx is in no MIME registry. max_bytes is optional — omit it and a platform ceiling applies — and a value above that ceiling is clamped down to it, so a field can lower the cap but never raise it. Files are never executed by Koios; they are handed to the component that asked for them.

HistoryInput

HistoryInput fields provide access to historical tag data via InfluxDB. They must be wired to a HISTORY connector on the environment canvas.

from koios_component_builder import Component, HistoryInput, Output

class TrendAnalyzer(Component):
    """Calculates trend from historical data."""

    sensor_history: HistoryInput = HistoryInput(
        description="Historical sensor readings"
    )
    trend: Output[float] = Output(default=0.0, description="Trend slope")

    def execute(self) -> None:
        if self.sensor_history is None:
            return  # Not wired to a history connector

        # Fetch last hour of data, max 200 samples
        df = self.sensor_history.get_history(
            period_seconds=3600,
            num_samples=200,
        )
        # df has columns: timestamp, value
        if not df.empty:
            values = df["value"].tolist()
            self.trend = values[-1] - values[0]

Component Metadata

Customize how components appear in the Koios UI:

class MyComponent(Component):
    """Component description shown in the UI."""

    class Meta:
        icon = ComponentIcon.CHART_LINE       # Tabler icon name
        category = ComponentCategory.ANALYSIS  # UI grouping
        canvas_width = 8                       # Node width (4–15 grid units, default: 6)
        canvas_minimal = False                 # Compact mode (no header/footer)

        # Optional: component-specific version (overrides library version)
        major = 2
        minor = 1
        patch = 0
        prerelease = "beta"

Icons — Any Tabler icon name in kebab-case. Common constants: SUM, CALCULATOR, CHART_LINE, GAUGE, THERMOMETER, FILTER, WAVE_SINE, TOGGLE_LEFT, ALERT_TRIANGLE, TRANSFORM.

Categories — Standard constants: MATH, STATISTICS, LOGIC, ANALYSIS, TRANSFORM, FILTER, CONTROL, MONITORING. Custom strings are also accepted.

Pin Layout

By default pins are laid out on the canvas in the order they appear in your class body. For control-systems blocks where grouping matters — setpoints together, tuning constants together, status outputs apart — you can declare an explicit arrangement in Meta and insert gaps between pins. The arrangement ships in the .kcl manifest so every instance of the component starts with the same visual default. Users can further tweak the layout per-instance from the Koios UI.

from koios_component_builder import (
    Component,
    ComponentCategory,
    ComponentIcon,
    Gap,
    Input,
    NumberConfig,
    Output,
)


class PIDController(Component):
    """Discrete PID controller with anti-windup."""

    class Meta:
        icon = ComponentIcon.GAUGE
        category = ComponentCategory.CONTROL
        canvas_width = 8

        # Group setpoint + process variable, give the tuning constants their
        # own visual block, separate the enable/reset inputs at the bottom,
        # and keep the diagnostic outputs distinct from the control output.
        inputs_layout = [
            "setpoint",
            "process_variable",
            Gap(),
            "kp",
            "ki",
            "kd",
            Gap(size=2),
            "enable",
            "reset",
        ]
        outputs_layout = [
            "control_output",
            Gap(),
            "saturated",
            "integral",
        ]

    setpoint: Input[float] = Input(default=0.0, description="Target value")
    process_variable: Input[float] = Input(default=0.0, description="Measured value")
    kp: Input[float] = Input(default=1.0, description="Proportional gain")
    ki: Input[float] = Input(default=0.0, description="Integral gain")
    kd: Input[float] = Input(default=0.0, description="Derivative gain")
    enable: Input[bool] = Input(default=True, description="Enable control")
    reset: Input[bool] = Input(default=False, description="Reset integrator")

    output_min: NumberConfig = NumberConfig(default=-100.0, description="Output lower bound")
    output_max: NumberConfig = NumberConfig(default=100.0, description="Output upper bound")

    control_output: Output[float] = Output(default=0.0, description="Manipulated variable")
    saturated: Output[bool] = Output(default=False, description="Output is at a limit")
    integral: Output[float] = Output(default=0.0, description="Current integrator state")

    def execute(self) -> None:
        ...  # PID math

Gap(size=1) — Insert a vertical spacer between pins. size is in grid units (1 row = the height of one pin); defaults to 1.

Validation — Strings in inputs_layout / outputs_layout are checked against your declared Input / Output / HistoryInput fields at class-creation time. A typo raises ComponentDefinitionError on import, not at runtime.

Partial layouts — Pins declared on the class but absent from the layout list are appended in declaration order, so you can add new pins to a component without touching the layout.

Empty layouts — If neither list is declared, pins use class-body order with no gaps — the same behavior components had before this feature.

Note on Input(order=...) — The old per-field order= parameter is deprecated for Input, Output, and HistoryInput. Use Meta.inputs_layout / Meta.outputs_layout instead — it gives finer control (gaps, explicit grouping) and the contract is clearer to read at the top of the class. The order= parameter on Config family fields is unaffected.

Dependencies

Libraries can declare third-party Python package dependencies. The builder resolves them against the Koios platform manifest to determine what's pre-installed in the container vs. what needs bundling.

class MyProtocolLibrary(ComponentLibrary):
    name = "my-protocol-library"
    major = 1
    minor = 0
    dependencies = ["crcmod", "minimalmodbus>=2.0"]
    components = [MyDevice]

Three tiers:

Tier Description Example
Platform Pre-installed in the Koios container numpy, pandas, scipy
Bundled Downloaded and included in the .kcl crcmod, pint
SDK Always available (koios-component-builder itself) pydantic, click
# Export without bundling (warns about non-platform deps)
koios-component-builder export my_library/

# Bundle non-platform dependencies into the .kcl (deprecated — prefer a
# package stack attached to the component environment in Koios)
koios-component-builder export my_library/ --include-deps

# Target specific platforms
koios-component-builder export my_library/ --include-deps \
    --platform manylinux2014_x86_64 --platform manylinux2014_aarch64

# List available platform packages
koios-component-builder platform-packages

System binaries

platform-packages lists Python distributions only. Some libraries shell out to a solver or CLI tool instead, and those are installed as system packages in the Koios image — they will never appear in that listing, and declaring them as a dependency will not work.

Binary Provides Available from
glpsol GLPK — LP and MIP solver Koios 1.2.0

A pyomo model can call it directly:

import pyomo.environ as pyo

results = pyo.SolverFactory("glpk").solve(model)

pyomo itself is not pre-installed — declare it as a dependency and export with --include-deps.

For a solver that ships as a Python wheel and needs no system package, use HiGHS via highspy: add it to dependencies and it bundles like any other third-party package. HiGHS is generally faster than GLPK on larger LP/MIP problems.

Exporting a stack bundle (.kps)

A component stack is a named, isolated set of packages inside Koios that component environments can be attached to. It exists for dependencies that conflict with the platform's own — a library capping pandas<3 when the container ships pandas 3, for example.

Wheels can be uploaded one at a time in the Koios UI, or exported here as a single .kps bundle:

# Bundle a requirements file (repeatable; --name defaults to the file's name)
koios-component-builder export-stack -r requirements.txt

# Bundle everything installed in the environment you build in
koios-component-builder export-stack --from-env --name ml-tools

# Target a specific platform / Python version, and allow a longer download
koios-component-builder export-stack -r requirements.txt \
    --platform manylinux2014_x86_64 --python-version 3.12 --timeout 1800

Wheels are downloaded for the target platforms — linux amd64 + arm64 by default — never copied out of your local site-packages, so a bundle built on a Mac installs on the on-prem Linux container. Packages the Koios container already provides are pinned during resolution (a hard conflict fails the download rather than shipping a mismatched transitive dependency) and then excluded from the bundle, exactly as --include-deps treats them. Each target platform is resolved in its own download, so a package with per-architecture wheels ships one for each.

Every target platform must resolve. A package with no aarch64 wheel fails the whole export rather than producing a bundle that installs on one architecture and silently claims both. Two ways forward: pass --platform manylinux2014_x86_64 (or whichever architecture you deploy on) to bundle for just that one, or pin the package to a version that publishes wheels for both. The same applies when the platforms resolve a package to different versions — the manifest records one version per package, so the export names the package and the versions and stops.

Requirements files may include other files with -r other.txt; other pip option lines (--index-url, -e, -f) are rejected. --from-env additionally leaves out the SDK's own dependencies — pydantic, click, packaging and what they pull in are always installed alongside the builder, and the container already provides them. Every export ends by listing what it withheld and why: --from-env accounts for every installed name, and a requirements export names the requirements you gave it that the container already provides. Dependencies pulled in transitively are resolved by pip and reported only in the log.

No Koios distribution is ever bundled. koios-component-builder, koios-component-engine, koiosutility and koioslicense are the packages the engine loads your components with; a stack carrying its own copy replaces them at import time and the worker cannot start. --from-env leaves every koios* distribution out — the machine you build on always has the SDK installed — and naming one of the four in a requirements file stops the export.

Import the resulting file in the Koios UI under Components > Stacks.

ml-tools.kps
├── manifest.json                              # Bundle metadata
└── wheels/
    ├── mlflow-3.13.0-py3-none-any.whl
    └── pandas-2.3.3-cp312-cp312-manylinux2014_x86_64.whl

manifest.json records the bundle format version, stack name, build time, target Python version and platforms, the requirements it was resolved from, and every bundled package with its wheel filenames.

Trust — code installed into a stack runs with full platform privileges on the Koios server. Only bundle wheels from sources you trust.

Bundling a library's dependencies into its .kcl with --include-deps is deprecated and will be removed in a future major release. Provide third-party packages by attaching a package stack to the component environment in Koios instead: stacks are curated per environment, isolated from the platform's versions, and shared across every library that runs there, where bundled wheels are injected into whichever worker runs the component. --include-deps continues to work in the meantime and prints a warning.

Package Format

The export command creates a .kcl (Koios Component Library) package — a ZIP archive:

my-library-1.0.0.kcl
├── manifest.json                              # Library metadata
├── my_library-1.0.0-py3-none-any.whl         # Python wheel
└── deps/                                      # Bundled dependency wheels (if any)
    ├── crcmod-1.7-cp312-...-x86_64.whl
    └── crcmod-1.7-cp312-...-aarch64.whl

Security Audit

Every .kcl export automatically runs a static security analysis on your source code. The audit classifies patterns into three tiers:

Tier Meaning Effect
✅ ALLOW Safe — no friction Not reported
⚠️ REVIEW Flagged for attention, but build continues Logged as warning, yellow badge in Koios UI
❌ DENY Blocked Export fails unless --allow-unsafe is used

What gets flagged

DENY — patterns that have no legitimate use in a component:

  • Dangerous imports: os, subprocess, socket, threading, pickle, ctypes, sys, etc.
  • Unsafe builtins: eval(), exec(), compile(), __import__()
  • Sandbox escape patterns: __subclasses__, __builtins__, __code__, __globals__

REVIEW — patterns that are often legitimate but worth being aware of:

  • Filesystem imports: pathlib, io, csv (common for loading AI model files in setup())
  • open() calls (useful for reading model weights or config files)
  • Unknown third-party packages not in the platform allow-list

Audit command

Run the audit without building a .kcl:

# Audit and print results
koios-component-builder audit my_library/

# Output as JSON (for CI integration)
koios-component-builder audit my_library/ --json

# Audit with a custom policy
koios-component-builder audit my_library/ --policy security_policy.yaml

Exit code is 1 if any DENY findings are present, 0 otherwise — suitable for CI pipelines.

Custom policies

Override the defaults by providing a YAML policy file:

# security_policy.yaml
policy_version: "1.0"

imports:
  allow:
    - my_internal_sdk      # Trust your own internal package
    - requests             # Allow HTTP if your components genuinely need it
  deny:
    - pandas               # Tighten if you want to forbid heavy deps

builtins:
  review:
    - open                 # Already REVIEW by default; shown here for reference

attributes:
  deny:
    - __dict__             # Add to deny if you want stricter introspection rules

Policy overrides work bidirectionally — you can move items to a less restrictive tier (e.g. DENY → ALLOW) or a more restrictive one (e.g. ALLOW → DENY). Items are removed from conflicting tiers automatically.

koios-component-builder export my_library/ --policy security_policy.yaml

Overriding for power users

If your library has a legitimate reason for a flagged pattern (e.g. loading an ONNX model from disk in setup()), the recommended approach is to use pathlib / io which are REVIEW-tier rather than DENY. For edge cases that genuinely need a denied import:

koios-component-builder export my_library/ --allow-unsafe

The .kcl is still created, but the security_audit in manifest.json records passed: false — the Koios UI will display a warning badge when the library is uploaded.

CLI Reference

# Export a library to a .kcl package
koios-component-builder export <source_path> [OPTIONS]

Options:
  -o, --output PATH        Output directory (default: dist/)
  --include-deps           Bundle non-platform dependencies (deprecated)
  --platform TEXT          Target platform tag (repeatable)
  --allow-unsafe           Create .kcl even with DENY findings
  --policy PATH            Custom security policy YAML file

# Export a stack bundle to a .kps package
koios-component-builder export-stack [OPTIONS]

Options:
  -r, --requirements PATH  Requirements file to bundle (repeatable)
  --from-env               Bundle the current environment instead
  --name TEXT              Stack name (required with --from-env)
  -o, --output PATH        Output directory (default: dist/)
  --platform TEXT          Target platform tag (repeatable)
  --python-version TEXT    Python version to resolve for (default: container's)
  --timeout INTEGER        Seconds to allow the download (default: 900)

# Audit a library without building
koios-component-builder audit <source_path> [OPTIONS]

Options:
  --policy PATH            Custom security policy YAML file
  --json                   Output results as JSON

# List pre-installed platform packages
koios-component-builder platform-packages

Local Testing

Test components locally before deploying:

from my_library.math_ops import SimpleAdder

adder = SimpleAdder("test-instance")
adder.a = 5.0
adder.b = 3.0
adder.execute()

print(f"Result: {adder.result}")  # Result: 8.0

License

Copyright Ai-OPs, Inc. All rights reserved.

Metadata

Release files for koios-component-builder 1.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for koios-component-builder 1.3.1
File Size Uploaded
koios_component_builder-1.3.1.tar.gz 136.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for koios-component-builder 1.3.1
File Interpreter ABI Platform
koios_component_builder-1.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 217.3 kB

Release files / koios_component_builder-1.3.1.tar.gz

Download URL koios_component_builder-1.3.1.tar.gz
Size 136.3 kB
Tags Source
SHA-256 checksum
How to use checksums
3dfd986bed69909f9c209d78a7ede8ddf5d7e06898a15eb8ecdc26a64182dd77
BLAKE2b-256 checksum
How to use checksums
e6c4ea439fde8c519eb0d19b18457aa86c6a564fc7dbc197c49ebb5bd60def97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / koios_component_builder-1.3.1-py3-none-any.whl

Download URL koios_component_builder-1.3.1-py3-none-any.whl
Size 80.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4f4ad5775856d56cf076d1794fff95ce1bb9352d77d8e219e934b7ef4362613d
BLAKE2b-256 checksum
How to use checksums
0e62e83f09ef670efbddab7e9d2f3b7c03ce63d8eb572f0377d514c6c3116312
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.3.1 This release

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page