Creating Your Own service¶
This tutorial will continue from where Deploying Your First Service left off.
There we created a basic service using the mvi init
command.
>>> mvi init
project_name [my_project]: my_first_mvi_project
And this created three files and a directory.
>>> ls ./my_first_mvi_project -a
. .. .s2i README.md requirements.txt service.py
Where service.py contains the service source code. Let’s look at how a typical service.py file might look.
Setup¶
As usual, the first step is to import packages:
import logging
from mvi.mvi import MviService, notify, Severity
Only MviService
is actually required, but we recommend to import
and use the standard python logging
package. notify()
and Severity
are used
for the notification functionality in MVI.
If any external packages are used, they must be specified in the requirements.txt file. That way they will be installed the service is deployed.
Note
It is highly recommended to pin your requirements to specific versions when in a production environment, for example numpy==1.19.4
Next we initialize a MviService
object and a logger:
mvi = MviService()
logger = logging.getLogger(__name__)
The MviService
object helps us set up entrypoints for the service,
add parameters and start the service. The logger is just a regular python logging
object, which MVI natively supports. The logs from a service can be read from the
dashboard or using mvi logs <name> <version>
.
Defining parameters¶
It is possible to define variables that automatically get exposed to the API and can be freely changed from outside a running service:
mvi.add_parameter("greeting_phrase", "Hello")
To use the parameter in the code you do:
greeting_phrase = mvi.get_parameter("greeting_phrase")
This way you can control the behaviour of your running services without having to make any code changes. We recommend using this for e.g. tuning parameters.
These parameters can be changed via the docs of the service, see API Autodocs for how to do it. Another way to change these parameters is by sending HTTP POST requests directly, for instance via pythons requests library.
Example of changing the greeting_phrase
parameter for this service:
json_data = {"greeting_phrase": "Bonjour"}
response = requests.post(
"http:://<host>/services/<my_service>/parameters/greeting_phrase",
json=json_data,
headers={"Authorization": f"Bearer {TOKEN}"})
Creating an Entrypoint¶
To define an entrypoint for a service we use the entrypoint()
decorator:
@mvi.entrypoint
def hello(name: str) -> str:
greeting_phrase = mvi.get_parameter("greeting_phrase")
logger.info(f"Greeting someone with the name: {name}")
return f"{greeting_phrase} {name}"
This will automatically expose the hello()
function to the API. We strongly
recommend that you use type hints in your MVI entrypoint functions. That way, you
will get type verification in your API and the auto-generated documentation will show
the expected data types. Please take a look at Typing in the SDK for a
more detailed guide on how typing is handled in MVI.
Notifications¶
Notifications is another feature of MVI. When a notification is raised it can be viewed
on the dashboard at http://your-host or sent to an email address. To add a notification
we use the notify()
function. They can be placed in a conditional statement
to send a notification if it’s true. With notifications added, the hello()
function
now looks like this:
@mvi.entrypoint
def hello(name: str) -> str:
greeting_phrase = mvi.get_parameter("greeting_phrase")
if name == "World":
notify(
msg="Someone is trying to greet the World, too time consuming. Skipping!",
severity=Severity.WARNING,
)
return "Greeting failed"
logger.info(f"Greeting someone with the name: {name}")
return f"{greeting_phrase} {name}"
Here we want to stop the user from greeting the world, because that would take a lot of time. So if the name is “World” we send a notification.
Starting the Service¶
The last thing we have to do is to ensure the service runs once it is deployed:
mvi.run()
Full Code¶
The code we just looked at is the same code that is generated by mvi init
by
default. It is fewer than 25 lines of code to create a fully functional service
complete with logging, notifications and configurable parameters. All together it
looks like this:
import logging
from mvi.mvi import MviService, notify, Severity
mvi = MviService()
logger = logging.getLogger(__name__)
mvi.add_parameter("greeting_phrase", "Hello")
@mvi.entrypoint
def hello(name: str) -> str:
greeting_phrase = mvi.get_parameter("greeting_phrase")
if name == "World":
notify(
msg="Someone is trying to greet the World, too time consuming. Skipping!",
severity=Severity.WARNING,
)
return "Greeting failed"
logger.info(f"Greeting someone with the name: {name}")
return f"{greeting_phrase} {name}"
mvi.run()
Deploying the Service¶
As a reminder from Deploying Your First Service we will deploy our new service.
>>> mvi deploy hello 1.0.0 ./my_first_mvi_project/
Deploying service...
Service deployed successfully
MAIN NAME VERSION STATUS RUNNING
------ ------ --------- -------- -----------------------------------
* hello 1.0.0 running Running (since 2020-11-23 10:29:01)
What’s Next?¶
Now you have seen the different components of the SDK and you should be ready to create your own service. The next step could be to take a look at the manager Dashboard, or the Software Development Kit documentation.