Upgrade notes
Upgrading to version 2.0.0
The release 2.0.0
drops the support for the V1 version of the API. Before
upgrading your code and deploying into production please ensure that you are no
longer using the V1 version of the API.
Upgrading to version 1.0.0
The release 1.0.0
of the DEEPaaS API implements several backwards
incompatible changes when compared with the previous releases in the 0.X.X
series. Before upgrading your code and deploying into production, read the
following, as changes are needed in your model. Please go through the following
checklist in order.
Python version
The new version of DEEPaaS uses
aiohttp
which requires at least Python 3.5.3. So please update you module accordingly if needed.Migrate new namespace entry point.
Previous code relied on a top-level entry point named
deepaas.model
, where we searched for the required functions or methods to be invoked for each API action.Now the namespace has changed to
deepaas.v2.model
.Therefore, assuming that your code for V1 of the API was under the
my_model.api
and you defined your the entry point as follows:[entry_points] deepaas.model = my_model = my_model.api
You should migrate to the following:
[entry_points] deepaas.v2.model = my_model = my_model.api
Note
If you do not change the namespace, we will try to load the old entrypoint. However, this is deprecated and you should upgrade as soon as possible.
Migrate from returning dictionaries to use an argument parser to define train and predict arguments.
Previous code relied on returning arbitrary dictionaries that were used to generate the arguments for each of the API endpoints. This is not anymore supported and you should return a
webargs
field dictionary (check here for more information. This is still done by defining theget_predict_args
andget_train_args
functions. These functions must receive no arguments and they should return a dictionary as follows:from webargs import fields (...) def get_predict_args(): return { "arg1": fields.Str( required=False, # force the user to define the value missing="foo", # default value to use enum=["choice1", "choice2"], # list of choices description="Argument one" # help string ), }
Note
If you do still follow the old way of returning the arguments we will try to load the arguments using the old
get_*_args
functions. However, this is DEPRECATED and you should migrate to using the argument parser as soon as possible. All the arguments will be converted to Strings, therefore you will loose any type checking, etc.Explicitly define your input arguments.
The previous version of the API defined two arguments for inference:
data
andurls
. This is not anymore true, and you must define your own input arguments. To replicate the response of v1 you have to define:from webargs import fields (...) def get_predict_args(): return { 'files': fields.Field( required=False, missing=None, type="file", data_key="data", location="form", description="Select the image you want to classify."), 'urls': fields.Url( required=False, missing=None, description="Select an URL of the image you want to classify.") }
Then, you will get your input data in the
data
andurls
keyword arguments in your application.Note
For the moment, in contrast with v1, only one url field at the same time is enabled, although multi-url (along with multi-files) support is coming soon.
Define your responses for the prediction.
Now, unless you explicitly define your application response schema, whatever you return will be converted into a string and wrapped in the following response:
{ "status": "OK", "predictions": "<model response as string>" }
Change in the ``predict`` function name.
The
predict_url
andpredict_data
functions have been merged into a singlepredict
function. In addition, arguments are now passed as unpacked keyword arguments, not anymore as a dictionary. So if you want to upgrade to v2 with minimal code changes, you just have to add the following function to your .py file:def predict(**args): if (not any([args['urls'], args['files']]) or all([args['urls'], args['files']])): raise Exception("You must provide either 'url' or 'data' in the payload") if args['files']: args['files'] = [args['files']] # patch until list is available return predict_data(args) elif args['urls']: args['urls'] = [args['urls']] # patch until list is available return predict_url(args)
Changes in the data response
The returned object in
args['files']
is no longer awerkzeug.FileStorage
but adeepaas.model.v2.wrapper.UploadedFile
which has attributes likename
(name of the argument where this file is being sent),filename
(complete file path to the temporary file in the filesystem) andcontent_type
(content-type of the uploaded file).The main difference is that now you should read the bytes using
open(f.filename, 'rb').read()
instead off.read()
.Catch error function
By default Exceptions raised in the PREDICT method from the application side will be rendered as
500 - HTTPInternalServerError
with the messageServer got itself in trouble
. If you want to render some Exceptions with custom status codes and custom messages (see thereason
arg) you have to raise an aiohttp web exception. For example:from aiohttp.web import HTTPBadRequest try: f() except Exception as e: raise HTTPBadRequest(reason=e)
And if you want to wrap a whole function so that any error it raises is passed as a certain HTTP error:
def catch_error(f): def wrap(*args, **kwargs): try: return f(*args, **kwargs) except Exception as e: raise HTTPBadRequest(reason=e) return wrap @catch_error def f(): ...
Note
Python Exceptions from the application side for the TRAIN method will be correctly rendered by DEEPaaS so there is no need to wrap the train function with the catch_error decorator.
API url
Now the API functions are accessed under http://api_url/docs (eg. http://0.0.0.0:5000/docs)