├── VERSION
├── evaluator-onnx
├── src
│ ├── main
│ │ ├── resources
│ │ │ └── reference.conf
│ │ └── java
│ │ │ └── com
│ │ │ └── ovh
│ │ │ └── mls
│ │ │ └── serving
│ │ │ └── runtime
│ │ │ └── onnx
│ │ │ └── OnnxEvaluatorManifest.java
│ └── test
│ │ └── resources
│ │ └── onnx
│ │ ├── iris
│ │ ├── single_gen.json
│ │ ├── batch_gen.json
│ │ ├── iris.onnx
│ │ ├── single.json
│ │ ├── batch.json
│ │ ├── manifest.json
│ │ └── manifest-same-output.json
│ │ ├── titanic
│ │ ├── single.json
│ │ ├── single_gen.json
│ │ ├── pipeline_titanic.onnx
│ │ ├── batch.json
│ │ ├── batch_gen.json
│ │ └── manifest.json
│ │ └── adult
│ │ ├── transformation.onnx
│ │ └── single_gen.json
├── pom.xml
└── python
│ └── iris.py
├── CHANGELOG.md
├── evaluator-torch
├── converter
│ └── requirements.txt
├── src
│ ├── test
│ │ ├── resources
│ │ │ ├── model.ts
│ │ │ └── manifest.json
│ │ └── java
│ │ │ └── com
│ │ │ └── ovh
│ │ │ └── mls
│ │ │ └── serving
│ │ │ └── runtime
│ │ │ └── torch
│ │ │ └── TorchScriptEvaluatorManifestTest.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ └── torch
│ │ └── TorchScriptEvaluatorManifest.java
├── README.md
├── Makefile
└── pom.xml
├── examples
├── Dockerfile
├── 2d_savedmodel.zip
├── pipeline_titanic.onnx
└── manifest.json
├── api
└── src
│ ├── test
│ ├── resources
│ │ ├── tensorflow
│ │ │ ├── mnist
│ │ │ │ ├── model
│ │ │ │ │ ├── variables
│ │ │ │ │ │ ├── variables.data-00000-of-00002
│ │ │ │ │ │ ├── variables.index
│ │ │ │ │ │ └── variables.data-00001-of-00002
│ │ │ │ │ └── saved_model.pb
│ │ │ │ ├── inputs
│ │ │ │ │ ├── 0.png
│ │ │ │ │ ├── 1.png
│ │ │ │ │ ├── 2.png
│ │ │ │ │ ├── 3.png
│ │ │ │ │ ├── 4.png
│ │ │ │ │ ├── 5.png
│ │ │ │ │ ├── 6.png
│ │ │ │ │ ├── 7.png
│ │ │ │ │ ├── 8.png
│ │ │ │ │ ├── 9.png
│ │ │ │ │ └── 0.json
│ │ │ │ ├── outputs
│ │ │ │ │ ├── 0.png
│ │ │ │ │ ├── 0.json
│ │ │ │ │ ├── 0.html
│ │ │ │ │ ├── 0.input0.png.html
│ │ │ │ │ └── 0.multipart
│ │ │ │ └── api.conf
│ │ │ └── 2d_savedmodel
│ │ │ │ ├── 2d_savedmodel.zip
│ │ │ │ ├── api.conf
│ │ │ │ ├── 2d_input.json
│ │ │ │ └── 2d-tensorflow-model-manifest.json
│ │ ├── onnx
│ │ │ ├── pipeline_titanic.onnx
│ │ │ ├── api.conf
│ │ │ └── batch_gen.json
│ │ ├── huggingface
│ │ │ ├── api.conf
│ │ │ └── manifest.json
│ │ └── torch
│ │ │ ├── simple_model
│ │ │ ├── model.ts
│ │ │ ├── api.conf
│ │ │ └── manifest.json
│ │ │ └── multiple_input_output_model
│ │ │ ├── model.ts
│ │ │ ├── api.conf
│ │ │ └── manifest.json
│ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ ├── IsCloseTo.java
│ │ ├── torch
│ │ ├── TorchSimpleIT.java
│ │ └── TorchMultipleInputOutputIT.java
│ │ ├── tensorflow
│ │ └── Tensorflow2DIT.java
│ │ └── onnx
│ │ └── OnnxIT.java
│ └── main
│ ├── resources
│ ├── reference.conf
│ ├── log4j2.xml
│ └── swagger
│ │ └── swagger.html
│ └── java
│ └── com
│ └── ovh
│ └── mls
│ └── serving
│ └── runtime
│ ├── Main.java
│ ├── exceptions
│ ├── ErrorMessage.java
│ ├── JsonParseExceptionMapper.java
│ ├── JsonMappingExceptionMapper.java
│ ├── WebApplicationExceptionMapper.java
│ ├── EvaluationExceptionMapper.java
│ └── RestExceptionMapper.java
│ ├── core
│ ├── builder
│ │ ├── from
│ │ │ ├── TensorIOIntoJsonBinary.java
│ │ │ ├── TensorIOIntoImageBinary.java
│ │ │ ├── TensorIOIntoMultipartBinary.java
│ │ │ └── TensorIOIntoHTMLBinary.java
│ │ └── into
│ │ │ └── InputStreamIntoTensorIO.java
│ └── LogFilter.java
│ ├── swagger
│ └── SwaggerHomeResource.java
│ ├── utils
│ └── MultipartUtils.java
│ └── EvaluationService.java
├── evaluator-tensorflow
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── tensorflow
│ │ │ │ ├── test_h5
│ │ │ │ ├── single_gen.json
│ │ │ │ └── test.h5
│ │ │ │ ├── 2d_savedmodel
│ │ │ │ ├── 2d_savedmodel.zip
│ │ │ │ ├── batch.json
│ │ │ │ ├── batch_tensor.json
│ │ │ │ ├── 2d-tensorflow-model-manifest.json
│ │ │ │ └── 2d-tensorflow-model-manifest-same-output.json
│ │ │ │ └── 3d_savedmodel
│ │ │ │ ├── 3d_savedmodel.zip
│ │ │ │ ├── batch.json
│ │ │ │ ├── batch_tensor.json
│ │ │ │ └── 3d-tensorflow-model-manifest.json
│ │ └── java
│ │ │ └── com
│ │ │ └── ovh
│ │ │ └── mls
│ │ │ └── serving
│ │ │ └── runtime
│ │ │ └── tensorflow
│ │ │ └── H5Test.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ └── tensorflow
│ │ ├── TensorflowPbGenerator.java
│ │ ├── TensorflowEvaluatorManifest.java
│ │ └── TensorflowH5Generator.java
├── h5_converter
│ ├── requirements.txt
│ ├── hooks
│ │ ├── __pycache__
│ │ │ ├── hook-tensorflow_core.cpython-36.pyc
│ │ │ └── hook-tensorflow_core.cpython-37.pyc
│ │ └── hook-tensorflow_core.py
│ └── Makefile
└── pom.xml
├── commons
├── src
│ ├── test
│ │ ├── resources
│ │ │ ├── core
│ │ │ │ ├── tensor-input
│ │ │ │ │ ├── vector-multiple.json
│ │ │ │ │ ├── tensor-multiple.json
│ │ │ │ │ └── tensor-single.json
│ │ │ │ └── test-manifest.json
│ │ │ └── utils
│ │ │ │ └── img
│ │ │ │ ├── amber.jpg
│ │ │ │ └── amber_100px_100px.png
│ │ └── java
│ │ │ └── com
│ │ │ └── ovh
│ │ │ └── mls
│ │ │ └── serving
│ │ │ └── runtime
│ │ │ ├── core
│ │ │ ├── io
│ │ │ │ └── TensorIOTest.java
│ │ │ ├── EvaluatorUtilTest.java
│ │ │ ├── tensor
│ │ │ │ └── TensorShapeTest.java
│ │ │ └── builder
│ │ │ │ └── TensorIntoImagesTest.java
│ │ │ ├── validation
│ │ │ └── ValidatorTest.java
│ │ │ └── utils
│ │ │ └── img
│ │ │ └── ImagesTensorConversionTest.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ ├── utils
│ │ └── img
│ │ │ ├── ImgProperties.java
│ │ │ ├── ImgChanelProperties.java
│ │ │ └── BinaryContent.java
│ │ ├── validation
│ │ ├── NumberOnly.java
│ │ └── Validator.java
│ │ ├── core
│ │ ├── EvaluatorGenerator.java
│ │ ├── builder
│ │ │ ├── Builder.java
│ │ │ └── InputStreamJsonIntoTensorIO.java
│ │ ├── IncludeAsEvaluatorGenerator.java
│ │ ├── IncludeAsEvaluatorManifest.java
│ │ ├── EvaluatorManifest.java
│ │ ├── Interval.java
│ │ ├── AbstractEvaluatorManifest.java
│ │ ├── Evaluator.java
│ │ ├── FlowEvaluatorManifest.java
│ │ ├── io
│ │ │ └── Part.java
│ │ ├── tensor
│ │ │ ├── TensorIndex.java
│ │ │ └── TensorField.java
│ │ ├── Field.java
│ │ ├── EvaluationContext.java
│ │ └── transformer
│ │ │ └── ImageTransformerInfo.java
│ │ └── exceptions
│ │ ├── EvaluationException.java
│ │ ├── deserialization
│ │ ├── TableDeserializationException.java
│ │ ├── MissingFieldException.java
│ │ ├── DifferentColumnLengthException.java
│ │ └── UnexpectedValueForColumnException.java
│ │ ├── EvaluatorException.java
│ │ └── SwiftConfigurationException.java
└── pom.xml
├── .dockerignore
├── evaluator-huggingface
├── huggingface-tokenizer-jni
│ ├── Makefile
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── src
│ └── main
│ │ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ └── huggingface
│ │ └── tokenizer
│ │ ├── Encoding.java
│ │ ├── Offset.java
│ │ ├── HuggingFaceTokenizerEvaluatorManifest.java
│ │ └── Tokenizer.java
└── pom.xml
├── evaluator-processors
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── processors
│ │ │ │ ├── standard-scaler-manifest.json
│ │ │ │ └── standard-scaler-same-output-manifest.json
│ │ └── java
│ │ │ └── com
│ │ │ └── ovh
│ │ │ └── mls
│ │ │ └── serving
│ │ │ └── runtime
│ │ │ └── processors
│ │ │ ├── StandardScalerTest.java
│ │ │ └── StandardScalerWithSameOutputTest.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ └── processors
│ │ ├── MeanStd.java
│ │ └── StandardScalerManifest.java
└── pom.xml
├── .gitignore
├── evaluator-timeseries
├── src
│ ├── test
│ │ └── resources
│ │ │ └── timeseries
│ │ │ ├── datetime-timestamp-manifest.json
│ │ │ ├── prediction-interval-manifest.json
│ │ │ ├── prediction-interval-manifest-partial.json
│ │ │ └── datetime-string-manifest.json
│ └── main
│ │ └── java
│ │ └── com
│ │ └── ovh
│ │ └── mls
│ │ └── serving
│ │ └── runtime
│ │ └── timeseries
│ │ ├── DatetimeEvaluatorManifest.java
│ │ ├── PredictionIntervalEvaluatorManifest.java
│ │ └── DatetimeEvaluator.java
└── pom.xml
├── AUTHORS
├── MAINTAINERS
├── CONTRIBUTORS
├── .github
└── workflows
│ ├── test.yml
│ └── deploy-packages.yml
├── LICENSE
├── Makefile
├── dockerfiles
├── onnx.Dockerfile
├── full.Dockerfile
└── tensorflow.Dockerfile
└── CONTRIBUTING.md
/VERSION:
--------------------------------------------------------------------------------
1 | 1.0.2
--------------------------------------------------------------------------------
/evaluator-onnx/src/main/resources/reference.conf:
--------------------------------------------------------------------------------
1 | evaluator: {}
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | ## Next Release
3 | - Init evaluator interface
--------------------------------------------------------------------------------
/evaluator-torch/converter/requirements.txt:
--------------------------------------------------------------------------------
1 | torch>=1.5,<=1.6
2 | tqdm>=4,<=5
--------------------------------------------------------------------------------
/examples/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM serving-runtime-base:latest
2 |
3 | COPY . /deployments
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/model/variables/variables.data-00000-of-00002:
--------------------------------------------------------------------------------
1 | '
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/single_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "float_input": [6.5, 3, 5.5, 1.8]
3 | }
--------------------------------------------------------------------------------
/examples/2d_savedmodel.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/examples/2d_savedmodel.zip
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/test_h5/single_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "inputs": [6.4, 3.2, 4.5, 1.5]
3 | }
--------------------------------------------------------------------------------
/examples/pipeline_titanic.onnx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/examples/pipeline_titanic.onnx
--------------------------------------------------------------------------------
/commons/src/test/resources/core/tensor-input/vector-multiple.json:
--------------------------------------------------------------------------------
1 | {
2 | "key1": [1, 2, 3],
3 | "key2": [4, 5, 6]
4 | }
--------------------------------------------------------------------------------
/evaluator-torch/src/test/resources/model.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-torch/src/test/resources/model.ts
--------------------------------------------------------------------------------
/commons/src/test/resources/utils/img/amber.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/commons/src/test/resources/utils/img/amber.jpg
--------------------------------------------------------------------------------
/api/src/test/resources/onnx/pipeline_titanic.onnx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/onnx/pipeline_titanic.onnx
--------------------------------------------------------------------------------
/api/src/test/resources/huggingface/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8088
3 | port: 8089
4 | }
5 |
6 | files.path: "src/test/resources/huggingface/"
--------------------------------------------------------------------------------
/api/src/test/resources/torch/simple_model/model.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/torch/simple_model/model.ts
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/batch_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "float_input": [
3 | [4.9, 2.5, 4.5, 1.7],
4 | [7.4, 2.8, 6.1, 1.9]
5 | ]
6 | }
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/0.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/1.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/2.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/3.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/4.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/5.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/6.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/7.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/8.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/inputs/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/inputs/9.png
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/outputs/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/outputs/0.png
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/iris.onnx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-onnx/src/test/resources/onnx/iris/iris.onnx
--------------------------------------------------------------------------------
/evaluator-tensorflow/h5_converter/requirements.txt:
--------------------------------------------------------------------------------
1 | tensorflow==1.15.4
2 | pyinstaller>=3.5,<=3.6
3 | absl-py==0.8.1
4 | setuptools>=41.0.1,<45.0.0
5 | h5py==2.10.0
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/single.json:
--------------------------------------------------------------------------------
1 | {
2 | "sepal_length": 6.5,
3 | "sepal_width": 3,
4 | "petal_length": 5.5,
5 | "petal_width": 1.8
6 | }
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/titanic/single.json:
--------------------------------------------------------------------------------
1 | {
2 | "pclass": "3",
3 | "sex": "male",
4 | "age": 30,
5 | "fare": 7.8958,
6 | "embarked": "S"
7 | }
--------------------------------------------------------------------------------
/api/src/test/resources/huggingface/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "huggingface_tokenizer",
3 | "saved_model_uri": "src/test/resources/huggingface/tokenizer.json"
4 | }
5 |
--------------------------------------------------------------------------------
/api/src/test/resources/torch/simple_model/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8092
3 | port: 8093
4 | }
5 |
6 | files.path: "src/test/resources/torch/simple_model"
7 |
--------------------------------------------------------------------------------
/commons/src/test/resources/utils/img/amber_100px_100px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/commons/src/test/resources/utils/img/amber_100px_100px.png
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/titanic/single_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "pclass": "3",
3 | "sex": "male",
4 | "age": 30,
5 | "fare": 7.8958,
6 | "embarked": "S"
7 | }
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/model/saved_model.pb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/model/saved_model.pb
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/adult/transformation.onnx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-onnx/src/test/resources/onnx/adult/transformation.onnx
--------------------------------------------------------------------------------
/api/src/test/resources/onnx/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8082
3 | port: 8083
4 | }
5 |
6 | files.path: "src/test/resources/onnx/"
7 | evaluator.tensorflow.h5_converter.path: ""
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/2d_savedmodel/2d_savedmodel.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/2d_savedmodel/2d_savedmodel.zip
--------------------------------------------------------------------------------
/api/src/test/resources/torch/multiple_input_output_model/model.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/torch/multiple_input_output_model/model.ts
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/test_h5/test.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-tensorflow/src/test/resources/tensorflow/test_h5/test.h5
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | */target/**
2 | **/*.iml
3 | **/tmp
4 | .git
5 | examples/
6 | dockerfiles/
7 | **.md
8 | .idea
9 | MAINTAINERS
10 | AUTHORS
11 | CONTRIBUTORS
12 | LICENSE
13 | VERSION
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/titanic/pipeline_titanic.onnx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-onnx/src/test/resources/onnx/titanic/pipeline_titanic.onnx
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/model/variables/variables.index:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/model/variables/variables.index
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/batch.json:
--------------------------------------------------------------------------------
1 | {
2 | "sepal_length": [4.9, 7.4],
3 | "sepal_width": [2.5, 2.8],
4 | "petal_length": [4.5, 6.1],
5 | "petal_width": [1.7, 1.9]
6 | }
--------------------------------------------------------------------------------
/api/src/test/resources/torch/multiple_input_output_model/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8092
3 | port: 8093
4 | }
5 |
6 | files.path: "src/test/resources/torch/multiple_input_output_model"
7 |
--------------------------------------------------------------------------------
/evaluator-huggingface/huggingface-tokenizer-jni/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := build
2 |
3 | WORKDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
4 |
5 | .PHONY: build
6 | build:
7 | cargo build --release
8 |
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/titanic/batch.json:
--------------------------------------------------------------------------------
1 | {
2 | "pclass": ["3", "3"],
3 | "sex": ["male", "female"],
4 | "age": [30, 48],
5 | "fare": [7.8958, 34.375],
6 | "embarked": ["S", "S"]
7 | }
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8086
3 | port: 8087
4 | }
5 |
6 | files.path: "src/test/resources/tensorflow/mnist/model/"
7 | evaluator.tensorflow.h5_converter.path: ""
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/2d_savedmodel/2d_savedmodel.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-tensorflow/src/test/resources/tensorflow/2d_savedmodel/2d_savedmodel.zip
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/3d_savedmodel/3d_savedmodel.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-tensorflow/src/test/resources/tensorflow/3d_savedmodel/3d_savedmodel.zip
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/2d_savedmodel/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8084
3 | port: 8085
4 | }
5 |
6 | files.path: "src/test/resources/tensorflow/2d_savedmodel/"
7 | evaluator.tensorflow.h5_converter.path: ""
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/model/variables/variables.data-00001-of-00002:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/api/src/test/resources/tensorflow/mnist/model/variables/variables.data-00001-of-00002
--------------------------------------------------------------------------------
/evaluator-tensorflow/h5_converter/hooks/__pycache__/hook-tensorflow_core.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-tensorflow/h5_converter/hooks/__pycache__/hook-tensorflow_core.cpython-36.pyc
--------------------------------------------------------------------------------
/evaluator-tensorflow/h5_converter/hooks/__pycache__/hook-tensorflow_core.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ovh/serving-runtime/HEAD/evaluator-tensorflow/h5_converter/hooks/__pycache__/hook-tensorflow_core.cpython-37.pyc
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/utils/img/ImgProperties.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.utils.img;
2 |
3 | public enum ImgProperties {
4 | BATCH_SIZE,
5 | WIDTH,
6 | HEIGHT,
7 | CHANEL
8 | }
9 |
--------------------------------------------------------------------------------
/commons/src/test/resources/core/tensor-input/tensor-multiple.json:
--------------------------------------------------------------------------------
1 | {
2 | "tensor_input_1": [
3 | [1, 2, 3],
4 | [4, 5, 6]
5 | ],
6 | "tensor_input_2": [
7 | [7, 8],
8 | [9, 10],
9 | [11, 12]
10 | ]
11 | }
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/utils/img/ImgChanelProperties.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.utils.img;
2 |
3 | public enum ImgChanelProperties {
4 | RED,
5 | BLUE,
6 | GREEN,
7 | GRAY_SCALE
8 | }
9 |
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/outputs/0.json:
--------------------------------------------------------------------------------
1 | {"class_ids":0,"logits":[3435.8083,-64.110596,107.91369,-479.41333,-688.7862,-592.7844,-576.5078,-386.3663,-845.04346,-607.1198],"classes":"0","probabilities":[1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]}
--------------------------------------------------------------------------------
/commons/src/test/resources/core/test-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "test",
3 | "test_value": "someTestString",
4 | "inputs": [
5 | {
6 | "name": "int",
7 | "type": "integer"
8 | }
9 | ],
10 | "outputs": [
11 | ]
12 | }
--------------------------------------------------------------------------------
/commons/src/test/resources/core/tensor-input/tensor-single.json:
--------------------------------------------------------------------------------
1 | {
2 | "tensor_input_1": [
3 | [
4 | [1, 2],
5 | [3, 4]
6 | ],
7 | [
8 | [5, 6],
9 | [7, 8]
10 | ],
11 | [
12 | [9, 10],
13 | [11, 12]
14 | ]
15 | ]
16 | }
--------------------------------------------------------------------------------
/evaluator-tensorflow/h5_converter/hooks/hook-tensorflow_core.py:
--------------------------------------------------------------------------------
1 | from PyInstaller.utils.hooks import collect_submodules, collect_data_files
2 |
3 | hiddenimports = collect_submodules('tensorflow_core')
4 | datas = collect_data_files('tensorflow_core', subdir=None, include_py_files=True)
5 |
--------------------------------------------------------------------------------
/api/src/main/resources/reference.conf:
--------------------------------------------------------------------------------
1 | server {
2 | metrics.port: 8081
3 | bind: "0.0.0.0"
4 | port: 8080
5 | }
6 |
7 | swagger {
8 | title: "Model Name",
9 | description: "Inference Model"
10 | version: "1"
11 | path: "/"
12 | }
13 |
14 | files.path: "examples/"
15 |
16 | evaluator: {}
--------------------------------------------------------------------------------
/api/src/test/resources/onnx/batch_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "pclass": [
3 | "3",
4 | "3"
5 | ],
6 | "sex": [
7 | "male",
8 | "female"
9 | ],
10 | "age": [
11 | 30,
12 | 48
13 | ],
14 | "fare": [
15 | 7.8958,
16 | 34.375
17 | ],
18 | "embarked": [
19 | "S",
20 | "S"
21 | ]
22 | }
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/titanic/batch_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "pclass": [
3 | "3",
4 | "3"
5 | ],
6 | "sex": [
7 | "male",
8 | "female"
9 | ],
10 | "age": [
11 | 30,
12 | 48
13 | ],
14 | "fare": [
15 | 7.8958,
16 | 34.375
17 | ],
18 | "embarked": [
19 | "S",
20 | "S"
21 | ]
22 | }
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/2d_savedmodel/2d_input.json:
--------------------------------------------------------------------------------
1 | {
2 | "scaled_imputed_t": [2.0196744706314087, 2.045239970259654],
3 | "scaled_imputed_label": [-0.17866892904935183, 1.8181895427895707],
4 | "scaled_imputed_feature1": [-0.12179146375316918, 0.083989161980639],
5 | "scaled_imputed_feature2": [-0.40634564254730754, -0.20722287490242464]
6 | }
--------------------------------------------------------------------------------
/evaluator-tensorflow/h5_converter/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := build
2 |
3 | WORKDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
4 |
5 | .PHONY: build
6 | build:
7 | pip install -r requirements.txt
8 | bash -c "pyinstaller --clean --additional-hooks-dir=./hooks -F h5_converter.py --log-level=INFO --add-data $$(python -c 'import os;import astor;print(os.path.dirname(astor.__file__))')/VERSION:astor/"
9 |
--------------------------------------------------------------------------------
/evaluator-processors/src/test/resources/processors/standard-scaler-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "mean_std_map": {
3 | "value": {
4 | "mean": 7.0,
5 | "std": 2.0
6 | }
7 | },
8 | "inputs": [
9 | {
10 | "name": "value",
11 | "type": "double"
12 | }
13 | ],
14 | "outputs": [
15 | {
16 | "name": "scaled_value",
17 | "type": "double"
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/*
2 | *.iml
3 | target/**
4 | **/target/**
5 |
6 | **/.factorypath
7 | **/.classpath
8 | **/.project
9 | **/.settings
10 | **/.DS_Store
11 |
12 | evaluator-tensorflow/h5_converter/__pycache__/*
13 | evaluator-tensorflow/h5_converter/dist/*
14 | evaluator-tensorflow/h5_converter/build/*
15 | evaluator-tensorflow/tmp/*
16 | evaluator-tensorflow/h5_converter/h5_converter.spec
17 |
18 | evaluator-torch/libtorch-*
--------------------------------------------------------------------------------
/evaluator-torch/src/test/resources/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "torch_script",
3 | "saved_model_uri": "model.ts",
4 | "inputs": [
5 | {
6 | "name": "input_0",
7 | "shape": [
8 | 2
9 | ],
10 | "type": "float"
11 | }
12 | ],
13 | "outputs": [
14 | {
15 | "name": "output_0",
16 | "shape": [
17 | 2
18 | ],
19 | "type": "float"
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/api/src/test/resources/torch/simple_model/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "torch_script",
3 | "saved_model_uri": "model.ts",
4 | "inputs": [
5 | {
6 | "name": "input_0",
7 | "shape": [
8 | 2
9 | ],
10 | "type": "float"
11 | }
12 | ],
13 | "outputs": [
14 | {
15 | "name": "output_0",
16 | "shape": [
17 | 2
18 | ],
19 | "type": "float"
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/2d_savedmodel/batch.json:
--------------------------------------------------------------------------------
1 | {
2 | "scaled_imputed_t": [2.0196744706314087, 2.045239970259654, 2.045239970259654],
3 | "scaled_imputed_label": [-0.17866892904935183, 1.8181895427895707, 2.045239970259654],
4 | "scaled_imputed_feature1": [-0.12179146375316918, 0.083989161980639, 2.045239970259654],
5 | "scaled_imputed_feature2": [-0.40634564254730754, -0.20722287490242464, 2.045239970259654]
6 | }
7 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/2d_savedmodel/batch_tensor.json:
--------------------------------------------------------------------------------
1 | {
2 | "input": [
3 | [2.0196744706314087, -0.17866892904935183, -0.12179146375316918, -0.40634564254730754, 2.045239970259654, 1.8181895427895707, 0.083989161980639, -0.20722287490242464],
4 | [2.045239970259654, 1.8181895427895707, 0.083989161980639, -0.20722287490242464, 2.045239970259654, 2.045239970259654, 2.045239970259654, 2.045239970259654]
5 | ]
6 | }
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/3d_savedmodel/batch.json:
--------------------------------------------------------------------------------
1 | {
2 | "scaled_imputed_t": [2.0196744706314087, 2.045239970259654, 2.045239970259654],
3 | "scaled_imputed_label": [-0.17866892904935183, 1.8181895427895707, 2.045239970259654],
4 | "scaled_imputed_feature1": [-0.12179146375316918, 0.083989161980639, 2.045239970259654],
5 | "scaled_imputed_feature2": [-0.40634564254730754, -0.20722287490242464, 2.045239970259654]
6 | }
7 |
--------------------------------------------------------------------------------
/evaluator-timeseries/src/test/resources/timeseries/datetime-timestamp-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "inputs": [
3 | {
4 | "name": "timestamp-seconds",
5 | "type": "long",
6 | "shape": [-1, -1],
7 | "format": "TS_S"
8 | }
9 | ],
10 | "outputs": [
11 | {
12 | "name": "date",
13 | "type": "string",
14 | "shape": [-1, -1],
15 | "format": "yyyy-MM-dd HH:mm:ss"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/outputs/0.html:
--------------------------------------------------------------------------------
1 |
class_ids
0
logits
- 3435.8083
- -64.110596
- 107.91369
- -479.41333
- -688.7862
- -592.7844
- -576.5078
- -386.3663
- -845.04346
- -607.1198
classes
0
probabilities
- 1.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
--------------------------------------------------------------------------------
/evaluator-timeseries/src/test/resources/timeseries/prediction-interval-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "residuals_std": {
3 | "label": 0.12
4 | },
5 | "inputs": [
6 | {
7 | "name": "label",
8 | "type": "double"
9 | }
10 | ],
11 | "outputs": [
12 | {
13 | "name": "label_quantile_inf",
14 | "type": "double"
15 | },
16 | {
17 | "name": "label_quantile_sup",
18 | "type": "double"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/Main.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime;
2 |
3 | import com.ovh.mls.serving.runtime.core.ApiServer;
4 | import com.typesafe.config.Config;
5 | import com.typesafe.config.ConfigFactory;
6 |
7 | public class Main {
8 | public static void main(String[] args) {
9 | Config config = ConfigFactory.load();
10 |
11 | new ApiServer(config)
12 | .start()
13 | .join();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/validation/NumberOnly.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.validation;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | /**
7 | * Annotation used for validation purposes. Any Evaluator annotated with this annotation can only handle Number inputs.
8 | *
9 | * @see Validator
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | public @interface NumberOnly {
13 | }
14 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/EvaluatorGenerator.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
4 | import com.typesafe.config.Config;
5 |
6 | import java.io.File;
7 | import java.io.FileNotFoundException;
8 |
9 | public interface EvaluatorGenerator {
10 |
11 | Evaluator generate(File filename, Config evaluatorConfig) throws EvaluatorException, FileNotFoundException;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/builder/Builder.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core.builder;
2 |
3 | import com.ovh.mls.serving.runtime.exceptions.EvaluationException;
4 |
5 | /**
6 | * Builder is a generic interface for converting an Object into another
7 | * @param The input object class
8 | * @param The output object class
9 | */
10 | public interface Builder {
11 |
12 | O build(I input) throws EvaluationException;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/EvaluationException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 | public class EvaluationException extends RuntimeException {
4 | public EvaluationException(String message) {
5 | super(message);
6 | }
7 |
8 | public EvaluationException(Exception e) {
9 | super(e);
10 | }
11 |
12 | public EvaluationException(String message, Exception e) {
13 | super(message, e);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/deserialization/TableDeserializationException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions.deserialization;
2 |
3 | public class TableDeserializationException extends RuntimeException {
4 |
5 | public TableDeserializationException(String message) {
6 | super(message);
7 | }
8 |
9 | public TableDeserializationException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the official list of OVH Serving Runtime authors for copyright purposes.
2 | # This file is distinct from the CONTRIBUTORS files
3 | # and it lists the copyright holders only.
4 |
5 | # Names should be added to this file as one of
6 | # Organization's name
7 | # Individual's name
8 | # Individual's name
9 | # See CONTRIBUTORS for the meaning of multiple email addresses.
10 |
11 | # Please keep the list sorted.
12 |
13 | OVH SAS
--------------------------------------------------------------------------------
/evaluator-huggingface/huggingface-tokenizer-jni/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "huggingface_tokenizer_jni"
3 | version = "0.1.0"
4 | authors = ["Corentin Regal "]
5 | edition = "2018"
6 |
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8 |
9 | [lib]
10 | name = "huggingface_tokenizer_jni"
11 | crate-type = ["cdylib"]
12 |
13 | [dependencies]
14 | jni = "0.16"
15 | tokenizers = { git = "https://github.com/huggingface/tokenizers" }
16 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/3d_savedmodel/batch_tensor.json:
--------------------------------------------------------------------------------
1 | {
2 | "input": [
3 | [
4 | [2.0196744706314087, -0.17866892904935183, -0.12179146375316918, -0.40634564254730754],
5 | [2.045239970259654, 1.8181895427895707, 0.083989161980639, -0.20722287490242464]
6 | ],
7 | [
8 | [2.045239970259654, 1.8181895427895707, 0.083989161980639, -0.20722287490242464],
9 | [2.045239970259654, 2.045239970259654, 2.045239970259654, 2.045239970259654]
10 | ]
11 | ]
12 | }
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/IncludeAsEvaluatorGenerator.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | /**
7 | * Class annotated with EvaluatorGenerator need to add this annotation and declare a unique extension name
8 | *
9 | * @see EvaluatorGenerator
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | public @interface IncludeAsEvaluatorGenerator {
13 | String extension();
14 | }
15 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/EvaluatorException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 | public class EvaluatorException extends RuntimeException {
4 | public EvaluatorException(String message) {
5 | super(message);
6 | }
7 |
8 | public EvaluatorException(Throwable throwable) {
9 | super(throwable);
10 | }
11 |
12 | public EvaluatorException(String message, Throwable throwable) {
13 | super(message, throwable);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/deserialization/MissingFieldException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions.deserialization;
2 |
3 | import java.util.Set;
4 |
5 | public class MissingFieldException extends TableDeserializationException {
6 |
7 | public MissingFieldException(Set missingFields) {
8 | super(String.format(
9 | "The following fields were expected but not found : %s",
10 | missingFields.toString()
11 | ));
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/adult/single_gen.json:
--------------------------------------------------------------------------------
1 | {
2 | "age": [[39]],
3 | "workclass": [["State-gov"]],
4 | "fnlwgt": [[77516]],
5 | "education": [["qsdfgqsfdqs"]],
6 | "education-num": [[13]],
7 | "marital-status": [["Married-civ-spouse"]],
8 | "occupation": [["Adm-clerical"]],
9 | "relationship": [["Not-in-family"]],
10 | "race": [["White"]],
11 | "sex" :[["Male"]],
12 | "capital-gain": [[2174]],
13 | "capital-loss": [[0]],
14 | "hours-per-week": [[40]],
15 | "native-country": [["United-States"]]
16 | }
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/outputs/0.input0.png.html:
--------------------------------------------------------------------------------
1 | image
--------------------------------------------------------------------------------
/api/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MAINTAINERS:
--------------------------------------------------------------------------------
1 | # This is the official list of the project maintainers.
2 | # This is mostly useful for contributors that want to push
3 | # significant pull requests or for project management issues.
4 | #
5 | #
6 | # Names should be added to this file like so:
7 | # Individual's name
8 | # Individual's name
9 | #
10 | # Please keep the list sorted.
11 | #
12 |
13 | Adrien Carreira
14 | Christophe Rannou
15 | Corentin Regal
16 | Maël Le Gal
--------------------------------------------------------------------------------
/evaluator-processors/src/main/java/com/ovh/mls/serving/runtime/processors/MeanStd.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.processors;
2 |
3 | public class MeanStd {
4 |
5 | private Double mean;
6 |
7 | private Double std;
8 |
9 | public Double getMean() {
10 | return mean;
11 | }
12 |
13 | public MeanStd setMean(Double mean) {
14 | this.mean = mean;
15 | return this;
16 | }
17 |
18 | public Double getStd() {
19 | return std;
20 | }
21 |
22 | public MeanStd setStd(Double std) {
23 | this.std = std;
24 | return this;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/deserialization/DifferentColumnLengthException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions.deserialization;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | public class DifferentColumnLengthException extends TableDeserializationException {
7 |
8 | public DifferentColumnLengthException(Map> errorMap) {
9 | super(
10 | String.format(
11 | "Not all the column have the same size : %s",
12 | errorMap.toString()
13 | )
14 | );
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/evaluator-processors/src/test/resources/processors/standard-scaler-same-output-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "mean_std_map": {
3 | "value": {
4 | "mean": 5.0,
5 | "std": 2.0
6 | },
7 | "label": {
8 | "mean": 10.0,
9 | "std": 4.0
10 | }
11 | },
12 | "inputs": [
13 | {
14 | "name": "value",
15 | "type": "double"
16 | },
17 | {
18 | "name": "label",
19 | "type": "double"
20 | }
21 | ],
22 | "outputs": [
23 | {
24 | "name": "value",
25 | "type": "double"
26 | },
27 | {
28 | "name": "label",
29 | "type": "double"
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/evaluator-timeseries/src/test/resources/timeseries/prediction-interval-manifest-partial.json:
--------------------------------------------------------------------------------
1 | {
2 | "residuals_std": {
3 | "label": 0.12,
4 | "label_t+1": 0.3
5 | },
6 | "inputs": [
7 | {
8 | "name": "label",
9 | "type": "double"
10 | },
11 | {
12 | "name": "label_t+1",
13 | "type": "double"
14 | }
15 | ],
16 | "outputs": [
17 | {
18 | "name": "label_quantile_inf",
19 | "type": "double"
20 | },
21 | {
22 | "name": "label_quantile_sup",
23 | "type": "double"
24 | },
25 | {
26 | "name": "label_t+1_quantile_sup",
27 | "type": "double"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/SwiftConfigurationException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 | public class SwiftConfigurationException extends Exception {
4 |
5 | public SwiftConfigurationException(String message) {
6 | super(message);
7 | }
8 |
9 | public SwiftConfigurationException(Exception e) {
10 | super(e);
11 | }
12 |
13 | public static SwiftConfigurationException keyNotFoundException(String key) {
14 | String message = String.format("Swift key '%s' was not found in configurations collection", key);
15 | return new SwiftConfigurationException(message);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | # This is the official list of people who can contribute
2 | # (and typically have contributed) code to the OVH Serving Runtime repository.
3 | #
4 | # Names should be added to this file only after verifying that
5 | # the individual or the individual's organization has agreed to
6 | # the appropriate CONTRIBUTING.md file.
7 | #
8 | # Names should be added to this file like so:
9 | # Individual's name
10 | # Individual's name qqqqhtop
11 |
12 | #
13 | # Please keep the list sorted.
14 | #
15 |
16 | Adrien Carreira
17 | Christophe Rannou
18 | Maël Le Gal
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/IncludeAsEvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | /**
7 | * Class implementing EvaluatorManifest need to add this annotation and declare a unique type property
8 | * use by the ObjectMapper to deserialize the JSON manifest. All classes with this annotation will be registered
9 | * at runtime as EvaluatorManifest subtypes.
10 | *
11 | * @see EvaluatorUtil
12 | * @see EvaluatorManifest
13 | */
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface IncludeAsEvaluatorManifest {
16 | String type();
17 | }
18 |
--------------------------------------------------------------------------------
/api/src/test/resources/torch/multiple_input_output_model/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "torch_script",
3 | "saved_model_uri": "model.ts",
4 | "inputs": [
5 | {
6 | "name": "input_0",
7 | "shape": [
8 | 4
9 | ],
10 | "type": "float"
11 | },
12 | {
13 | "name": "input_1",
14 | "shape": [
15 | 2
16 | ],
17 | "type": "float"
18 | }
19 | ],
20 | "outputs": [
21 | {
22 | "name": "output_0",
23 | "shape": [
24 | 1
25 | ],
26 | "type": "float"
27 | },
28 | {
29 | "name": "output_1",
30 | "shape": [
31 | 1
32 | ],
33 | "type": "float"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/EvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy;
5 | import com.fasterxml.jackson.databind.annotation.JsonNaming;
6 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * @see Evaluator
12 | */
13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
14 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
15 | public interface EvaluatorManifest {
16 |
17 | Evaluator create(String path) throws EvaluatorException, IOException;
18 |
19 | String getType();
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/Interval.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | public class Interval {
7 |
8 | private final Double lowerBound;
9 | private final Double upperBound;
10 |
11 | @JsonCreator
12 | public Interval(@JsonProperty("lower_bound") Double lowerBound, @JsonProperty("upper_bound") Double upperBound) {
13 | this.lowerBound = lowerBound;
14 | this.upperBound = upperBound;
15 | }
16 |
17 | public Double getLowerBound() {
18 | return lowerBound;
19 | }
20 |
21 | public Double getUpperBound() {
22 | return upperBound;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/exceptions/ErrorMessage.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 | import org.eclipse.jetty.http.HttpStatus;
4 |
5 | /**
6 | * Error Message Model
7 | */
8 | public class ErrorMessage {
9 | private final int status;
10 | private final String message;
11 |
12 | public ErrorMessage(String message) {
13 | this.message = message;
14 | this.status = HttpStatus.INTERNAL_SERVER_ERROR_500;
15 | }
16 |
17 | public ErrorMessage(String message, int status) {
18 | this.message = message;
19 | this.status = status;
20 | }
21 |
22 | public int getStatus() {
23 | return status;
24 | }
25 |
26 | public String getMessage() {
27 | return message;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/exceptions/deserialization/UnexpectedValueForColumnException.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions.deserialization;
2 |
3 | import com.ovh.mls.serving.runtime.core.Field;
4 |
5 | public class UnexpectedValueForColumnException extends TableDeserializationException {
6 |
7 | public UnexpectedValueForColumnException(Field expectedField, Object object) {
8 | super(
9 | String.format(
10 | "Expected column '%s' to be of type '%s', found '%s' with value '%s'",
11 | expectedField.getName(),
12 | expectedField.getType().toString(),
13 | object.getClass().getSimpleName(),
14 | object.toString()
15 | )
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/utils/img/BinaryContent.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.utils.img;
2 |
3 | import org.apache.http.entity.ContentType;
4 |
5 | public class BinaryContent {
6 | private String fileExtension;
7 | private ContentType contentType;
8 | private byte[] bytes;
9 |
10 | public BinaryContent(String extension, ContentType contentType, byte[] bytes) {
11 | this.fileExtension = extension;
12 | this.contentType = contentType;
13 | this.bytes = bytes;
14 | }
15 |
16 | public String getFileExtension() {
17 | return fileExtension;
18 | }
19 |
20 | public ContentType getContentType() {
21 | return contentType;
22 | }
23 |
24 | public byte[] getBytes() {
25 | return bytes;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/mnist/outputs/0.multipart:
--------------------------------------------------------------------------------
1 | --jetty1735298322k66b8jwv
2 | Content-Type: application/json
3 | Content-Disposition: form-data; name="class_ids"; filename="class_ids.json"
4 |
5 | 0
6 | --jetty1735298322k66b8jwv
7 | Content-Type: application/json
8 | Content-Disposition: form-data; name="logits"; filename="logits.json"
9 |
10 | [3435.8083,-64.110596,107.91369,-479.41333,-688.7862,-592.7844,-576.5078,-386.3663,-845.04346,-607.1198]
11 | --jetty1735298322k66b8jwv
12 | Content-Type: application/json
13 | Content-Disposition: form-data; name="classes"; filename="classes.json"
14 |
15 | "0"
16 | --jetty1735298322k66b8jwv
17 | Content-Type: application/json
18 | Content-Disposition: form-data; name="probabilities"; filename="probabilities.json"
19 |
20 | [1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]
21 | --jetty1735298322k66b8jwv--
22 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/AbstractEvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public abstract class AbstractEvaluatorManifest implements EvaluatorManifest {
7 |
8 | private List inputs = new ArrayList<>();
9 | private List outputs = new ArrayList<>();
10 |
11 | public List getInputs() {
12 | return inputs;
13 | }
14 |
15 | public AbstractEvaluatorManifest setInputs(List inputs) {
16 | this.inputs = inputs;
17 | return this;
18 | }
19 |
20 | public List getOutputs() {
21 | return outputs;
22 | }
23 |
24 | public AbstractEvaluatorManifest setOutputs(List outputs) {
25 | this.outputs = outputs;
26 | return this;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/evaluator-timeseries/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | serving-runtime
7 | com.ovh.mls.serving.runtime
8 | 1.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | evaluator-timeseries
13 |
14 |
15 |
16 | com.ovh.mls.serving.runtime
17 | commons
18 | 1.0.1-SNAPSHOT
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/evaluator-processors/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | serving-runtime
7 | com.ovh.mls.serving.runtime
8 | 1.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | evaluator-processors
13 |
14 |
15 |
16 | com.ovh.mls.serving.runtime
17 | commons
18 | 1.0.1-SNAPSHOT
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: TUs & TIs
2 | on:
3 | push:
4 | branches: [ '**', '*/*' ]
5 | jobs:
6 | test-build-jar:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout project
10 | uses: actions/checkout@v2
11 | - name: Set up JDK 1.11
12 | uses: actions/setup-java@v1
13 | with:
14 | java-version: 1.11
15 | - name: Set up Python 3.7
16 | uses: actions/setup-python@v1
17 | with:
18 | python-version: 3.7
19 | - name: Cache maven modules
20 | uses: actions/cache@v1
21 | env:
22 | cache-name: serving-runtime-maven-deps
23 | with:
24 | path: ~/.m2/repository
25 | key: cache-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }}
26 |
27 | - name: TUs & TIs
28 | run: |
29 | make initialize-tensorflow initialize-huggingface initialize-torch
30 | make test
31 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/exceptions/JsonParseExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 |
4 | import com.fasterxml.jackson.core.JsonParseException;
5 | import com.google.inject.Singleton;
6 |
7 | import javax.ws.rs.core.MediaType;
8 | import javax.ws.rs.core.Response;
9 | import javax.ws.rs.ext.ExceptionMapper;
10 | import javax.ws.rs.ext.Provider;
11 |
12 | import static org.eclipse.jetty.http.HttpStatus.BAD_REQUEST_400;
13 |
14 | @Provider
15 | @Singleton
16 | public class JsonParseExceptionMapper implements ExceptionMapper {
17 |
18 | @Override
19 | public Response toResponse(JsonParseException exception) {
20 |
21 | return Response
22 | .status(BAD_REQUEST_400)
23 | .entity(new ErrorMessage("Bad json provided", BAD_REQUEST_400))
24 | .type(MediaType.APPLICATION_JSON)
25 | .build();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/exceptions/JsonMappingExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 |
4 | import com.fasterxml.jackson.databind.JsonMappingException;
5 | import com.google.inject.Singleton;
6 |
7 | import javax.ws.rs.core.MediaType;
8 | import javax.ws.rs.core.Response;
9 | import javax.ws.rs.ext.ExceptionMapper;
10 | import javax.ws.rs.ext.Provider;
11 |
12 | import static org.eclipse.jetty.http.HttpStatus.BAD_REQUEST_400;
13 |
14 | @Provider
15 | @Singleton
16 | public class JsonMappingExceptionMapper implements ExceptionMapper {
17 |
18 | @Override
19 | public Response toResponse(JsonMappingException exception) {
20 |
21 | return Response
22 | .status(BAD_REQUEST_400)
23 | .entity(new ErrorMessage("Bad json provided", BAD_REQUEST_400))
24 | .type(MediaType.APPLICATION_JSON)
25 | .build();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/2d_savedmodel/2d-tensorflow-model-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "saved_model_uri": "src/test/resources/tensorflow/2d_savedmodel/2d_savedmodel.zip",
3 | "batch_size": 2,
4 | "inputs": [
5 | {
6 | "name": "input",
7 | "shape": [-1, 8],
8 | "type": "float",
9 | "fields": [
10 | {
11 | "name": "scaled_imputed_t",
12 | "index": 0
13 | },
14 | {
15 | "name": "scaled_imputed_label",
16 | "index": 1
17 | },
18 | {
19 | "name": "scaled_imputed_feature1",
20 | "index": 2
21 | },
22 | {
23 | "name": "scaled_imputed_feature2",
24 | "index": 3
25 | }
26 | ]
27 | }
28 | ],
29 | "outputs": [
30 | {
31 | "name": "output",
32 | "shape": [-1, 1],
33 | "type": "float",
34 | "fields": [
35 | {
36 | "name": "scaled_imputed_label_predicted",
37 | "index": 0
38 | }
39 | ]
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/examples/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "tensorflow",
3 | "saved_model_uri": "2d_savedmodel.zip",
4 | "batch_size": 2,
5 | "inputs": {
6 | "dense_1_input:0": {
7 | "shape": "(?, 8)",
8 | "fields": [
9 | {
10 | "name": "scaled_imputed_t",
11 | "type": "float",
12 | "index": 0
13 | },
14 | {
15 | "name": "scaled_imputed_label",
16 | "type": "float",
17 | "index": 1
18 | },
19 | {
20 | "name": "scaled_imputed_feature1",
21 | "type": "float",
22 | "index": 2
23 | },
24 | {
25 | "name": "scaled_imputed_feature2",
26 | "type": "float",
27 | "index": 3
28 | }
29 | ]
30 | }
31 | },
32 | "outputs": {
33 | "dense_2/BiasAdd:0": {
34 | "shape": "(?, 1)",
35 | "fields": [
36 | {
37 | "name": "scaled_imputed_label_predicted",
38 | "type": "double",
39 | "index": 0
40 | }
41 | ]
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/2d_savedmodel/2d-tensorflow-model-manifest-same-output.json:
--------------------------------------------------------------------------------
1 | {
2 | "saved_model_uri": "src/test/resources/tensorflow/2d_savedmodel/2d_savedmodel.zip",
3 | "batch_size": 2,
4 | "inputs": [
5 | {
6 | "name": "input",
7 | "shape": [-1, 8],
8 | "type": "float",
9 | "fields": [
10 | {
11 | "name": "scaled_imputed_t",
12 | "index": 0
13 | },
14 | {
15 | "name": "scaled_imputed_label",
16 | "index": 1
17 | },
18 | {
19 | "name": "scaled_imputed_feature1",
20 | "index": 2
21 | },
22 | {
23 | "name": "scaled_imputed_feature2",
24 | "index": 3
25 | }
26 | ]
27 | }
28 | ],
29 | "outputs": [
30 | {
31 | "name": "output",
32 | "shape": [-1, 1],
33 | "type": "float",
34 | "fields": [
35 | {
36 | "name": "scaled_imputed_label",
37 | "index": 0
38 | }
39 | ]
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/test/resources/tensorflow/3d_savedmodel/3d-tensorflow-model-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "saved_model_uri": "src/test/resources/tensorflow/3d_savedmodel/3d_savedmodel.zip",
3 | "batch_size": 2,
4 | "inputs": [
5 | {
6 | "name": "input",
7 | "shape": [-1, 2, 4],
8 | "type": "float",
9 | "fields": [
10 | {
11 | "name": "scaled_imputed_t",
12 | "index": 0
13 | },
14 | {
15 | "name": "scaled_imputed_label",
16 | "index": 1
17 | },
18 | {
19 | "name": "scaled_imputed_feature1",
20 | "index": 2
21 | },
22 | {
23 | "name": "scaled_imputed_feature2",
24 | "index": 3
25 | }
26 | ]
27 | }
28 | ],
29 | "outputs": [
30 | {
31 | "name": "output",
32 | "shape": [-1, 1],
33 | "type": "float",
34 | "fields": [
35 | {
36 | "name": "scaled_imputed_label_predicted",
37 | "index": 0
38 | }
39 | ]
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/evaluator-timeseries/src/main/java/com/ovh/mls/serving/runtime/timeseries/DatetimeEvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.timeseries;
2 |
3 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
4 | import com.ovh.mls.serving.runtime.core.AbstractEvaluatorManifest;
5 | import com.ovh.mls.serving.runtime.core.IncludeAsEvaluatorManifest;
6 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
7 |
8 | // In case of direct deserialization we override parent JsonTypeInfo
9 | @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
10 | @IncludeAsEvaluatorManifest(type = DatetimeEvaluatorManifest.TYPE)
11 | public class DatetimeEvaluatorManifest extends AbstractEvaluatorManifest {
12 |
13 | public static final String TYPE = "datetime_encoder";
14 |
15 | @Override
16 | public DatetimeEvaluator create(String path) throws EvaluatorException {
17 | return new DatetimeEvaluator(this.getInputs(), this.getOutputs());
18 | }
19 |
20 | @Override
21 | public String getType() {
22 | return TYPE;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/src/main/java/com/ovh/mls/serving/runtime/tensorflow/TensorflowPbGenerator.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.tensorflow;
2 |
3 | import com.ovh.mls.serving.runtime.core.Evaluator;
4 | import com.ovh.mls.serving.runtime.core.EvaluatorGenerator;
5 | import com.ovh.mls.serving.runtime.core.IncludeAsEvaluatorGenerator;
6 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
7 | import com.typesafe.config.Config;
8 | import org.tensorflow.SavedModelBundle;
9 |
10 | import java.io.File;
11 |
12 | import static com.ovh.mls.serving.runtime.tensorflow.TensorflowEvaluator.DEFAULT_TAG_TENSORFLOW;
13 |
14 | @IncludeAsEvaluatorGenerator(extension = "pb")
15 | public class TensorflowPbGenerator implements EvaluatorGenerator {
16 |
17 | @Override
18 | public Evaluator generate(File filename, Config evaluatorConfig) throws EvaluatorException {
19 |
20 | SavedModelBundle savedModel = SavedModelBundle.load(
21 | filename.getParent(),
22 | DEFAULT_TAG_TENSORFLOW
23 | );
24 | return TensorflowEvaluator.create(savedModel);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/exceptions/WebApplicationExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import javax.ws.rs.WebApplicationException;
8 | import javax.ws.rs.core.MediaType;
9 | import javax.ws.rs.core.Response;
10 | import javax.ws.rs.ext.ExceptionMapper;
11 | import javax.ws.rs.ext.Provider;
12 |
13 |
14 | /**
15 | * Wrap all resteasy exception with clear error messages and status
16 | */
17 | @Provider
18 | public class WebApplicationExceptionMapper implements ExceptionMapper {
19 | private static final Logger LOGGER = LoggerFactory.getLogger(WebApplicationExceptionMapper.class);
20 |
21 | @Override
22 | public Response toResponse(WebApplicationException e) {
23 | LOGGER.error("Error {}", e.getMessage());
24 |
25 | return Response
26 | .status(e.getResponse().getStatus())
27 | .entity(new ErrorMessage(e.getMessage(), e.getResponse().getStatus()))
28 | .type(MediaType.APPLICATION_JSON)
29 | .build();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/commons/src/test/java/com/ovh/mls/serving/runtime/core/io/TensorIOTest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core.io;
2 |
3 | import com.ovh.mls.serving.runtime.core.tensor.Tensor;
4 | import org.junit.jupiter.api.Test;
5 | import tech.tablesaw.api.Table;
6 |
7 | import java.util.Map;
8 |
9 | import static org.junit.jupiter.api.Assertions.assertEquals;
10 |
11 | public class TensorIOTest {
12 |
13 | @Test
14 | public void testIntoTable() {
15 | Tensor tensor1 = Tensor.fromLongData(new long[]{1, 2, 3});
16 | Tensor tensor2 = Tensor.fromStringData(new String[]{"1", "2", "3"});
17 |
18 | Table table = new TensorIO(Map.of("col1", tensor1, "col2", tensor2)).intoTable();
19 | assertEquals(2, table.columnCount());
20 | assertEquals(3, table.rowCount());
21 |
22 | assertEquals(1L, table.column("col1").get(0));
23 | assertEquals(2L, table.column("col1").get(1));
24 | assertEquals(3L, table.column("col1").get(2));
25 | assertEquals("1", table.column("col2").get(0));
26 | assertEquals("2", table.column("col2").get(1));
27 | assertEquals("3", table.column("col2").get(2));
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/evaluator-huggingface/src/main/java/com/ovh/mls/serving/runtime/huggingface/tokenizer/Encoding.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.huggingface.tokenizer;
2 |
3 | import java.util.Optional;
4 |
5 | /**
6 | * Represents the output of a Tokenizer
7 | */
8 | public class Encoding {
9 |
10 | // Pointer to the Rust structure
11 | private long handle = -1;
12 |
13 | private Encoding() {
14 | }
15 |
16 | public native boolean isEmpty();
17 |
18 | public native long size();
19 |
20 | public native String[] getTokens();
21 |
22 | public native Optional[] getWords();
23 |
24 | public native int[] getIds();
25 |
26 | public native int[] getTypeIds();
27 |
28 | public native Offset[] getOffsets();
29 |
30 | public native int[] getSpecialTokensMask();
31 |
32 | public native int[] getAttentionMask();
33 |
34 | /**
35 | * Give back the Rust pointer to be freed
36 | */
37 | private native void releaseHandle();
38 |
39 | @Override
40 | protected void finalize() throws Throwable {
41 | try {
42 | releaseHandle();
43 | } finally {
44 | super.finalize();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/evaluator-huggingface/src/main/java/com/ovh/mls/serving/runtime/huggingface/tokenizer/Offset.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.huggingface.tokenizer;
2 |
3 | import java.util.Objects;
4 |
5 | public class Offset {
6 |
7 | private final long start;
8 | private final long end;
9 |
10 | public Offset(long start, long end) {
11 | this.start = start;
12 | this.end = end;
13 | }
14 |
15 | public long getStart() {
16 | return start;
17 | }
18 |
19 | public long getEnd() {
20 | return end;
21 | }
22 |
23 | @Override
24 | public boolean equals(Object o) {
25 | if (this == o) {
26 | return true;
27 | }
28 | if (o == null || getClass() != o.getClass()) {
29 | return false;
30 | }
31 | Offset offset = (Offset) o;
32 | return start == offset.start && end == offset.end;
33 | }
34 |
35 | @Override
36 | public int hashCode() {
37 | return Objects.hash(start, end);
38 | }
39 |
40 | @Override
41 | public String toString() {
42 | return "Offset{" +
43 | "start=" + start +
44 | ", end=" + end +
45 | '}';
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/exceptions/EvaluationExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 | import com.google.inject.Singleton;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.ws.rs.core.Context;
7 | import javax.ws.rs.core.MediaType;
8 | import javax.ws.rs.core.Response;
9 | import javax.ws.rs.ext.ExceptionMapper;
10 | import javax.ws.rs.ext.Provider;
11 |
12 |
13 | /**
14 | * Return a json error message in case of any exception not handle by the default Exception mapper
15 | */
16 | @Provider
17 | @Singleton
18 | public class EvaluationExceptionMapper implements ExceptionMapper {
19 |
20 | @Context
21 | private HttpServletRequest request;
22 |
23 | /**
24 | * Create a Json response from an exception
25 | */
26 | @Override
27 | public Response toResponse(EvaluationException exception) {
28 | return Response
29 | // Return 500 status and Json message
30 | .status(Response.Status.BAD_REQUEST)
31 | .entity(new ErrorMessage(exception.getMessage(), Response.Status.BAD_REQUEST.getStatusCode()))
32 | .type(MediaType.APPLICATION_JSON)
33 | .build();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/evaluator-onnx/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | serving-runtime
7 | com.ovh.mls.serving.runtime
8 | 1.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | evaluator-onnx
13 |
14 |
15 |
16 | com.ovh.mls.serving.runtime
17 | commons
18 | 1.0.1-SNAPSHOT
19 |
20 |
21 | org.bytedeco
22 | onnxruntime
23 | 1.2.0-1.5.3
24 |
25 |
26 | org.bytedeco
27 | onnxruntime-platform
28 | 1.2.0-1.5.3
29 |
30 |
31 |
--------------------------------------------------------------------------------
/api/src/test/resources/tensorflow/2d_savedmodel/2d-tensorflow-model-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "flow",
3 | "evaluator_manifests": [
4 | {
5 | "type": "tensorflow",
6 | "saved_model_uri": "2d_savedmodel.zip",
7 | "batch_size": 2,
8 | "inputs": [
9 | {
10 | "name": "input",
11 | "shape": "(?, 8)",
12 | "type": "float",
13 | "fields": [
14 | {
15 | "name": "scaled_imputed_t",
16 | "index": 0
17 | },
18 | {
19 | "name": "scaled_imputed_label",
20 | "index": 1
21 | },
22 | {
23 | "name": "scaled_imputed_feature1",
24 | "index": 2
25 | },
26 | {
27 | "name": "scaled_imputed_feature2",
28 | "index": 3
29 | }
30 | ]
31 | }
32 | ],
33 | "outputs": [
34 | {
35 | "name": "output",
36 | "shape": "(?, 1)",
37 | "type": "float",
38 | "fields": [
39 | {
40 | "name": "scaled_imputed_label_predicted",
41 | "index": 0
42 | }
43 | ]
44 | }
45 | ]
46 | }
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/evaluator-torch/README.md:
--------------------------------------------------------------------------------
1 | # Get libtorch
2 |
3 | ```bash
4 | make initialize
5 | ```
6 |
7 | # Serve a TorchScript model
8 |
9 | The model MUST be a TorchScript model, to convert pyTorch models see below.
10 |
11 | Example of a `manifest.json`:
12 | ```json
13 | {
14 | "type": "torch_script",
15 | "saved_model_uri": "model.ts",
16 | "inputs": [
17 | {
18 | "name": "input",
19 | "shape": [
20 | -1,
21 | 10
22 | ],
23 | "type": "long"
24 | }
25 | ],
26 | "outputs": [
27 | {
28 | "name": "output",
29 | "shape": [
30 | 1,
31 | 2
32 | ],
33 | "type": "float"
34 | }
35 | ]
36 | }
37 | ```
38 |
39 | # Convert pyTorch to TorchScript
40 |
41 | Use `converter/convert.py`.
42 | You may have to modify the generated manifest.json to fit your needs.
43 |
44 | ```
45 | usage: convert.py [-h] model output_folder input_examples
46 |
47 | Convert pyTorch models into TorchScript models
48 |
49 | positional arguments:
50 | model pyTorch model (e.g. model.pt)
51 | output_folder TorchScript model and manifest will be saved there
52 | input_example JSON used as input for the model (e.g. [[0.5, 0.2]])
53 |
54 | optional arguments:
55 | -h, --help show this help message and exit
56 | ```
57 |
--------------------------------------------------------------------------------
/evaluator-processors/src/main/java/com/ovh/mls/serving/runtime/processors/StandardScalerManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.processors;
2 |
3 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
4 | import com.ovh.mls.serving.runtime.core.AbstractEvaluatorManifest;
5 | import com.ovh.mls.serving.runtime.core.Field;
6 | import com.ovh.mls.serving.runtime.core.IncludeAsEvaluatorManifest;
7 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
8 |
9 | import java.util.Map;
10 |
11 | @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
12 | @IncludeAsEvaluatorManifest(type = "standard_scaler")
13 | public class StandardScalerManifest extends AbstractEvaluatorManifest {
14 |
15 | private static final String type = "standard_scaler";
16 |
17 | private Map meanStdMap;
18 |
19 | public Map getMeanStdMap() {
20 | return meanStdMap;
21 | }
22 |
23 | public StandardScalerManifest setMeanStdMap(Map meanStdMap) {
24 | this.meanStdMap = meanStdMap;
25 | return this;
26 | }
27 |
28 | @Override
29 | public StandardScaler create(String path) throws EvaluatorException {
30 | return StandardScaler.create(this, path);
31 | }
32 |
33 | @Override
34 | public String getType() {
35 | return type;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/Evaluator.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.ovh.mls.serving.runtime.core.io.TensorIO;
5 | import com.ovh.mls.serving.runtime.exceptions.EvaluationException;
6 |
7 | import java.util.List;
8 |
9 | public interface Evaluator {
10 | /**
11 | * Applies the evaluator operations over a Table and returns the initial
12 | * Table enriched with generated outputs.
13 | *
14 | * @param io 'EvaluatorIO' implementation over which the operations are applied
15 | * @return 'EvaluatorIO' implementation with generated outputs
16 | */
17 | TensorIO evaluate(TensorIO io, EvaluationContext evaluationContext) throws EvaluationException;
18 |
19 | /**
20 | * @return The list of inputs required by the evaluator
21 | */
22 | @JsonProperty("inputs")
23 | List getInputs();
24 |
25 | /**
26 | * @return The list of outputs added to the Table after applying the evaluator
27 | */
28 | @JsonProperty("outputs")
29 | List getOutputs();
30 |
31 | /**
32 | * @return The required number of rows in the Table for the evaluator
33 | */
34 | @JsonProperty("rolling_windows_size")
35 | default int getRollingWindowSize() {
36 | return 1;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/evaluator-huggingface/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | serving-runtime
7 | com.ovh.mls.serving.runtime
8 | 1.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | evaluator-huggingface
13 |
14 |
15 |
16 | com.ovh.mls.serving.runtime
17 | commons
18 | ${project.version}
19 |
20 |
21 |
22 |
23 |
24 |
25 | huggingface-tokenizer-jni/target/release
26 |
27 | *huggingface_tokenizer_jni.dylib
28 | *huggingface_tokenizer_jni.so
29 | *huggingface_tokenizer_jni.dll
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/evaluator-huggingface/src/main/java/com/ovh/mls/serving/runtime/huggingface/tokenizer/HuggingFaceTokenizerEvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.huggingface.tokenizer;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
5 | import com.ovh.mls.serving.runtime.core.AbstractEvaluatorManifest;
6 | import com.ovh.mls.serving.runtime.core.IncludeAsEvaluatorManifest;
7 | import com.ovh.mls.serving.runtime.core.tensor.TensorField;
8 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
9 |
10 | import java.nio.file.Paths;
11 |
12 | // In case of direct deserialization we override parent JsonTypeInfo
13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
14 | @IncludeAsEvaluatorManifest(type = HuggingFaceTokenizerEvaluatorManifest.TYPE)
15 | public class HuggingFaceTokenizerEvaluatorManifest extends AbstractEvaluatorManifest {
16 |
17 | public static final String TYPE = "huggingface_tokenizer";
18 |
19 | @JsonProperty("saved_model_uri")
20 | private String savedModelUri;
21 |
22 | @Override
23 | public HuggingFaceTokenizerEvaluator create(String path) throws EvaluatorException {
24 | Tokenizer tokenizer = Tokenizer.fromFile(Paths.get(savedModelUri));
25 | return new HuggingFaceTokenizerEvaluator(tokenizer);
26 | }
27 |
28 | @Override
29 | public String getType() {
30 | return TYPE;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "onnx_model_uri": "src/test/resources/onnx/iris/iris.onnx",
3 | "batch_size": 1,
4 | "inputs": [
5 | {
6 | "name": "float_input",
7 | "shape": [-1, 4],
8 | "type": "float",
9 | "fields": [
10 | {
11 | "name": "sepal_length",
12 | "index": 0
13 | },
14 | {
15 | "name": "sepal_width",
16 | "index": 1
17 | },
18 | {
19 | "name": "petal_length",
20 | "index": 2
21 | },
22 | {
23 | "name": "petal_width",
24 | "index": 3
25 | }
26 | ]
27 | }
28 | ],
29 | "outputs": [
30 | {
31 | "name": "output_label",
32 | "shape": [-1],
33 | "type": "long",
34 | "fields": [
35 | {
36 | "name": "classification",
37 | "type": "long"
38 | }
39 | ]
40 | },
41 | {
42 | "name": "output_probability",
43 | "shape": [-1, 3],
44 | "type": "float",
45 | "fields": [
46 | {
47 | "name": "probability(0)",
48 | "index": 0,
49 | "key": 0
50 | },
51 | {
52 | "name": "probability(1)",
53 | "index": 1,
54 | "key": 1
55 | },
56 | {
57 | "name": "probability(2)",
58 | "index": 2,
59 | "key": 2
60 | }
61 | ]
62 | }
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/evaluator-onnx/src/test/resources/onnx/iris/manifest-same-output.json:
--------------------------------------------------------------------------------
1 | {
2 | "onnx_model_uri": "src/test/resources/onnx/iris/iris.onnx",
3 | "batch_size": 1,
4 | "inputs": [
5 | {
6 | "name": "float_input",
7 | "shape": [-1, 4],
8 | "type": "float",
9 | "fields": [
10 | {
11 | "name": "sepal_length",
12 | "index": 0
13 | },
14 | {
15 | "name": "sepal_width",
16 | "index": 1
17 | },
18 | {
19 | "name": "petal_length",
20 | "index": 2
21 | },
22 | {
23 | "name": "petal_width",
24 | "index": 3
25 | }
26 | ]
27 | }
28 | ],
29 | "outputs": [
30 | {
31 | "name": "output_label",
32 | "shape": [-1],
33 | "type": "long",
34 | "fields": [
35 | {
36 | "name": "sepal_length",
37 | "type": "long"
38 | }
39 | ]
40 | },
41 | {
42 | "name": "output_probability",
43 | "shape": [-1, 3],
44 | "type": "float",
45 | "fields": [
46 | {
47 | "name": "sepal_width",
48 | "index": 0,
49 | "key": 0
50 | },
51 | {
52 | "name": "petal_length",
53 | "index": 1,
54 | "key": 1
55 | },
56 | {
57 | "name": "petal_width",
58 | "index": 2,
59 | "key": 2
60 | }
61 | ]
62 | }
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/evaluator-timeseries/src/main/java/com/ovh/mls/serving/runtime/timeseries/PredictionIntervalEvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.timeseries;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
5 | import com.ovh.mls.serving.runtime.core.AbstractEvaluatorManifest;
6 | import com.ovh.mls.serving.runtime.core.Field;
7 | import com.ovh.mls.serving.runtime.core.IncludeAsEvaluatorManifest;
8 |
9 | import java.util.Collections;
10 | import java.util.Map;
11 |
12 | // In case of direct deserialization we override parent JsonTypeInfo
13 | @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
14 | @IncludeAsEvaluatorManifest(type = "pi")
15 | public class PredictionIntervalEvaluatorManifest extends AbstractEvaluatorManifest {
16 |
17 | private static final String type = "pi";
18 |
19 | @JsonProperty("residuals_std")
20 | private Map residualsStd = Collections.emptyMap();
21 |
22 | public Map getResidualsStd() {
23 | return residualsStd;
24 | }
25 |
26 | public PredictionIntervalEvaluatorManifest setResidualsStd(Map residualsStd) {
27 | this.residualsStd = residualsStd;
28 | return this;
29 | }
30 |
31 | public PredictionIntervalEvaluator create(String path) {
32 | return PredictionIntervalEvaluator.create(this, path);
33 | }
34 |
35 | @Override
36 | public String getType() {
37 | return type;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/core/builder/from/TensorIOIntoJsonBinary.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core.builder.from;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.ovh.mls.serving.runtime.core.builder.Builder;
6 | import com.ovh.mls.serving.runtime.core.io.TensorIO;
7 | import com.ovh.mls.serving.runtime.exceptions.EvaluationException;
8 | import com.ovh.mls.serving.runtime.utils.img.BinaryContent;
9 | import org.apache.http.entity.ContentType;
10 |
11 | /**
12 | * Convert a TensorIO into a Json Binary content
13 | */
14 | public class TensorIOIntoJsonBinary implements Builder {
15 |
16 | private final ObjectMapper mapper;
17 | private final boolean shouldSimplify;
18 |
19 | public TensorIOIntoJsonBinary(ObjectMapper mapper, boolean shouldSimplify) {
20 | this.shouldSimplify = shouldSimplify;
21 | this.mapper = mapper;
22 | }
23 |
24 | @Override
25 | public BinaryContent build(TensorIO input) throws EvaluationException {
26 | try {
27 | return new BinaryContent(
28 | "json",
29 | ContentType.APPLICATION_JSON,
30 | this.mapper.writeValueAsBytes(
31 | input.intoMap(shouldSimplify)
32 | )
33 | );
34 | } catch (JsonProcessingException e) {
35 | throw new EvaluationException(e);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/FlowEvaluatorManifest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.ovh.mls.serving.runtime.exceptions.EvaluatorException;
5 |
6 | import java.io.IOException;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | @IncludeAsEvaluatorManifest(type = "flow")
11 | public class FlowEvaluatorManifest implements EvaluatorManifest {
12 |
13 | private static final String type = "flow";
14 |
15 | private List outputs = new ArrayList<>();
16 |
17 | @JsonProperty("evaluator_manifests")
18 | private List evaluatorManifests;
19 |
20 | public List getEvaluatorManifests() {
21 | return evaluatorManifests;
22 | }
23 |
24 | public FlowEvaluatorManifest setEvaluatorManifests(List evaluatorManifests) {
25 | this.evaluatorManifests = evaluatorManifests;
26 | return this;
27 | }
28 |
29 | public List getOutputs() {
30 | return outputs;
31 | }
32 |
33 | public FlowEvaluatorManifest setOutputs(List outputs) {
34 | this.outputs = outputs;
35 | return this;
36 | }
37 |
38 | @Override
39 | public FlowEvaluator create(String path) throws EvaluatorException, IOException {
40 | return FlowEvaluator.create(this, path);
41 | }
42 |
43 | @Override
44 | public String getType() {
45 | return type;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/evaluator-onnx/python/iris.py:
--------------------------------------------------------------------------------
1 | from sklearn.datasets import load_iris
2 |
3 | iris = load_iris()
4 | X, y = iris.data, iris.target
5 |
6 | from sklearn.model_selection import train_test_split
7 |
8 | X_train, X_test, y_train, y_test = train_test_split(X, y)
9 |
10 | from sklearn.linear_model import LogisticRegression
11 |
12 | clr = LogisticRegression()
13 | clr.fit(X_train, y_train)
14 |
15 | from skl2onnx import convert_sklearn
16 | from skl2onnx.common.data_types import FloatTensorType
17 |
18 | initial_type = [('float_input', FloatTensorType([None, 4]))]
19 | onx = convert_sklearn(clr, initial_types=initial_type)
20 | with open("iris.onnx", "wb") as f:
21 | f.write(onx.SerializeToString())
22 |
23 | import onnxruntime as rt
24 |
25 | sess = rt.InferenceSession("iris.onnx")
26 |
27 | print("input name='{}' and shape={}".format(
28 | sess.get_inputs()[0].name, sess.get_inputs()[0].shape))
29 | print("output name='{}' and shape={}".format(
30 | sess.get_outputs()[0].name, sess.get_outputs()[0].shape))
31 |
32 | input_name = sess.get_inputs()[0].name
33 | label_name = sess.get_outputs()[0].name
34 |
35 | import numpy
36 |
37 | pred_onx = sess.run([label_name], {input_name: X_test.astype(numpy.float32)})[:5]
38 | print(X_test.astype(numpy.float32)[:5])
39 | print("Classification:")
40 | print(pred_onx[:5])
41 |
42 | prob_name = sess.get_outputs()[1].name
43 | prob_rt = sess.run([prob_name], {input_name: X_test.astype(numpy.float32)})[:5]
44 |
45 | import pprint
46 |
47 | print("Proba")
48 | pprint.pprint(prob_rt[0][:5])
49 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/exceptions/RestExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.exceptions;
2 |
3 | import com.google.inject.Singleton;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.ws.rs.core.Context;
9 | import javax.ws.rs.core.MediaType;
10 | import javax.ws.rs.core.Response;
11 | import javax.ws.rs.ext.ExceptionMapper;
12 | import javax.ws.rs.ext.Provider;
13 |
14 | import static org.eclipse.jetty.http.HttpStatus.INTERNAL_SERVER_ERROR_500;
15 |
16 | /**
17 | * Return a json error message in case of any exception not handle by the default Exception mapper
18 | */
19 | @Provider
20 | @Singleton
21 | public class RestExceptionMapper implements ExceptionMapper {
22 |
23 | private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionMapper.class);
24 |
25 | @Context
26 | private HttpServletRequest request;
27 |
28 | /**
29 | * Create a Json response from an exception
30 | */
31 | @Override
32 | public Response toResponse(Exception exception) {
33 |
34 | LOGGER.error("Error during Request {}, Exception {}", exception.getMessage());
35 | LOGGER.error("Error", exception);
36 |
37 | return Response
38 | // Return 500 status and Json message
39 | .status(INTERNAL_SERVER_ERROR_500)
40 | .entity(new ErrorMessage("Internal error"))
41 | .type(MediaType.APPLICATION_JSON)
42 | .build();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/api/src/main/java/com/ovh/mls/serving/runtime/swagger/SwaggerHomeResource.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.swagger;
2 |
3 |
4 | import io.swagger.v3.oas.annotations.Hidden;
5 |
6 | import javax.ws.rs.GET;
7 | import javax.ws.rs.Path;
8 | import javax.ws.rs.PathParam;
9 | import javax.ws.rs.Produces;
10 | import javax.ws.rs.core.Context;
11 | import javax.ws.rs.core.MediaType;
12 | import javax.ws.rs.core.Response;
13 | import javax.ws.rs.core.UriInfo;
14 | import java.io.InputStream;
15 | import java.net.URI;
16 | import java.net.URISyntaxException;
17 |
18 | /**
19 | * Swagger UI
20 | */
21 | @Path("/")
22 | @Hidden
23 | @Produces(MediaType.TEXT_HTML)
24 | public class SwaggerHomeResource {
25 | private static final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
26 |
27 | @Context
28 | private UriInfo uriInfo;
29 |
30 | @GET
31 | public Response viewHome() throws URISyntaxException {
32 | if (!uriInfo.getAbsolutePath().toString().endsWith("/")) {
33 | return Response.temporaryRedirect(new URI(uriInfo.getAbsolutePath().toString() + "/")).build();
34 | }
35 |
36 | return Response
37 | .ok(contextClassLoader.getResourceAsStream("swagger/swagger.html"))
38 | .build();
39 | }
40 |
41 | @GET
42 | @Path("{file}{ext:(.js|.css)}")
43 | @Produces("text/css")
44 | public InputStream renderFiler(@PathParam("file") String file, @PathParam("ext") String ext) {
45 | return contextClassLoader.getResourceAsStream("swagger/" + file + ext);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2013-2019, OVH SAS
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/evaluator-torch/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := initialize
2 |
3 | WORKDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
4 |
5 | OS :=
6 | ifeq ($(OS),Windows_NT)
7 | OS = windows
8 | else
9 | UNAME_S := $(shell uname -s)
10 | ifeq ($(UNAME_S),Linux)
11 | OS = linux
12 | endif
13 | ifeq ($(UNAME_S),Darwin)
14 | OS = darwin
15 | endif
16 | endif
17 |
18 | .PHONY: initialize
19 | initialize: initialize-${OS}
20 |
21 | .PHONY: initialize-darwin
22 | initialize-darwin:
23 | curl "https://download.pytorch.org/libtorch/cpu/libtorch-macos-1.5.1.zip" -o libtorch-darwin-cpu.zip && \
24 | unzip -q libtorch-darwin-cpu.zip && \
25 | rm -rf libtorch-darwin-cpu && mv -vf libtorch libtorch-darwin-cpu && rm -v libtorch-darwin-cpu.zip
26 | # Fix OpenMP https://github.com/pytorch/pytorch/issues/38607
27 | install_name_tool -id @rpath/libiomp5.dylib libtorch-darwin-cpu/lib/libiomp5.dylib
28 |
29 | .PHONY: initialize-linux
30 | initialize-linux:
31 | curl "https://download.pytorch.org/libtorch/cpu/libtorch-shared-with-deps-1.5.1%2Bcpu.zip" -o libtorch-linux-cpu.zip && \
32 | unzip -q libtorch-linux-cpu.zip && \
33 | rm -rf libtorch-linux-cpu && mv -vf libtorch libtorch-linux-cpu && rm -v libtorch-linux-cpu.zip
34 | # Fix OpenMP
35 | patchelf --set-soname libgomp-7c85b1e2.so.1 libtorch-linux-cpu/lib/libgomp-7c85b1e2.so.1
36 |
37 | .PHONY: initialize-windows
38 | initialize-windows:
39 | curl "https://download.pytorch.org/libtorch/cpu/libtorch-win-shared-with-deps-1.5.1%2Bcpu.zip" -o libtorch-windows-cpu.zip && \
40 | unzip -q libtorch-windows-cpu.zip && \
41 | rm -rf libtorch-windows-cpu && mv -vf libtorch libtorch-windows-cpu && rm -v libtorch-windows-cpu.zip
42 |
--------------------------------------------------------------------------------
/evaluator-tensorflow/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | serving-runtime
7 | com.ovh.mls.serving.runtime
8 | 1.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | evaluator-tensorflow
13 |
14 |
15 | 1.15.0
16 | 3.5.1
17 |
18 |
19 |
20 |
21 | com.ovh.mls.serving.runtime
22 | commons
23 | 1.0.1-SNAPSHOT
24 |
25 |
26 | org.tensorflow
27 | tensorflow
28 | ${tensorflow.version}
29 |
30 |
31 | org.tensorflow
32 | proto
33 | ${tensorflow.version}
34 |
35 |
36 | com.google.protobuf
37 | protobuf-java
38 | ${protobuf.version}
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/evaluator-timeseries/src/test/resources/timeseries/datetime-string-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "inputs": [
3 | {
4 | "name": "date",
5 | "type": "string",
6 | "shape": [-1, -1],
7 | "format": "yyyy-MM-dd HH:mm:ss"
8 | }
9 | ],
10 | "outputs": [
11 | {
12 | "name": "year",
13 | "type": "integer",
14 | "shape": [-1, -1],
15 | "format": "YEAR"
16 | },
17 | {
18 | "name": "month",
19 | "type": "integer",
20 | "shape": [-1, -1],
21 | "format": "MONTH"
22 | },
23 | {
24 | "name": "dayofmonth",
25 | "type": "integer",
26 | "shape": [-1, -1],
27 | "format": "DAYOFMONTH"
28 | },
29 | {
30 | "name": "hourofday",
31 | "type": "integer",
32 | "shape": [-1, -1],
33 | "format": "HOUROFDAY"
34 | },
35 | {
36 | "name": "minuteofhour",
37 | "type": "integer",
38 | "shape": [-1, -1],
39 | "format": "MINUTEOFHOUR"
40 | },
41 | {
42 | "name": "dayofweek",
43 | "type": "integer",
44 | "shape": [-1, -1],
45 | "format": "DAYOFWEEK"
46 | },
47 | {
48 | "name": "year-string",
49 | "type": "string",
50 | "shape": [-1, -1],
51 | "format": "yyyy"
52 | },
53 | {
54 | "name": "year-float",
55 | "type": "float",
56 | "shape": [-1, -1],
57 | "format": "YEAR"
58 | },
59 | {
60 | "name": "timestamp-seconds",
61 | "type": "long",
62 | "shape": [-1, -1],
63 | "format": "TS_S"
64 | },
65 | {
66 | "name": "timestamp-years",
67 | "type": "long",
68 | "shape": [-1, -1],
69 | "format": "TS_Y"
70 | }
71 | ]
72 | }
73 |
--------------------------------------------------------------------------------
/evaluator-torch/src/test/java/com/ovh/mls/serving/runtime/torch/TorchScriptEvaluatorManifestTest.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.torch;
2 |
3 | import com.ovh.mls.serving.runtime.core.DataType;
4 | import com.ovh.mls.serving.runtime.core.EvaluatorManifest;
5 | import com.ovh.mls.serving.runtime.core.EvaluatorUtil;
6 | import com.ovh.mls.serving.runtime.core.io.TensorIO;
7 | import com.ovh.mls.serving.runtime.core.tensor.Tensor;
8 | import com.typesafe.config.ConfigFactory;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Test;
11 |
12 | import java.io.IOException;
13 | import java.util.Map;
14 |
15 | class TorchScriptEvaluatorManifestTest {
16 |
17 | @Test
18 | public void testCreate() throws IOException {
19 | // Load evaluator
20 | EvaluatorUtil evaluatorUtil = new EvaluatorUtil(ConfigFactory.load());
21 | TorchScriptEvaluatorManifest torchScriptEvaluatorManifest = (TorchScriptEvaluatorManifest) evaluatorUtil
22 | .getObjectMapper()
23 | .readValue(
24 | getClass().getResourceAsStream("/manifest.json"),
25 | EvaluatorManifest.class
26 | );
27 | TorchScriptEvaluator evaluator = torchScriptEvaluatorManifest.create("src/test/resources");
28 |
29 | // Evaluate
30 | TensorIO input = new TensorIO(Map.of(
31 | "input_0", new Tensor(DataType.FLOAT, new int[]{2}, new float[]{0.5f, 0.2f})
32 | ));
33 | TensorIO output = evaluator.evaluateTensor(input);
34 | float[] output0 = (float[]) output.getTensors().get("output_0").getData();
35 |
36 | Assertions.assertEquals(output0[0], 0.120716706f, 1e-6);
37 | Assertions.assertEquals(output0[1], 0.054772355f, 1e-6);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | WORKDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
2 | NAME := serving-runtime-base
3 | REGISTRY :=
4 | REPOSITORY := infaas
5 | .DEFAULT_GOAL := build
6 | TAG := $(lastword $(subst /, ,$(shell git rev-parse --abbrev-ref HEAD)))
7 | M2 := '$(HOME)/.m2'
8 |
9 | H5_CONVERTER := h5_converter/dist/h5_converter
10 | MAVEN_PROFILE=full
11 |
12 | .PHONY: docker-base
13 | docker-base:
14 | docker build --target base -t $(NAME) -f dockerfiles/$(MAVEN_PROFILE).Dockerfile .
15 |
16 | .PHONY: docker-test
17 | docker-test: docker-base
18 | docker run --rm -v $(WORKDIR):/usr/src/app -v $(M2):/root/.m2 $(NAME) make test H5_CONVERTER=/usr/src/bin/h5_converter
19 |
20 | .PHONY: docker-test
21 | docker-build: docker-base
22 | docker run --rm -v $(WORKDIR):/usr/src/app -v $(M2):/root/.m2 $(NAME) make build H5_CONVERTER=/usr/src/bin/h5_converter
23 |
24 | .PHONY: docker-build-api
25 | docker-build-api:
26 | docker build --build-arg MAVEN_PROFILE=$(MAVEN_PROFILE) -t $(NAME) -f dockerfiles/$(MAVEN_PROFILE).Dockerfile .
27 |
28 | .PHONY: docker-push-api
29 | docker-push-api:
30 | docker tag $(NAME) $(REGISTRY)/$(REPOSITORY)/$(NAME):$(TAG)
31 | docker push $(REGISTRY)/$(REPOSITORY)/$(NAME):$(TAG)
32 |
33 | .PHONY: build
34 | build:
35 | mvn package -DskipTests -B -P$(MAVEN_PROFILE)
36 |
37 | .PHONY: test
38 | test:
39 | mvn -B verify -DtrimStackTrace=false -Devaluator.tensorflow.h5_converter.path=$(H5_CONVERTER) -P$(MAVEN_PROFILE)
40 |
41 | .PHONY: deploy
42 | deploy:
43 | mvn -B deploy -DskipTests -P$(MAVEN_PROFILE)
44 |
45 | .PHONY: initialize-tensorflow
46 | initialize-tensorflow:
47 | make -C evaluator-tensorflow/h5_converter build
48 |
49 | .PHONY: initialize-huggingface
50 | initialize-huggingface:
51 | make -C evaluator-huggingface/huggingface-tokenizer-jni
52 |
53 | .PHONY: initialize-torch
54 | initialize-torch:
55 | make -C evaluator-torch
56 |
--------------------------------------------------------------------------------
/api/src/test/java/com/ovh/mls/serving/runtime/IsCloseTo.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime;
2 |
3 | import org.hamcrest.Description;
4 | import org.hamcrest.Factory;
5 | import org.hamcrest.Matcher;
6 | import org.hamcrest.TypeSafeMatcher;
7 |
8 | public class IsCloseTo extends TypeSafeMatcher {
9 |
10 | private final float delta;
11 | private final float value;
12 |
13 | public IsCloseTo(float value, float error) {
14 | this.delta = error;
15 | this.value = value;
16 | }
17 |
18 | /**
19 | * Creates a matcher of {@link Double}s that matches when an examined double is equal
20 | * to the specified operand, within a range of +/- error.
21 | *
22 | * For example:
23 | * assertThat(1.03, is(closeTo(1.0, 0.03)))
24 | *
25 | * @param operand the expected value of matching doubles
26 | * @param error the delta (+/-) within which matches will be allowed
27 | */
28 | @Factory
29 | public static Matcher closeTo(float operand, float error) {
30 | return new IsCloseTo(operand, error);
31 | }
32 |
33 | @Override
34 | public boolean matchesSafely(Float item) {
35 | return actualDelta(item) <= 0.0;
36 | }
37 |
38 | @Override
39 | public void describeMismatchSafely(Float item, Description mismatchDescription) {
40 | mismatchDescription.appendValue(item)
41 | .appendText(" differed by ")
42 | .appendValue(actualDelta(item));
43 | }
44 |
45 | @Override
46 | public void describeTo(Description description) {
47 | description.appendText("a numeric value within ")
48 | .appendValue(delta)
49 | .appendText(" of ")
50 | .appendValue(value);
51 | }
52 |
53 | private double actualDelta(Float item) {
54 | return (Math.abs((item - value)) - delta);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/ovh/mls/serving/runtime/core/builder/InputStreamJsonIntoTensorIO.java:
--------------------------------------------------------------------------------
1 | package com.ovh.mls.serving.runtime.core.builder;
2 |
3 | import com.fasterxml.jackson.core.type.TypeReference;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.ovh.mls.serving.runtime.core.io.TensorIO;
6 | import com.ovh.mls.serving.runtime.core.tensor.Tensor;
7 | import com.ovh.mls.serving.runtime.exceptions.EvaluationException;
8 |
9 | import java.io.InputStream;
10 | import java.util.AbstractMap;
11 | import java.util.Map;
12 | import java.util.stream.Collectors;
13 |
14 | public class InputStreamJsonIntoTensorIO implements Builder {
15 |
16 | private final ObjectMapper mapper;
17 |
18 | public InputStreamJsonIntoTensorIO(ObjectMapper mapper) {
19 | this.mapper = mapper;
20 | }
21 |
22 | @Override
23 | public TensorIO build(InputStream inputStream) throws EvaluationException {
24 | try {
25 | ObjectIntoTensor tensorBuilder = new ObjectIntoTensor();
26 | // The InputStream will be converted into Map
27 | TypeReference