Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 24 additions & 18 deletions docs/custom_code.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

In some cases, you may need to create and deploy custom code as part of your MLOps workflow using vetiver. This could be necessary when you need to:

- deploy custom models in vetiver
- deploy unsupported models in vetiver
- include custom code in vetiver
- deploy a vetiver model with a custom pipeline
- deploy custom models in vetiver
- deploy unsupported models in vetiver
- include custom code in vetiver
- deploy a vetiver model with a custom pipeline

You may also have custom code in a known framework, such as a column transformer for a scikit-learn model.
You may also have custom code in a known framework, such as a column transformer for a scikit-learn model.

In these cases, extra steps will be required to successfully create and deploy a `VetiverModel` object.

Expand All @@ -17,12 +17,12 @@ Vetiver supports basic [scikit-learn](https://scikit-learn.org/), [torch](https:

To create a model handler, you should create a subclass of vetiver's `BaseHandler` class. This handler should include the following:

- `model_type`: A static method that declares the type of your model.
- `handler_predict()`: A method that defines how predictions should be made for your model. This method is used at the /predict endpoint in the VetiverAPI.
- `model_type`: A static method that declares the type of your model.
- `handler_predict()`: A method that defines how predictions should be made for your model. This method is used at the /predict endpoint in the VetiverAPI.

Here's an example of a handler for a model of `newmodeltype` type. Once you have defined your handler, you can initialize it with your model and pass it to the `VetiverModel` class.

```python
``` python
from vetiver.handlers.base import BaseHandler

class CustomHandler(BaseHandler):
Expand Down Expand Up @@ -60,7 +60,7 @@ To deploy custom code, you need to include the necessary source code in your dep

If your `VetiverModel` includes custom source code, you need to include that code in your deployment files to build an API in another location. The example below shows a user-created `FeatureSelector`, which is part of a scikit-learn pipeline.

```{.python filename="model.py"}
``` {.python filename="model.py"}
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
Expand Down Expand Up @@ -94,18 +94,18 @@ board = pins.board_connect(allow_pickle_read=True)
vetiver_pin_write(board, v)
```

::: {.panel-tabset}
::: panel-tabset
## Docker

To generate files needed to start a Docker container, you can use the command `vetiver.prepare_docker`.

```{.python}
``` python
vetiver.prepare_docker(board, "selected_decision_tree")
```

When you run this line, 3 files are generated: a Dockerfile, an `app.py` file, and a `vetiver_requirements.txt`. In the `app.py` file, you'll need to add an import statement that is formatted `from {name of file, excluding .py, that has custom element} import {name of custom element}`.

```{.python filename="app.py"}
``` {.python filename="app.py"}
from vetiver import VetiverModel
import vetiver
import pins
Expand All @@ -121,7 +121,7 @@ api = vetiver_api.app

Add a line to your Dockerfile to copy your source file(s) into your Docker container. The format will be `COPY path/to/your/filename.py /vetiver/app/filename.py`, where the destination is always in the `/vetiver/app/` directory.

```{.bash filename="Dockerfile"}
``` {.bash filename="Dockerfile"}
# # Generated by the vetiver package; edit with care
# start with python base image
FROM python:3.10
Expand Down Expand Up @@ -152,13 +152,13 @@ CMD ["uvicorn", "app.app:api", "--host", "0.0.0.0", "--port", "8080"]

To deploy custom code to Posit Connect, you'll first start with the command `vetiver.write_app`.

```{.python}
``` python
vetiver.write_app(board, 'selected_decision_tree')
```

This will generate an `app.py` file, where you'll need to add an import statement that is formatted `from {name of file, excluding .py, that has custom element} import {name of custom element}`.

```{.python filename=="app.py"}
``` {.python filename="=\"app.py\""}
from vetiver import VetiverModel
import vetiver
import pins
Expand All @@ -174,7 +174,7 @@ api = vetiver_api.app

After editing the `app.py` file, you can deploy it to Posit Connect using the `rsconnect` package. Use the `rsconnect.api.actions.deploy_python_fastapi()` function to deploy the API, specifying the Connect server URL, API key, directory containing the `app.py` and `model.py` files, and the entry point of the API.

```{.python}
``` python
from rsconnect.api.actions import deploy_python_fastapi
import rsconnect

Expand All @@ -191,9 +191,15 @@ rsconnect.actions.deploy_python_fastapi(
directory = "./", # path to the directory containing the app.py and model.py files
entry_point = "app:api" # the API is the app.py file, in a variable named api
)

```

:::

## Common Pitfalls

When deploying custom code, the most common error is something similar to `AttributeError: Can't get attribute 'ExampleModel' on <module '__main__' (built-in)>`. There are a few possible causes for this error:

1. The original `ExampleModel` may have been pinned from inside a Jupyter Notebook. Because pickling only saves a reference for how to read a class, not the source code, a custom model transformer pinned from a Jupyter Notebook cannot be imported and resolved later. To fix this, repin your model/transformer from inside a Python script.

2. You may not be uploading the custom code to be used later. When deploying, you'll want to add the files containing your custom code to the `extra_files` argument so that it can be imported, eg, `vetiver.deploy_rsconnect(connect_server, board, model_name, extra_files=['custom_model.py', 'requirements.txt'])` .

Please note that the above steps are a general guide, and you may need to adapt them to your specific use case and deployment environment. If you have any questions, please consider [opening an issue](https://github.com/rstudio/vetiver-python/issues/new).
Loading