Plugins

Plugins

Kōji has an integrated plugin system to extend three of its core algorithms:

  • Clustering
  • Routing
  • Bootstrapping

How to Use

Plugins are loaded from the respective plugins directory found in each of the three algorithm directories. A plugin can be a single file or a directory containing any number of files or directories. You can pass in your own arguments via the Kōji client or the API. Each arg should be separated by a space, keys starting with -- and the respective values following the keys. For example, --arg1 value1 --arg2 value2. Due to limits on the length of an input argument, the coordinates or GeoJSON Feature will be passed via stdin from Kōji to your plugin.

Single File

A plugin file can either be executable or a script that is run by an interpreter. By default, Kōji supports the following plugins without any additional args:

  • .sh will be executed with Bash
  • .js will be executed with Node
  • .ts will be executed with tsx (opens in a new tab)
  • .py will be executed with Python3
  • Extension-less, executable binaries

If you would like to use a different interpreter for a plugin you can add it to the respective args. For example, if you would like to use Bun, you can prefix your input arguments with bun file.ts.

Directory

A directory can also be used a plugin. The directory name will be used as the plugin name and you must add the relative path from the plugin folder to the plugin entry point to your input arguments. For example, bun my_plugin/index.ts.

Example Folder Structure & Usage

.
├── client
├── docs
├── or-tools
└── server
    ├── algorithms
├── clustering
   └── src
       └── plugins
           ├── plugin_1
                 # clustering_args = `python3 plugin_1/clustering_plugin.py`
              └── clustering_plugin.py
                  # clustering_args = `` - executed automatically with Node by Kōji
           ├── plugin_2.js
                  # clustering_args = `` - executed automatically Kōji
           └── plugin_3_binary
├── routing
   └── src
       └── plugins
           ├── plugin_1
                 # routing_args = `bash plugin_1/my_custom_plugin.sh`
              └── my_custom_plugin.sh
                  # routing_args = `bun plugin_2.ts`
           ├── plugin_2.ts
                  # routing_args = `` - executed automatically by Kōji
           └── plugin_3_binary
└── bootstrapping
└── src
└── plugins
├── plugin_1
      # bootstrapping_args = `node plugin_1/bootstrapping_plugin.js`
   └── bootstrapping_plugin.js
       # bootstrapping_args = `` - executed automatically by Kōji
├── plugin_2.sh
       # bootstrapping_args = `` - executed automatically by Kōji
└── plugin_3_binary
    └── src

Parsing Examples

Below are some examples demonstrating how the input args are parsed and passed along to your plugin. These are category agnostic. The Entry is the file in which the plugin must be executed from. This will either be an individual file that's directly placed in the plugin folder or a full path that includes a directory. The Args value is the value that you pass in via the Kōji client or API.

Example 1

  • Entry: cluster.py
  • Args: --foo 1 --bar 2
  • Radius: 70
  • Min Points: 3
  • Max Clusters: 500

Result: python3 cluster.py --foo 1 --bar 2 --radius 70 --min_points 3 --max_clusters 500 Stdin: 40.780374,-73.969161 40.252042,-73.882841 40.256022,-74.105120

Example 2

  • Entry: my_plugin/routing.js
  • Args: node my_plugin/routing.js --baz 10 --qux hello!

Result: node my_plugin/routing.js --baz 10 --qux hello! Stdin: 40.780374,-73.969161 40.252042,-73.882841 40.256022,-74.105120

Example 3

  • Entry: test.ts
  • Args: bun

Result: bun test.ts Stdin: 40.780374,-73.969161 40.252042,-73.882841 40.256022,-74.105120

The main takeaway is that the first and second arguments are optional. Once Kōji finds the first argument that is prefixed by --, it now assumes that the rest of the arguments are meant to be passed to the plugin. If your plugin is only a single file, you can omit the interpreter and file path, or you can just omit the file path, however you can not omit the interpreter and try to use a custom file path.

Clustering

stdin Value

The stdin value for a clustering plugin is a stringified list of points of n length: lat,lng lat,lng lat,lng .... These are the points that are to be clustered, e.g. spawnpoints or forts.

Automatically Appended Args:

  • radius
  • min_points
  • max_clusters

Example Usage

  • Plugin: custom.py
  • The plugin accepts the following custom args:
    • foo
    • bar

API Usage

{
  ... // other args
  "cluster_mode": "custom.py",
  "clustering_args": "--foo 1 --bar 123"
  ...
}

Client Usage

Set your options like so in the Kōji client drawer:

Clustering Plugin Example

Routing

stdin Value

The stdin value for the routing plugin is a stringified list of points of n length: lat,lng lat,lng lat,lng .... These are the cluster values.

Example Usage

  • Plugin: routing.js
  • The plugin accepts the following custom args:
    • baz
    • qux

API Usage

{
  ... // other args
  "sort_by": "routing.js",
  "routing_args": "--baz 10 --qux hello!"
  ...
}

Client Usage

Set your options like so in the Kōji client drawer:

Routing Plugin Example

Bootstrapping

stdin Value

The stdin value for the bootstrapping plugin is a GeoJSON Feature of either a Polygon or MultiPolygon type. This is the area that will be used to constrain the points that the bootstrap algorithm generates.

Automatically Appended Args:

  • radius

Example Usage

  • Plugin: abc/some_plugin.py
  • The plugin entry point is located in a directory so we must specify the interpreter and path.

API Usage

{
  ... // other args
  "calculation_mode": "abc",
  "bootstrapping_args": "python3 abc/some_plugin.py" // can be omitted but included here for demonstration purposes
  ...
}

Client Usage

Set your options like so in the Kōji client drawer:

Bootstrapping Plugin Example

Returning Results to Kōji

The results of your plugin must be returned to Kōji via stdout. The results must be a stringified list of points of n length: lat,lng lat,lng lat,lng .... In Python for example, this is as simple as printing the results. While Kōji attempts to filter out any unnecessary or invalid text that was logged, it's best not to log anything other than the final results.

Plugin Example