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
. An --input
arg is appended by Kōji, the values of which are described below in each respective section.
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 TS-Node.py
will be executed with Python- 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: python cluster.py --foo 1 --bar 2 --radius 70 --min_points 3 --max_clusters 500 --input 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! --input 40.780374,-73.969161 40.252042,-73.882841 40.256022,-74.105120
Example 3
- Entry:
test.ts
- Args:
bun
Result: bun test.ts --input 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
Input Value
The input 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
- input
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:
Routing
Input Value
The input 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.
Automatically Appended Args:
- input
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:
Bootstrapping
Input Value
The input 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
- input
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:
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
You can already view a live plugin example as the OR-Tools integration (TSP) has been reworker to be used with this plugin system. You can view the source code here (opens in a new tab).