Skip to content

Commit

Permalink
docs: plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
TurtIeSocks committed Dec 14, 2023
1 parent 31246e4 commit 960f3e3
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/pages/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"setup": "Setup",
"client": "Client",
"api-reference": "API Reference",
"plugins": "Plugins",
"algorithms": "Algorithms",
"integrations": "Integrations",
"development": "Development"
Expand Down
220 changes: 220 additions & 0 deletions docs/pages/plugins.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import Image from 'next/image'

# 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

```bash
.
├── 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

```json
{
... // 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:

<Image
src="/images/plugins/clustering.png"
alt="Clustering Plugin Example"
width={300}
height={300}
/>

## 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

```json
{
... // 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:

<Image
src="/images/plugins/routing.png"
alt="Routing Plugin Example"
width={300}
height={300}
/>

## 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

```json
{
... // 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:

<Image
src="/images/plugins/bootstrapping.png"
alt="Bootstrapping Plugin Example"
width={300}
height={300}
/>
Binary file added docs/public/images/plugins/bootstrapping.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/images/plugins/clustering.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/images/plugins/routing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion server/algorithms/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl Plugin {
));
}
let mut interpreter = match plugin.split(".").last() {
Some("py") => "python3",
Some("py") => "python",
Some("js") => "node",
Some("sh") => "bash",
Some("ts") => "ts-node",
Expand Down
11 changes: 9 additions & 2 deletions server/model/src/api/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,15 @@ impl Args {
let mode = get_enum(mode);
let route_split_level = validate_s2_cell(route_split_level, "route_split_level");
let routing_args = routing_args.unwrap_or("".to_string());
let clustering_args = clustering_args.unwrap_or("".to_string());
let bootstrapping_args = bootstrapping_args.unwrap_or("".to_string());

let mut clustering_args = clustering_args.unwrap_or("".to_string());
clustering_args += &format!(" --radius {}", radius);
clustering_args += &format!(" --min_points {}", min_points);
clustering_args += &format!(" --max_clusters {}", max_clusters);

let mut bootstrapping_args = bootstrapping_args.unwrap_or("".to_string());
bootstrapping_args += &format!(" --radius {}", radius);

if route_chunk_size.is_some() {
log::warn!("route_chunk_size is now deprecated, please use route_split_level")
}
Expand Down

0 comments on commit 960f3e3

Please sign in to comment.