Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GRPC API for javascript models with Nodejs s2i wrapper #224

Merged
merged 44 commits into from
Oct 1, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6382455
GRPC implementation
Sep 17, 2018
9e8b655
updating the environment file for grpc
Sep 17, 2018
db42271
initial proxy commits
ukclivecox Sep 18, 2018
89bd230
update examples with GRPC API
Sep 19, 2018
a672cb3
initial tensorrt integration and wrapper for python
ukclivecox Sep 20, 2018
8d2977f
nvidia server example initial commits
ukclivecox Sep 22, 2018
eeeb829
Merge branch 'master' into proxies
ukclivecox Sep 22, 2018
4a69252
first running version nvidia mnist example
ukclivecox Sep 22, 2018
3540f5f
ensure single context for nvidia inference connection
ukclivecox Sep 22, 2018
b6cea76
update graph visualizer
ukclivecox Sep 22, 2018
14b713c
initial tfserving commit
ukclivecox Sep 22, 2018
4cac7a5
initial tfserving mnist example with helm notebook
ukclivecox Sep 23, 2018
f9039de
updates to tfserving to fix return shape
ukclivecox Sep 23, 2018
0eaae1a
Update kubectl_demo_minikube_rbac.ipynb
benoitbayol Sep 24, 2018
24b23ee
Merge pull request #230 from benoitbayol/patch-1
ukclivecox Sep 24, 2018
2d4c786
Update epsilon-greedy example to Python 3
jklaise Sep 24, 2018
eddeabf
Update kubectl_demo_minikube_rbac.ipynb
benoitbayol Sep 24, 2018
0d589a9
Merge pull request #232 from benoitbayol/patch-2
ukclivecox Sep 24, 2018
6d90eed
update nvidia example to use helm chart
ukclivecox Sep 24, 2018
76a603f
update notebook examples for tfserving and nvidia
ukclivecox Sep 24, 2018
e6eb4a5
fix for missing image-pull-policy in ksonnet
gsunner Sep 25, 2018
6d06eb1
fix for missing image-pull-policy in core.libsonnet
gsunner Sep 25, 2018
03e3581
Merge pull request #235 from gsunner/image-pull-policy-ksonnet-fix
gsunner Sep 25, 2018
2f7d724
Update docs on proxies
ukclivecox Sep 25, 2018
0ab7c1c
re-creating model server for new format
Sep 25, 2018
4123b9d
Adding transformer as a template
Sep 25, 2018
0558394
Adding SendFeedback API for models
Sep 25, 2018
b99f2bf
add missing files
ukclivecox Sep 26, 2018
df02555
Merge pull request #231 from jklaise/python3-routers
ukclivecox Sep 26, 2018
3ebb9f9
fix typos in READMEs
ukclivecox Sep 26, 2018
43ee776
Merge pull request #234 from cliveseldon/proxies
ukclivecox Sep 26, 2018
7e72abb
Don't change CR when defaulting just update status. Also some small f…
ukclivecox Sep 26, 2018
6eb9309
Merge pull request #238 from cliveseldon/defaulting
ukclivecox Sep 26, 2018
b70d984
GRPC implementation
Sep 17, 2018
3819585
updating the environment file for grpc
Sep 17, 2018
3a5b3e0
update examples with GRPC API
Sep 19, 2018
25f0ebb
re-creating model server for new format
Sep 25, 2018
651294a
Adding transformer as a template
Sep 25, 2018
dff29d5
Adding SendFeedback API for models
Sep 25, 2018
5421480
updated notebooks to use new wrapper
Sep 26, 2018
a392f8d
Merge branch 'issue-216' of https://github.com/SachinVarghese/seldon-…
Sep 26, 2018
e393b8d
update docs for grpc implementation
Sep 27, 2018
dfe6d66
Adding SIGTERM responses for the gRPC server
Sep 29, 2018
0ad9eaf
Adding SIGTERM responses for the REST server
Sep 29, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions wrappers/s2i/nodejs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ COPY microservice.js /microservice

COPY package.json /microservice

COPY prediction_grpc_pb.js /microservice

COPY prediction_pb.js /microservice

RUN npm install

COPY ./s2i/bin/ /s2i/bin
Expand Down
21 changes: 20 additions & 1 deletion wrappers/s2i/nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ This builds the base wrapper image needed for any nodejs model to be deployed on
### Building s2i nodejs model Image

```
s2i build test/model-template-app seldonio/seldon-core-s2i-nodejs seldon-core-template-model
s2i build -E ./test/model-template-app/.s2i/environment test/model-template-app seldonio/seldon-core-s2i-nodejs seldon-core-template-model
```

This creates the actual nodejs model image as a seldon component
Expand All @@ -77,3 +77,22 @@ Make sure the current user can run npm commands.
```
make test
```

### GRPC code-generated Proto JS Files

This is the code pre-generated using protoc and the Node gRPC protoc plugin, and the generated code can be found in various `*_pb.js` files.
The creation of the grpc srevice assumes these files to be present.

```
cd ../../../proto/
npm install -g grpc-tools
grpc_tools_node_protoc --js_out=import_style=commonjs,binary:../wrappers/s2i/nodejs/ --grpc_out=../wrappers/s2i/nodejs --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` prediction.proto
cd ../wrappers/s2i/nodejs/
```

### Test using GRPC client

```
npm i
node grpc_client.js
```
61 changes: 61 additions & 0 deletions wrappers/s2i/nodejs/grpc_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

var messages = require("./prediction_pb");
var services = require("./prediction_grpc_pb");

var grpc = require("grpc");

function main() {
var client = new services.ModelClient(
"localhost:5000",
grpc.credentials.createInsecure()
);
var tensorData = new messages.Tensor();
tensorData.setShapeList([1, 10]);
tensorData.setValuesList([0, 0, 1, 1, 5, 6, 7, 8, 4, 3]);

var defdata = new messages.DefaultData();
defdata.setTensor(tensorData);
defdata.setNamesList([]);

var request = new messages.SeldonMessage();
request.setData(defdata);
client.predict(request, function(err, response) {
if (err) {
console.log(err);
} else {
console.log(
"Seldon Message => \n\nNames: ",
response.getData().getNamesList(),
"\n\nShape: ",
response
.getData()
.getTensor()
.getShapeList(),
"\n\nValues: ",
response
.getData()
.getTensor()
.getValuesList()
);
}
});
}

main();
112 changes: 86 additions & 26 deletions wrappers/s2i/nodejs/microservice.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const nj = require("numjs");
app.use(bodyParser.urlencoded({ extended: true }));
const grpc = require("grpc");
const grpc_messages = require("./prediction_pb");
const grpc_services = require("./prediction_grpc_pb");
const process = require("process");
let predict = null;
const port = process.env.PREDICTIVE_UNIT_SERVICE_PORT || 5000;

const loadModel = async function(model) {
model = "./model/" + model;
try {
Expand Down Expand Up @@ -58,6 +62,26 @@ const array_to_rest_data = function(array, original_datadef) {
return data;
};

const array_to_grpc_data = function(array, original_datadef) {
array = nj.array(array);

var defdata = new grpc_messages.DefaultData();
defdata.setNamesList(get_predict_classNames(array.shape[1]));
if (original_datadef["tensor"]) {
var tensorData = new grpc_messages.Tensor();
tensorData.setShapeList(array.shape.length > 1 ? array.shape : []);
tensorData.setValuesList(array.flatten().tolist());
defdata.setTensor(tensorData);
} else if (original_datadef["ndarray"]) {
datadef.setNdarray(array.tolist());
} else {
datadef.setNdarray(array.tolist());
}
var data = new grpc_messages.SeldonMessage();
data.setData(defdata);
return data;
};

const parser = new argparse.ArgumentParser({
description: "Seldon-core nodejs microservice builder",
addHelp: true
Expand All @@ -81,34 +105,70 @@ parser.addArgument("--persistence", {
});
const args = parser.parseArgs();

console.log(args.model, args.api, args.service, args.persistence);

if (args.service === "MODEL" && args.api === "REST") {
app.post("/predict", async (req, res) => {
try {
body = JSON.parse(req.body.json);
body = body.data;
} catch (msg) {
console.log(msg);
res.status(500).send("Cannot parse predict input json " + req.body);
}
if (predict && typeof predict === "function") {
result = predict(rest_data_to_array(body), body.names);
result = { data: array_to_rest_data(result, body) };
res.status(200).send(result);
} else {
console.log("Predict function not Found");
res.status(500).send(predict);
}
});
}

app.listen(port, async () => {
const getPredictFunction = async () => {
predict = await loadModel(
args.model,
args.api,
args.service,
args.persistence
);
console.log(`NodeJs Microservice listening on port ${port}!`);
});
if (predict && typeof predict === "function") {
console.log("Predict function loaded successfully");
createServer();
} else {
console.log("Predict function not Found ", predict);
process.exit(1);
}
};
console.log(args.model, args.api, args.service, args.persistence);
getPredictFunction();

const createServer = () => {
if (args.service === "MODEL" && args.api === "REST") {
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/predict", (req, res) => {
try {
body = JSON.parse(req.body.json);
body = body.data;
} catch (msg) {
console.log(msg);
res.status(500).send("Cannot parse predict input json " + req.body);
}
if (predict && typeof predict === "function") {
result = predict(rest_data_to_array(body), body.names);
result = { data: array_to_rest_data(result, body) };
res.status(200).send(result);
} else {
console.log("Predict function not Found");
res.status(500).send(predict);
}
});
app.listen(port, () => {
console.log(`NodeJs REST Microservice listening on port ${port}!`);
});
}

if (args.service === "MODEL" && args.api === "GRPC") {
function predictEndpoint(call, callback) {
let data = call.request.getData();
let body = { names: data.getNamesList() };

if (data.hasTensor()) {
data = data.getTensor();
body["tensor"] = {
shape: data.getShapeList(),
values: data.getValuesList()
};
} else {
body["ndarray"] = data.getNdarray();
}
result = predict(rest_data_to_array(body), body.names);
callback(null, array_to_grpc_data(result, body));
}
var server = new grpc.Server();
server.addService(grpc_services.ModelService, { predict: predictEndpoint });
server.bind("0.0.0.0:" + port, grpc.ServerCredentials.createInsecure());
server.start();
console.log(`NodeJs GRPC Microservice listening on port ${port}!`);
}
};
2 changes: 2 additions & 0 deletions wrappers/s2i/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"argparse": "1.0.10",
"body-parser": "1.18.3",
"express": "4.16.3",
"google-protobuf": "3.6.1",
"grpc": "1.15.1",
"numjs": "0.16.0"
}
}
Loading