Skip to content

Commit

Permalink
fix: go back to stdin due to arg length limits
Browse files Browse the repository at this point in the history
  • Loading branch information
TurtIeSocks committed Feb 28, 2024
1 parent 2479d8d commit 0b68666
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 39 deletions.
33 changes: 15 additions & 18 deletions docs/pages/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ Kōji has an integrated plugin system to extend three of its core algorithms:

## 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.
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 TS-Node
- `.py` will be executed with Python
- `.ts` will be executed with [tsx](https://www.npmjs.com/package/tsx)
- `.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`.
Expand Down Expand Up @@ -82,36 +82,38 @@ Below are some examples demonstrating how the input args are parsed and passed a
- 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`
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! --input 40.780374,-73.969161 40.252042,-73.882841 40.256022,-74.105120`
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 --input 40.780374,-73.969161 40.252042,-73.882841 40.256022,-74.105120`
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

### Input Value
### stdin 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.
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
- input

### Example Usage

Expand Down Expand Up @@ -144,13 +146,9 @@ Set your options like so in the Kōji client drawer:

## Routing

### Input Value
### stdin 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
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

Expand Down Expand Up @@ -183,14 +181,13 @@ Set your options like so in the Kōji client drawer:

## Bootstrapping

### Input Value
### stdin 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.
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
- input

### Example Usage

Expand Down
31 changes: 14 additions & 17 deletions or-tools/tsp/tsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,29 +172,26 @@ int main(int argc, char *argv[])
RawInput points;
std::vector<std::string> stringPoints;

std::string line;
while (std::getline(std::cin, line, ' ') && !line.empty())
{
auto coordinates = split(line, ',');
if (coordinates.size() == 2)
{
double lat = std::stod(coordinates[0]);
double lng = std::stod(coordinates[1]);
points.push_back({lat, lng});
stringPoints.push_back(line);
}
}

for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
if (arg.find("--") == 0)
{
std::string key = arg.substr(2);
if (key == "input")
{
std::string pointsStr = argv[++i];
std::vector<std::string> pointStrings = split(pointsStr, ' ');
for (const auto &pointStr : pointStrings)
{
auto coordinates = split(pointStr, ',');
if (coordinates.size() == 2)
{
double lat = std::stod(coordinates[0]);
double lng = std::stod(coordinates[1]);
points.push_back({lat, lng});
stringPoints.push_back(pointStr);
}
}
}
else if (i + 1 < argc)
if (i + 1 < argc)
{
args[key] = argv[++i];
}
Expand Down
30 changes: 26 additions & 4 deletions server/algorithms/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::Display;
use std::io::{self, BufRead, BufReader};
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;
use std::process::{Command, Stdio};
use std::time::Instant;
Expand Down Expand Up @@ -67,7 +67,7 @@ impl Plugin {
));
}
let mut interpreter = match plugin.split(".").last() {
Some("py") => "python",
Some("py") => "python3",
Some("js") => "node",
Some("sh") => "bash",
Some("ts") => "ts-node",
Expand Down Expand Up @@ -113,7 +113,8 @@ impl Plugin {
std::io::ErrorKind::NotFound,
format!("{plugin} is a directory, not a file, something may not be right with the provided args"),
));
} else if path.exists() {
}
if path.exists() {
plugin_path = path.display().to_string();
log::info!("{interpreter} {plugin_path} {}", args.join(" "));
} else {
Expand Down Expand Up @@ -175,7 +176,6 @@ impl Plugin {
};
let mut child = match child
.args(self.args.iter())
.args(&["--input", &input])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
Expand All @@ -184,6 +184,28 @@ impl Plugin {
Err(err) => return Err(err),
};

let mut stdin = match child.stdin.take() {
Some(stdin) => stdin,
None => {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Failed to open stdin",
));
}
};

std::thread::spawn(move || match stdin.write_all(input.as_bytes()) {
Ok(_) => match stdin.flush() {
Ok(_) => {}
Err(err) => {
log::error!("failed to flush stdin: {}", err);
}
},
Err(err) => {
log::error!("failed to write to stdin: {}", err)
}
});

let stdout = child
.stdout
.take()
Expand Down

0 comments on commit 0b68666

Please sign in to comment.