├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── workflows │ ├── R-CMD-check-hard.yaml │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── api.R ├── attach-pkgs.R ├── caret.R ├── dashboard.R ├── endpoint.R ├── gam.R ├── glm.R ├── handlers.R ├── import-standalone-obj-type.R ├── import-standalone-types-check.R ├── keras.R ├── kproto.R ├── lm.R ├── luz.R ├── meta.R ├── mlr3.R ├── monitor.R ├── open-api-spec.R ├── pin-read-write.R ├── prepare.R ├── prototype.R ├── ranger.R ├── recipe.R ├── renv.R ├── rsconnect.R ├── sagemaker-utils.R ├── sagemaker.R ├── stacks.R ├── tidymodels.R ├── vetiver-model.R ├── vetiver-package.R ├── write-docker.R ├── write-plumber.R ├── xgboost.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── inst ├── biv_tidymodels.R ├── chicago_rpart.R ├── mtcars_keras.R ├── mtcars_lm.R ├── mtcars_luz.R ├── mtcars_ranger.R ├── mtcars_xgb.R ├── netflix_tidymodels.R ├── pima_Learner.R ├── plumber │ ├── biv-svm │ │ └── plumber.R │ ├── chicago-rpart │ │ └── plumber.R │ ├── mtcars-lm │ │ └── plumber.R │ ├── mtcars-ranger │ │ └── plumber.R │ ├── mtcars-xgb │ │ └── plumber.R │ └── netflix-descriptions │ │ └── plumber.R ├── requirements │ ├── keras-requirements.txt │ └── luz-renviron.txt ├── rmarkdown │ └── templates │ │ ├── vetiver_dashboard │ │ ├── skeleton │ │ │ └── skeleton.Rmd │ │ └── template.yaml │ │ └── vetiver_model_card │ │ ├── skeleton │ │ └── skeleton.Rmd │ │ └── template.yaml ├── vendor │ └── renv.R └── vetiver.png ├── man ├── api_spec.Rd ├── attach_pkgs.Rd ├── augment.vetiver_endpoint.Rd ├── augment.vetiver_endpoint_sagemaker.Rd ├── figures │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-soft-deprecated.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── logo.png ├── handler_startup.Rd ├── map_request_body.Rd ├── predict.vetiver_endpoint.Rd ├── predict.vetiver_endpoint_sagemaker.Rd ├── reexports.Rd ├── vetiver-package.Rd ├── vetiver_api.Rd ├── vetiver_compute_metrics.Rd ├── vetiver_create_description.Rd ├── vetiver_create_meta.Rd ├── vetiver_create_ptype.Rd ├── vetiver_create_rsconnect_bundle.Rd ├── vetiver_dashboard.Rd ├── vetiver_deploy_rsconnect.Rd ├── vetiver_deploy_sagemaker.Rd ├── vetiver_endpoint.Rd ├── vetiver_endpoint_sagemaker.Rd ├── vetiver_model.Rd ├── vetiver_pin_metrics.Rd ├── vetiver_pin_write.Rd ├── vetiver_plot_metrics.Rd ├── vetiver_pr_predict.Rd ├── vetiver_prepare_docker.Rd ├── vetiver_python_requirements.Rd ├── vetiver_sm_build.Rd ├── vetiver_sm_delete.Rd ├── vetiver_type_convert.Rd ├── vetiver_write_docker.Rd └── vetiver_write_plumber.Rd ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── api.md │ ├── attach-pkgs.md │ ├── caret.md │ ├── choose-version.md │ ├── create-ptype.md │ ├── dashboard.md │ ├── gam.md │ ├── glm.md │ ├── keras.md │ ├── kproto.md │ ├── luz.md │ ├── mlr3.md │ ├── monitor.md │ ├── monitor │ │ └── default-metrics-plot.svg │ ├── pin-read-write.md │ ├── predict.md │ ├── ranger.md │ ├── recipe.md │ ├── rsconnect.md │ ├── sagemaker.md │ ├── stacks.md │ ├── tidymodels.md │ ├── type-convert.md │ ├── write-docker.md │ ├── write-plumber.md │ └── xgboost.md │ ├── helper.R │ ├── setup.R │ ├── test-api.R │ ├── test-attach-pkgs.R │ ├── test-caret.R │ ├── test-choose-version.R │ ├── test-create-ptype.R │ ├── test-dashboard.R │ ├── test-gam.R │ ├── test-glm.R │ ├── test-keras.R │ ├── test-kproto.R │ ├── test-luz.R │ ├── test-mlr3.R │ ├── test-monitor.R │ ├── test-pin-read-write.R │ ├── test-predict.R │ ├── test-ranger.R │ ├── test-recipe.R │ ├── test-rsconnect.R │ ├── test-sagemaker.R │ ├── test-stacks.R │ ├── test-tidymodels.R │ ├── test-type-convert.R │ ├── test-write-docker.R │ ├── test-write-plumber.R │ └── test-xgboost.R ├── vetiver-r.Rproj └── vignettes ├── .gitignore └── vetiver.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^requirements\.txt$ 2 | ^renv$ 3 | ^renv\.lock$ 4 | ^vetiver_renv\.lock$ 5 | ^vetiver-r\.Rproj$ 6 | ^\.Rproj\.user$ 7 | ^LICENSE\.md$ 8 | ^README\.Rmd$ 9 | ^cran-comments\.md$ 10 | ^\.github$ 11 | ^vignettes/articles$ 12 | ^CODE_OF_CONDUCT\.md$ 13 | ^_pkgdown\.yml$ 14 | ^docs$ 15 | ^pkgdown$ 16 | ^codecov\.yml$ 17 | ^CRAN-RELEASE$ 18 | ^rsconnect$ 19 | ^inst/plumber/chicago-rpart/rsconnect$ 20 | ^inst/plumber/netflix-descriptions/rsconnect$ 21 | ^inst/rmarkdown/templates/vetiver_dashboard/skeleton/rsconnect$ 22 | ^CRAN-SUBMISSION$ 23 | ^.Renviron$ 24 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to vetiver 2 | 3 | This outlines how to propose a change to vetiver. 4 | For more detailed info about contributing to this, and other tidyverse packages, please see the 5 | [**development contributing guide**](https://rstd.io/tidy-contrib). 6 | 7 | ## Fixing typos 8 | 9 | You can fix typos, spelling mistakes, or grammatical errors in the documentation directly using the GitHub web interface, as long as the changes are made in the _source_ file. 10 | This generally means you'll need to edit [roxygen2 comments](https://roxygen2.r-lib.org/articles/roxygen2.html) in an `.R`, not a `.Rd` file. 11 | You can find the `.R` file that generates the `.Rd` by reading the comment in the first line. 12 | 13 | ## Bigger changes 14 | 15 | If you want to make a bigger change, it's a good idea to first file an issue and make sure someone from the team agrees that it’s needed. 16 | If you’ve found a bug, please file an issue that illustrates the bug with a minimal 17 | [reprex](https://www.tidyverse.org/help/#reprex) (this will also help you write a unit test, if needed). 18 | 19 | ### Pull request process 20 | 21 | * Fork the package and clone onto your computer. If you haven't done this before, we recommend using `usethis::create_from_github("rstudio/vetiver-r", fork = TRUE)`. 22 | 23 | * Install all development dependences with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 24 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 25 | * Create a Git branch for your pull request (PR). We recommend using `usethis::pr_init("brief-description-of-change")`. 26 | 27 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 28 | The title of your PR should briefly describe the change. 29 | The body of your PR should contain `Fixes #issue-number`. 30 | 31 | * For user-facing changes, add a bullet to the top of `NEWS.md` (i.e. just below the first header). Follow the style described in . 32 | 33 | ### Code style 34 | 35 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 36 | You can use the [styler](https://CRAN.R-project.org/package=styler) package to apply these styles, but please don't restyle code that has nothing to do with your PR. 37 | 38 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/rd-formatting.html), for documentation. 39 | 40 | * We use [testthat](https://cran.r-project.org/package=testthat) for unit tests. 41 | Contributions with test cases included are easier to accept. 42 | 43 | ## Code of Conduct 44 | 45 | Please note that the modelops project is released with a 46 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 47 | project you agree to abide by its terms. 48 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check-hard.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow only directly installs "hard" dependencies, i.e. Depends, 5 | # Imports, and LinkingTo dependencies. Notably, Suggests dependencies are never 6 | # installed, with the exception of testthat, knitr, and rmarkdown. The cache is 7 | # never used to avoid accidentally restoring a cache containing a suggested 8 | # dependency. 9 | on: 10 | push: 11 | branches: [main] 12 | pull_request: 13 | branches: [main] 14 | 15 | name: R-CMD-check-hard 16 | 17 | jobs: 18 | R-CMD-check: 19 | runs-on: ${{ matrix.config.os }} 20 | 21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | config: 27 | - {os: ubuntu-latest, r: 'release'} 28 | 29 | env: 30 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 31 | R_KEEP_PKG_SOURCE: yes 32 | 33 | steps: 34 | - uses: actions/checkout@v3 35 | 36 | - uses: r-lib/actions/setup-pandoc@v2 37 | 38 | - uses: r-lib/actions/setup-r@v2 39 | with: 40 | r-version: ${{ matrix.config.r }} 41 | http-user-agent: ${{ matrix.config.http-user-agent }} 42 | use-public-rspm: true 43 | 44 | - uses: r-lib/actions/setup-r-dependencies@v2 45 | with: 46 | dependencies: '"hard"' 47 | cache: false 48 | extra-packages: | 49 | any::rcmdcheck 50 | any::testthat 51 | any::knitr 52 | any::rmarkdown 53 | needs: check 54 | 55 | - uses: r-lib/actions/check-r-package@v2 56 | with: 57 | upload-snapshots: true 58 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check 14 | 15 | jobs: 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | - {os: macos-latest, r: 'release'} 26 | 27 | - {os: windows-latest, r: 'release'} 28 | 29 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 30 | - {os: ubuntu-latest, r: 'release'} 31 | - {os: ubuntu-latest, r: 'oldrel-1'} 32 | - {os: ubuntu-latest, r: 'oldrel-2'} 33 | 34 | env: 35 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 36 | R_KEEP_PKG_SOURCE: yes 37 | TORCH_INSTALL: 1 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | 42 | - uses: r-lib/actions/setup-pandoc@v2 43 | 44 | - uses: r-lib/actions/setup-r@v2 45 | with: 46 | r-version: ${{ matrix.config.r }} 47 | http-user-agent: ${{ matrix.config.http-user-agent }} 48 | use-public-rspm: true 49 | 50 | - uses: r-lib/actions/setup-r-dependencies@v2 51 | with: 52 | extra-packages: 53 | any::rcmdcheck, 54 | any::cpp11, 55 | ranger=?ignore-before-r=4.1.0 56 | needs: check 57 | 58 | - name: Install Miniconda & Tensorflow 59 | # conda can fail at downgrading python, so we specify python version in advance 60 | env: 61 | RETICULATE_MINICONDA_PYTHON_VERSION: "3.10" 62 | run: | 63 | reticulate::install_miniconda() # creates r-reticulate conda env by default 64 | tensorflow::install_tensorflow(version="2.15", conda_python_version = NULL) 65 | shell: Rscript {0} 66 | 67 | - uses: r-lib/actions/check-r-package@v2 68 | with: 69 | error-on: '"note"' 70 | upload-snapshots: true 71 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - uses: r-lib/actions/setup-pandoc@v2 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | with: 31 | use-public-rspm: true 32 | 33 | - uses: r-lib/actions/setup-r-dependencies@v2 34 | with: 35 | extra-packages: any::pkgdown, local::. 36 | needs: website 37 | 38 | - name: Build site 39 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 40 | shell: Rscript {0} 41 | 42 | - name: Deploy to GitHub pages 🚀 43 | if: github.event_name != 'pull_request' 44 | uses: JamesIves/github-pages-deploy-action@v4.4.1 45 | with: 46 | clean: false 47 | branch: gh-pages 48 | folder: docs 49 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | jobs: 10 | document: 11 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 12 | name: document 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: r-lib/actions/pr-fetch@v2 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | extra-packages: any::roxygen2 30 | needs: pr-document 31 | 32 | - name: Document 33 | run: roxygen2::roxygenise() 34 | shell: Rscript {0} 35 | 36 | - name: commit 37 | run: | 38 | git config --local user.name "$GITHUB_ACTOR" 39 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 40 | git add man/\* NAMESPACE 41 | git commit -m 'Document' 42 | 43 | - uses: r-lib/actions/pr-push@v2 44 | with: 45 | repo-token: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | style: 48 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 49 | name: style 50 | runs-on: ubuntu-latest 51 | env: 52 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 53 | steps: 54 | - uses: actions/checkout@v3 55 | 56 | - uses: r-lib/actions/pr-fetch@v2 57 | with: 58 | repo-token: ${{ secrets.GITHUB_TOKEN }} 59 | 60 | - uses: r-lib/actions/setup-r@v2 61 | 62 | - name: Install dependencies 63 | run: install.packages("styler") 64 | shell: Rscript {0} 65 | 66 | - name: Style 67 | run: styler::style_pkg() 68 | shell: Rscript {0} 69 | 70 | - name: commit 71 | run: | 72 | git config --local user.name "$GITHUB_ACTOR" 73 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 74 | git add \*.R 75 | git commit -m 'Style' 76 | 77 | - uses: r-lib/actions/pr-push@v2 78 | with: 79 | repo-token: ${{ secrets.GITHUB_TOKEN }} 80 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | TORCH_INSTALL: 1 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - uses: r-lib/actions/setup-r-dependencies@v2 26 | with: 27 | extra-packages: any::covr, any::cpp11 28 | needs: coverage 29 | 30 | - name: Test coverage 31 | run: | 32 | covr::codecov( 33 | quiet = FALSE, 34 | clean = FALSE, 35 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 36 | ) 37 | shell: Rscript {0} 38 | 39 | - name: Show testthat output 40 | if: always() 41 | run: | 42 | ## -------------------------------------------------------------------- 43 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 44 | shell: bash 45 | 46 | - name: Upload test results 47 | if: failure() 48 | uses: actions/upload-artifact@v3 49 | with: 50 | name: coverage-test-failures 51 | path: ${{ runner.temp }}/package 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .Renviron 6 | *dcf 7 | docs 8 | inst/doc 9 | *html 10 | pkgdown/* 11 | vetiver_renv.lock 12 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: vetiver 2 | Title: Version, Share, Deploy, and Monitor Models 3 | Version: 0.2.5.9000 4 | Authors@R: c( 5 | person("Julia", "Silge", , "julia.silge@posit.co", role = c("cre", "aut"), 6 | comment = c(ORCID = "0000-0002-3671-836X")), 7 | person("Posit Software, PBC", role = c("cph", "fnd")) 8 | ) 9 | Description: The goal of 'vetiver' is to provide fluent tooling to 10 | version, share, deploy, and monitor a trained model. Functions handle 11 | both recording and checking the model's input data prototype, and 12 | predicting from a remote API endpoint. The 'vetiver' package is 13 | extensible, with generics that can support many kinds of models. 14 | License: MIT + file LICENSE 15 | URL: https://vetiver.rstudio.com, https://rstudio.github.io/vetiver-r/, 16 | https://github.com/rstudio/vetiver-r/ 17 | BugReports: https://github.com/rstudio/vetiver-r/issues 18 | Depends: 19 | R (>= 3.6) 20 | Imports: 21 | bundle, 22 | butcher (>= 0.3.1), 23 | cereal, 24 | cli, 25 | fs, 26 | generics, 27 | glue, 28 | hardhat, 29 | lifecycle, 30 | magrittr (>= 2.0.3), 31 | pins (>= 1.3.0), 32 | purrr, 33 | rapidoc, 34 | readr (>= 1.4.0), 35 | rlang (>= 1.1.0), 36 | tibble, 37 | vctrs, 38 | withr 39 | Suggests: 40 | arrow, 41 | callr, 42 | caret, 43 | clustMixType, 44 | covr, 45 | curl, 46 | dplyr, 47 | flexdashboard, 48 | ggplot2, 49 | httpuv, 50 | httr, 51 | jsonlite, 52 | keras, 53 | knitr, 54 | LiblineaR, 55 | luz, 56 | mgcv, 57 | mlr3 (>= 0.17.0), 58 | mlr3data, 59 | mlr3learners, 60 | mockery, 61 | modeldata, 62 | parsnip, 63 | paws.machine.learning (>= 0.2.0), 64 | pingr, 65 | plotly, 66 | plumber (>= 1.0.0), 67 | ranger, 68 | recipes (>= 1.1.0), 69 | reticulate, 70 | rmarkdown, 71 | rpart, 72 | rsconnect, 73 | slider (>= 0.2.2), 74 | smdocker (>= 0.1.2), 75 | stacks, 76 | tensorflow, 77 | testthat (>= 3.1.8), 78 | tidyselect, 79 | torch, 80 | vdiffr, 81 | workflows, 82 | xgboost, 83 | yardstick 84 | VignetteBuilder: 85 | knitr 86 | Config/Needs/website: tidyverse/tidytemplate 87 | Config/testthat/edition: 3 88 | Encoding: UTF-8 89 | Roxygen: list(markdown = TRUE) 90 | RoxygenNote: 7.3.1 91 | Remotes: 92 | rstudio/reticulate 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: vetiver authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 vetiver authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /R/attach-pkgs.R: -------------------------------------------------------------------------------- 1 | #' Fully attach or load packages for making model predictions 2 | #' 3 | #' These are developer-facing functions, useful for supporting new model types. 4 | #' Some models require one or more R packages to be fully attached to make 5 | #' predictions, and some require only that the namespace of one or more R 6 | #' packages is loaded. 7 | #' 8 | #' @details These two functions will attempt either to: 9 | #' 10 | #' - fully attach or 11 | #' - load 12 | #' 13 | #' the namespace of the `pkgs` vector of package names, preserving the current 14 | #' random seed. 15 | #' 16 | #' To learn more about load vs. attach, read the ["Dependencies" chapter of 17 | #' *R Packages*](https://r-pkgs.org/dependencies-mindset-background.html#sec-dependencies-attach-vs-load). 18 | #' For deploying a model, it is likely safer to fully attach needed packages 19 | #' but that comes with the risk of naming conflicts between packages. 20 | #' 21 | #' @param pkgs A character vector of package names to load or fully attach. 22 | #' 23 | #' @return An invisible `TRUE`. 24 | #' @family namespace 25 | #' @export 26 | #' 27 | #' @examples 28 | #' ## succeed 29 | #' load_pkgs(c("knitr", "readr")) 30 | #' attach_pkgs(c("knitr", "readr")) 31 | #' 32 | #' ## fail 33 | #' try(load_pkgs(c("bloopy", "readr"))) 34 | #' try(attach_pkgs(c("bloopy", "readr"))) 35 | #' 36 | attach_pkgs <- function(pkgs) { 37 | attached <- paste0("package:", pkgs) %in% search() 38 | pkgs <- pkgs[!attached] 39 | namespace_handling(pkgs, attachNamespace, "Package(s) could not be attached:") 40 | } 41 | 42 | #' @export 43 | #' @rdname attach_pkgs 44 | load_pkgs <- function(pkgs) { 45 | loaded <- map_lgl(pkgs, isNamespaceLoaded) 46 | pkgs <- pkgs[!loaded] 47 | namespace_handling(pkgs, loadNamespace, "Namespace(s) could not be loaded:") 48 | } 49 | 50 | namespace_handling <- function(pkgs, func, error_msg, call = rlang::caller_env()) { 51 | safe_load <- safely(withr::with_preserve_seed(func)) 52 | did_load <- map(pkgs, safe_load) 53 | bad <- compact(map(did_load, "error")) 54 | bad <- map_chr(bad, "package") 55 | if (length(bad) >= 1) { 56 | abort(c(error_msg, bad), call = call) 57 | } 58 | 59 | invisible(TRUE) 60 | } 61 | -------------------------------------------------------------------------------- /R/caret.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.train <- function(model) { 4 | glue("A {tolower(model$modelInfo$label)} {tolower(model$modelType)} model") 5 | } 6 | 7 | #' @rdname vetiver_create_meta 8 | #' @export 9 | vetiver_create_meta.train <- function(model, metadata) { 10 | reqs <- c("caret", model$modelInfo$library) 11 | reqs <- sort(unique(reqs)) 12 | vetiver_meta(metadata, required_pkgs = reqs) 13 | } 14 | 15 | #' @rdname vetiver_create_ptype 16 | #' @export 17 | vetiver_ptype.train <- function(model, ...) { 18 | tibble::as_tibble(model$ptype) 19 | } 20 | 21 | #' @rdname vetiver_create_description 22 | #' @export 23 | vetiver_prepare_model.train <- function(model) { 24 | ret <- butcher::butcher(model) 25 | ret <- bundle::bundle(ret) 26 | ret 27 | } 28 | 29 | #' @rdname handler_startup 30 | #' @export 31 | handler_startup.train <- function(vetiver_model) { 32 | attach_pkgs(vetiver_model$metadata$required_pkgs) 33 | } 34 | 35 | #' @rdname handler_startup 36 | #' @export 37 | handler_predict.train <- function(vetiver_model, ...) { 38 | 39 | function(req) { 40 | newdata <- req$body 41 | newdata <- vetiver_type_convert(newdata, vetiver_model$prototype) 42 | predict(vetiver_model$model, newdata = newdata, ...) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /R/endpoint.R: -------------------------------------------------------------------------------- 1 | #' Post new data to a deployed model API endpoint and return predictions 2 | #' 3 | #' @param object A model API endpoint object created with [vetiver_endpoint()]. 4 | #' @param new_data New data for making predictions, such as a data frame. 5 | #' @param ... Extra arguments passed to [httr::POST()] 6 | #' 7 | #' @return A tibble of model predictions with as many rows as in `new_data`. 8 | #' @seealso [augment.vetiver_endpoint()] 9 | #' @export 10 | #' 11 | #' @examples 12 | #' 13 | #' if (FALSE) { 14 | #' endpoint <- vetiver_endpoint("http://127.0.0.1:8088/predict") 15 | #' predict(endpoint, mtcars[4:7, -1]) 16 | #' } 17 | #' 18 | #' 19 | predict.vetiver_endpoint <- function(object, new_data, ...) { 20 | rlang::check_installed(c("jsonlite", "httr")) 21 | check_dots_used() 22 | data_json <- jsonlite::toJSON(new_data, na = "string") 23 | ret <- httr::POST(object$url, ..., body = data_json) 24 | resp <- httr::content(ret, "text", encoding = "UTF-8") 25 | resp <- jsonlite::fromJSON(resp) 26 | 27 | if (httr::status_code(ret) >= 300) { 28 | if (has_name(resp, "message")) { 29 | abort(glue("Failed to predict: {resp$message}")) 30 | } else { 31 | status <- httr::http_status(ret) 32 | abort(c("Failed to predict.", status$message)) 33 | } 34 | } 35 | 36 | tibble::as_tibble(resp) 37 | } 38 | 39 | #' Post new data to a deployed model API endpoint and augment with predictions 40 | #' 41 | #' @param x A model API endpoint object created with [vetiver_endpoint()]. 42 | #' @inheritParams predict.vetiver_endpoint 43 | #' 44 | #' @return The `new_data` with added prediction column(s). 45 | #' @seealso [predict.vetiver_endpoint()] 46 | #' @export 47 | #' 48 | #' @examples 49 | #' 50 | #' if (FALSE) { 51 | #' endpoint <- vetiver_endpoint("http://127.0.0.1:8088/predict") 52 | #' augment(endpoint, mtcars[4:7, -1]) 53 | #' } 54 | #' 55 | augment.vetiver_endpoint <- function(x, new_data, ...) { 56 | preds <- predict(x, new_data = new_data, ...) 57 | vctrs::vec_cbind(tibble::as_tibble(new_data), preds) 58 | } 59 | 60 | 61 | #' Create a model API endpoint object for prediction 62 | #' 63 | #' This function creates a model API endpoint for prediction from a URL. No 64 | #' HTTP calls are made until you actually 65 | #' [`predict()`][predict.vetiver_endpoint()] with your endpoint. 66 | #' 67 | #' @param url An API endpoint URL 68 | #' @return A new `vetiver_endpoint` object 69 | #' 70 | #' @examples 71 | #' vetiver_endpoint("https://colorado.rstudio.com/rsc/seattle-housing/predict") 72 | #' 73 | #' @export 74 | vetiver_endpoint <- function(url) { 75 | url <- as.character(url) 76 | url <- gsub("/$", "", url) 77 | new_vetiver_endpoint(url) 78 | } 79 | 80 | new_vetiver_endpoint <- function(url = character()) { 81 | stopifnot(is.character(url)) 82 | structure(list(url = url), class = "vetiver_endpoint") 83 | } 84 | 85 | #' @export 86 | format.vetiver_endpoint <- function(x, ...) { 87 | cli::cli_format_method({ 88 | cli::cli_h3("A model API endpoint for prediction:") 89 | cli::cli_text("{x$url}") 90 | }) 91 | } 92 | 93 | #' @export 94 | print.vetiver_endpoint <- function(x, ...) { 95 | cat(format(x), sep = "\n") 96 | invisible(x) 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /R/gam.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.gam <- function(model) { 4 | glue("A generalized additive model ({model$family$family} family, {model$family$link} link)") 5 | } 6 | 7 | #' @rdname vetiver_create_description 8 | #' @export 9 | vetiver_prepare_model.gam <- function(model) { 10 | butcher::butcher(model) 11 | } 12 | 13 | #' @rdname vetiver_create_meta 14 | #' @export 15 | vetiver_create_meta.gam <- function(model, metadata) { 16 | vetiver_meta(metadata, required_pkgs = "mgcv") 17 | } 18 | 19 | #' @rdname vetiver_create_ptype 20 | #' @export 21 | vetiver_ptype.gam <- function(model, ...) { 22 | vetiver_ptype.lm(model, ...) 23 | } 24 | 25 | #' @rdname handler_startup 26 | #' @export 27 | handler_startup.gam <- function(vetiver_model) { 28 | attach_pkgs(vetiver_model$metadata$required_pkgs) 29 | } 30 | 31 | #' @rdname handler_startup 32 | #' @export 33 | handler_predict.gam <- function(vetiver_model, ...) { 34 | handler_predict.lm(vetiver_model, ...) 35 | } 36 | -------------------------------------------------------------------------------- /R/glm.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.glm <- function(model) { 4 | glue("A generalized linear model ({model$family$family} family, {model$family$link} link)") 5 | } 6 | 7 | #' @rdname vetiver_create_description 8 | #' @export 9 | vetiver_prepare_model.glm <- function(model) { 10 | butcher::butcher(model) 11 | } 12 | 13 | #' @rdname vetiver_create_ptype 14 | #' @export 15 | vetiver_ptype.glm <- function(model, ...) { 16 | vetiver_ptype.lm(model, ...) 17 | } 18 | 19 | #' @rdname handler_startup 20 | #' @export 21 | handler_predict.glm <- function(vetiver_model, ...) { 22 | handler_predict.lm(vetiver_model, ...) 23 | } 24 | -------------------------------------------------------------------------------- /R/handlers.R: -------------------------------------------------------------------------------- 1 | #' Model handler functions for API endpoint 2 | #' 3 | #' These are developer-facing functions, useful for supporting new model types. 4 | #' Each model supported by `vetiver_model()` uses two handler functions 5 | #' in [vetiver_api()]: 6 | #' - The `handler_startup` function executes when the API starts. Use this 7 | #' function for tasks like loading packages. A model can use the default 8 | #' method here, which is `NULL` (to do nothing at startup). 9 | #' - The `handler_predict` function executes at each API call. Use this 10 | #' function for calling `predict()` and any other tasks that must be executed 11 | #' at each API call. 12 | #' 13 | #' @details These are two generics that use the class of `vetiver_model$model` 14 | #' for dispatch. 15 | #' 16 | #' @inheritParams vetiver_api 17 | #' 18 | #' @examples 19 | #' 20 | #' cars_lm <- lm(mpg ~ ., data = mtcars) 21 | #' v <- vetiver_model(cars_lm, "cars_linear") 22 | #' handler_startup(v) 23 | #' handler_predict(v) 24 | #' 25 | #' @return A `handler_startup` function should return invisibly, while a 26 | #' `handler_predict` function should return a function with the signature 27 | #' `function(req)`. The request body (`req$body`) consists of the new data 28 | #' at prediction time; this function should return predictions either as a 29 | #' tibble or as a list coercable to a tibble via [tibble::as_tibble()]. 30 | #' @rdname handler_startup 31 | #' @export 32 | handler_startup <- function(vetiver_model) 33 | UseMethod("handler_startup", vetiver_model$model) 34 | 35 | #' @rdname handler_startup 36 | #' @export 37 | handler_startup.default <- function(vetiver_model) invisible(NULL) 38 | 39 | #' @rdname handler_startup 40 | #' @export 41 | handler_predict <- function(vetiver_model, ...) 42 | UseMethod("handler_predict", vetiver_model$model) 43 | 44 | #' @rdname handler_startup 45 | #' @export 46 | handler_predict.default <- function(vetiver_model, ...) 47 | abort("There is no method available to build a prediction handler for `x`.") 48 | 49 | #' Convert new data at prediction time using input data prototype 50 | #' 51 | #' This is a developer-facing function, useful for supporting new model types. 52 | #' At prediction time, new observations typically must be checked and sometimes 53 | #' converted to the data types from training time. 54 | #' 55 | #' @examples 56 | #' 57 | #' library(tibble) 58 | #' training_df <- tibble(x = as.Date("2021-01-01") + 0:9, 59 | #' y = LETTERS[1:10], z = letters[11:20]) 60 | #' training_df 61 | #' 62 | #' prototype <- vctrs::vec_slice(training_df, 0) 63 | #' vetiver_type_convert(tibble(x = "2021-02-01", y = "J", z = "k"), prototype) 64 | #' 65 | #' ## unsuccessful conversion generates an error: 66 | #' try(vetiver_type_convert(tibble(x = "potato", y = "J", z = "k"), prototype)) 67 | #' 68 | #' ## error for missing column: 69 | #' try(vetiver_type_convert(tibble(x = "potato", y = "J"), prototype)) 70 | #' 71 | #' @inheritParams predict.vetiver_endpoint 72 | #' @param ptype An input data prototype, such as a 0-row slice of the training 73 | #' data 74 | #' @return A converted dataframe 75 | #' @export 76 | vetiver_type_convert <- function(new_data, ptype) { 77 | if (is.null(ptype)) { 78 | return(new_data) 79 | } 80 | new_data <- hardhat::validate_column_names(new_data, colnames(ptype)) 81 | spec <- readr::as.col_spec(ptype) 82 | is_character <- vapply(new_data, is.character, logical(1)) 83 | if (any(is_character)) { 84 | new_data <- type_convert_strict(new_data, col_types = spec) 85 | } 86 | new_data 87 | } 88 | 89 | type_convert_strict <- function(new_data, col_types, call = rlang::caller_env()) { 90 | warn_to_error <- function(e) { 91 | abort(conditionMessage(e), call = call) 92 | } 93 | 94 | tryCatch( 95 | warning = function(e) warn_to_error(e), 96 | expr = readr::type_convert(new_data, col_types = col_types) 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /R/keras.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.keras.engine.training.Model <- function(model) { 4 | model_config <- model$get_config() 5 | glue("A {model_config$name} keras model with {length(model_config$layers)} layers") 6 | } 7 | 8 | #' @rdname vetiver_create_meta 9 | #' @export 10 | vetiver_create_meta.keras.engine.training.Model <- function(model, metadata) { 11 | vetiver_meta(metadata, required_pkgs = "keras") 12 | } 13 | 14 | #' @rdname vetiver_create_ptype 15 | #' @export 16 | vetiver_ptype.keras.engine.training.Model <- function(model, ...) { 17 | if (length(model$inputs) > 1) { 18 | abort(c( 19 | "There is currently no support in vetiver for multi-input keras models.", 20 | i = "Consider creating a custom handler." 21 | )) 22 | } 23 | rlang::check_dots_used() 24 | dots <- list(...) 25 | check_ptype_data(dots) 26 | ptype <- vctrs::vec_ptype(dots$prototype_data) 27 | tibble::as_tibble(ptype) 28 | } 29 | 30 | #' @rdname vetiver_create_description 31 | #' @export 32 | vetiver_prepare_model.keras.engine.training.Model <- function(model) { 33 | bundle::bundle(model) 34 | } 35 | 36 | #' @rdname handler_startup 37 | #' @export 38 | handler_startup.keras.engine.training.Model <- function(vetiver_model) { 39 | attach_pkgs(vetiver_model$metadata$required_pkgs) 40 | } 41 | 42 | #' @rdname handler_startup 43 | #' @export 44 | handler_predict.keras.engine.training.Model <- function(vetiver_model, ...) { 45 | 46 | dtype <- vetiver_model$model$input$dtype$name 47 | shape <- dim(vetiver_model$model$input) 48 | 49 | function(req) { 50 | new_data <- vetiver_type_convert(req$body, vetiver_model$ptype) 51 | new_data <- tensorflow::as_tensor( 52 | as.matrix(new_data), 53 | dtype = dtype, 54 | shape = shape 55 | ) 56 | predict(vetiver_model$model, x = new_data, ...) 57 | } 58 | 59 | } 60 | 61 | #' @rdname vetiver_python_requirements 62 | #' @export 63 | vetiver_python_requirements.keras.engine.training.Model <- function(model) { 64 | ## TODO: something like pip freeze for keras and tensorflow to get versions 65 | ## Also maybe protobuf because very picky wrt tensorflow 66 | system.file("requirements/keras-requirements.txt", package = "vetiver") 67 | } 68 | -------------------------------------------------------------------------------- /R/kproto.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.kproto <- function(model) { 4 | glue("A k-prototypes clustering model ({length(model$size)} clusters)") 5 | } 6 | 7 | #' @rdname vetiver_create_meta 8 | #' @export 9 | vetiver_create_meta.kproto <- function(model, metadata) { 10 | vetiver_meta(metadata, required_pkgs = "clustMixType") 11 | } 12 | 13 | #' @rdname vetiver_create_description 14 | #' @export 15 | vetiver_prepare_model.kproto <- function(model) { 16 | butcher::butcher(model) 17 | } 18 | 19 | #' @rdname vetiver_create_ptype 20 | #' @export 21 | vetiver_ptype.kproto <- function(model, ...) { 22 | prototype <- vctrs::vec_ptype(model$data) 23 | tibble::as_tibble(prototype) 24 | } 25 | 26 | #' @rdname handler_startup 27 | #' @export 28 | handler_predict.kproto <- function(vetiver_model, ...) { 29 | 30 | prototype <- vetiver_model$prototype 31 | 32 | function(req) { 33 | newdata <- req$body 34 | if (!is_null(prototype)) { 35 | newdata <- vetiver_type_convert(newdata, prototype) 36 | newdata <- hardhat::scream(newdata, prototype) 37 | } 38 | newdata <- as.data.frame(newdata) 39 | # clustMixType:::predict.kproto() 40 | ret <- predict(vetiver_model$model, newdata = newdata, ...) 41 | list(.pred = ret$cluster) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /R/lm.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.lm <- function(model) { 4 | "An OLS linear regression model" 5 | } 6 | 7 | #' @rdname vetiver_create_description 8 | #' @export 9 | vetiver_prepare_model.lm <- function(model) { 10 | butcher::butcher(model) 11 | } 12 | 13 | #' @rdname vetiver_create_ptype 14 | #' @export 15 | vetiver_ptype.lm <- function(model, ...) { 16 | pred_names <- preds_lm_ish(model) 17 | prototype <- vctrs::vec_ptype(model$model[pred_names]) 18 | tibble::as_tibble(prototype) 19 | } 20 | 21 | #' @rdname handler_startup 22 | #' @export 23 | handler_predict.lm <- function(vetiver_model, ...) { 24 | 25 | ptype <- vetiver_model$prototype 26 | 27 | function(req) { 28 | newdata <- req$body 29 | if (!is_null(ptype)) { 30 | newdata <- vetiver_type_convert(newdata, ptype) 31 | newdata <- hardhat::scream(newdata, ptype) 32 | } 33 | ret <- predict(vetiver_model$model, newdata = newdata, ...) 34 | list(.pred = ret) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /R/luz.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.luz_module_fitted <- function(model) { 4 | n_parameters <- lapply(model$model$parameters, function(x) prod(x$shape)) 5 | n_parameters <- do.call(sum, n_parameters) 6 | n_parameters <- formatC(n_parameters, big.mark = ",", format = "d") 7 | glue("A luz module with {n_parameters} parameters") 8 | } 9 | 10 | #' @rdname vetiver_create_meta 11 | #' @export 12 | vetiver_create_meta.luz_module_fitted <- function(model, metadata) { 13 | pkgs <- c("luz", "torch", model$model$required_pkgs) 14 | vetiver_meta(metadata, required_pkgs = pkgs) 15 | } 16 | 17 | #' @rdname vetiver_create_ptype 18 | #' @export 19 | vetiver_ptype.luz_module_fitted <- function(model, ...) { 20 | rlang::check_dots_used() 21 | dots <- list(...) 22 | check_ptype_data(dots) 23 | ptype <- vctrs::vec_ptype(dots$prototype_data) 24 | tibble::as_tibble(ptype) 25 | } 26 | 27 | #' @rdname vetiver_create_description 28 | #' @export 29 | vetiver_prepare_model.luz_module_fitted <- function(model) { 30 | bundle::bundle(model) 31 | } 32 | 33 | #' @rdname handler_startup 34 | #' @export 35 | handler_startup.luz_module_fitted <- function(vetiver_model) { 36 | attach_pkgs(vetiver_model$metadata$required_pkgs) 37 | } 38 | 39 | #' @rdname handler_startup 40 | #' @export 41 | handler_predict.luz_module_fitted <- function(vetiver_model, ...) { 42 | force(vetiver_model) 43 | function(req) { 44 | new_data <- vetiver_type_convert(req$body, vetiver_model$prototype) 45 | new_data <- if (is.data.frame(new_data)) as.matrix(new_data) else new_data 46 | preds <- tensors_to_array(predict(vetiver_model$model, new_data)) 47 | tibble::tibble(preds) 48 | } 49 | } 50 | 51 | tensors_to_array <- function(x) { 52 | if (is.list(x)) { 53 | lapply(x, tensors_to_array) 54 | } else if (inherits(x, "torch_tensor")) { 55 | as.array(x$cpu()) 56 | } else { 57 | x 58 | } 59 | } 60 | 61 | #' @rdname vetiver_python_requirements 62 | #' @export 63 | vetiver_renviron_requirements.luz_module_fitted <- function(model) { 64 | system.file("requirements/luz-renviron.txt", package = "vetiver") 65 | } 66 | -------------------------------------------------------------------------------- /R/meta.R: -------------------------------------------------------------------------------- 1 | #' Metadata constructors for `vetiver_model()` object 2 | #' 3 | #' These are developer-facing functions, useful for supporting new model types. 4 | #' The metadata stored in a [vetiver_model()] object has four elements: 5 | #' - `$user`, the metadata supplied by the user 6 | #' - `$version`, the version of the pin (which can be `NULL` before pinning) 7 | #' - `$url`, the URL where the pin is located, if any 8 | #' - `$required_pkgs`, a character string of R packages required for prediction 9 | #' 10 | #' @inheritParams vetiver_model 11 | #' @param user Metadata supplied by the user 12 | #' @param version Version of the pin 13 | #' @param url URL for the pin, if any 14 | #' @param required_pkgs Character string of R packages required for prediction 15 | #' 16 | #' @return The `vetiver_meta()` constructor returns a list. The 17 | #' `vetiver_create_meta` function returns a `vetiver_meta()` list. 18 | #' 19 | #' @examples 20 | #' vetiver_meta() 21 | #' 22 | #' cars_lm <- lm(mpg ~ ., data = mtcars) 23 | #' vetiver_create_meta(cars_lm, list()) 24 | #' 25 | #' @rdname vetiver_create_meta 26 | #' @export 27 | vetiver_meta <- function(user = list(), version = NULL, 28 | url = NULL, required_pkgs = NULL) { 29 | list(user = user, version = version, 30 | url = url, required_pkgs = required_pkgs) 31 | } 32 | 33 | 34 | #' @rdname vetiver_create_meta 35 | #' @export 36 | vetiver_create_meta <- function(model, metadata) { 37 | UseMethod("vetiver_create_meta") 38 | } 39 | 40 | #' @rdname vetiver_create_meta 41 | #' @export 42 | vetiver_create_meta.default <- function(model, metadata) { 43 | vetiver_meta(metadata) 44 | } 45 | -------------------------------------------------------------------------------- /R/mlr3.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.Learner <- function(model) { 4 | glue("A mlr3 {model$id} learner") 5 | } 6 | 7 | #' @rdname vetiver_create_meta 8 | #' @export 9 | vetiver_create_meta.Learner <- function(model, metadata) { 10 | reqs <- model$packages 11 | reqs <- sort(unique(c(reqs, "mlr3"))) 12 | vetiver_meta(metadata, required_pkgs = reqs) 13 | } 14 | 15 | #' @rdname vetiver_create_ptype 16 | #' @export 17 | vetiver_ptype.Learner <- function(model, ...) { 18 | tibble::as_tibble(model$state$data_prototype)[, model$state$train_task$feature_names] 19 | } 20 | 21 | #' @rdname vetiver_create_description 22 | #' @export 23 | vetiver_prepare_model.Learner <- function(model) { 24 | if (is.null(model$state)) { 25 | rlang::abort("Your `model` object is not a trained learner.") 26 | } 27 | model 28 | } 29 | 30 | #' @rdname handler_startup 31 | #' @export 32 | handler_startup.Learner <- function(vetiver_model) { 33 | attach_pkgs(vetiver_model$metadata$required_pkgs) 34 | } 35 | 36 | #' @rdname handler_startup 37 | #' @export 38 | handler_predict.Learner <- function(vetiver_model, ...) { 39 | function(req) { 40 | new_data <- req$body 41 | new_data <- vetiver_type_convert(new_data, vetiver_model$prototype) 42 | pred <- vetiver_model$model$predict_newdata(newdata = new_data) 43 | stats::setNames(list(pred$response), vetiver_model$model$state$train_task$target_names) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /R/prepare.R: -------------------------------------------------------------------------------- 1 | #' Model constructor methods 2 | #' 3 | #' These are developer-facing functions, useful for supporting new model types. 4 | #' Each model supported by [`vetiver_model()`] uses up to four methods when the 5 | #' deployable object is created: 6 | #' - The `vetiver_create_description()` function generates a helpful description 7 | #' of the model based on its characteristics. This method is required. 8 | #' - The [vetiver_create_meta()] function creates the correct [vetiver_meta()] 9 | #' for the model. This is especially helpful for specifying which packages are 10 | #' needed for prediction. A model can use the default method here, which is 11 | #' to have no special metadata. 12 | #' - The [vetiver_ptype()] function finds an input data prototype from the 13 | #' training data (a zero-row slice) to use for checking at prediction time. 14 | #' This method is required. 15 | #' - The `vetiver_prepare_model()` function executes last. Use this function 16 | #' for tasks like checking if the model is trained and reducing the size of the 17 | #' model via [butcher::butcher()]. A model can use the default method here, 18 | #' which is to return the model without changes. 19 | #' 20 | #' @inheritParams vetiver_model 21 | #' @details These are four generics that use the class of `model` for dispatch. 22 | #' 23 | #' @examples 24 | #' 25 | #' cars_lm <- lm(mpg ~ ., data = mtcars) 26 | #' vetiver_create_description(cars_lm) 27 | #' vetiver_prepare_model(cars_lm) 28 | #' 29 | #' @rdname vetiver_create_description 30 | #' @export 31 | vetiver_create_description <- function(model) { 32 | UseMethod("vetiver_create_description") 33 | } 34 | 35 | #' @rdname vetiver_create_description 36 | #' @export 37 | vetiver_create_description.default <- function(model) { 38 | abort("There is no method available to create a description for `model`.") 39 | } 40 | 41 | #' @rdname vetiver_create_description 42 | #' @export 43 | vetiver_prepare_model <- function(model) { 44 | UseMethod("vetiver_prepare_model") 45 | } 46 | 47 | #' @rdname vetiver_create_description 48 | #' @export 49 | vetiver_prepare_model.default <- function(model) { 50 | model 51 | } 52 | -------------------------------------------------------------------------------- /R/prototype.R: -------------------------------------------------------------------------------- 1 | #' Create a vetiver input data prototype 2 | #' 3 | #' Optionally find and return an input data prototype for a model. 4 | #' 5 | #' @details 6 | #' These are developer-facing functions, useful for supporting new model types. 7 | #' A [vetiver_model()] object optionally stores an input data prototype for 8 | #' checking at prediction time. 9 | #' 10 | #' - The default for `save_prototype`, `TRUE`, finds an input data prototype (a 11 | #' zero-row slice of the training data) via [vetiver_ptype()]. 12 | #' - `save_prototype = FALSE` opts out of storing any input data prototype. 13 | #' - You may pass your own data to `save_prototype`, but be sure to check that it 14 | #' has the same structure as your training data, perhaps with 15 | #' [hardhat::scream()]. 16 | #' 17 | #' @inheritParams vetiver_model 18 | #' 19 | #' @return A `vetiver_ptype` method returns a zero-row dataframe, and 20 | #' `vetiver_create_ptype()` returns either such a zero-row dataframe, `NULL`, 21 | #' or the dataframe passed to `save_prototype`. 22 | #' 23 | #' @examples 24 | #' 25 | #' cars_lm <- lm(mpg ~ cyl + disp, data = mtcars) 26 | #' 27 | #' vetiver_create_ptype(cars_lm, TRUE) 28 | #' 29 | #' ## calls the right method for `model` via: 30 | #' vetiver_ptype(cars_lm) 31 | #' 32 | #' ## can also turn off prototype 33 | #' vetiver_create_ptype(cars_lm, FALSE) 34 | #' @examplesIf rlang::is_installed("ranger") 35 | #' ## some models require that you pass in training features 36 | #' cars_rf <- ranger::ranger(mpg ~ ., data = mtcars) 37 | #' vetiver_ptype(cars_rf, prototype_data = mtcars[,-1]) 38 | #' 39 | #' @rdname vetiver_create_ptype 40 | #' @export 41 | vetiver_ptype <- function(model, ...) { 42 | UseMethod("vetiver_ptype") 43 | } 44 | 45 | #' @rdname vetiver_create_ptype 46 | #' @export 47 | vetiver_ptype.default <- function(model, ...) { 48 | abort("There is no method available to create a 0-row input data prototype for `model`.") 49 | } 50 | 51 | #' @rdname vetiver_create_ptype 52 | #' @export 53 | vetiver_create_ptype <- function(model, save_prototype, ...) { 54 | check_dots_used() 55 | if (isTRUE(save_prototype)) { 56 | ptype <- vetiver_ptype(model, ...) 57 | } else if (isFALSE(save_prototype)) { 58 | ptype <- NULL 59 | } else if (rlang::inherits_any(save_prototype, "data.frame")) { 60 | return(save_prototype) 61 | } else { 62 | abort("The `save_prototype` argument must be TRUE, FALSE, or a dataframe.") 63 | } 64 | ptype 65 | } 66 | 67 | check_ptype_data <- function(dots, call = rlang::caller_env()) { 68 | if (!rlang::has_name(dots, "prototype_data")) { 69 | abort(c("No `prototype_data` available to create an input data prototype", 70 | "Pass at least one row of training features as `prototype_data`", 71 | "See the documentation for `vetiver_ptype()`"), 72 | call = call) 73 | } 74 | } 75 | 76 | 77 | preds_lm_ish <- function(model) { 78 | .terms <- terms(model) 79 | terms_matrix <- attr(.terms, "factors") 80 | terms_names <- colnames(terms_matrix) 81 | terms_exprs <- parse_exprs(terms_names) 82 | has_interactions <- map_lgl(terms_exprs, expr_contains, what = as.name(":")) 83 | terms_names[!has_interactions] 84 | } 85 | 86 | expr_contains <- function(expr, what) { 87 | switch(typeof(expr), 88 | symbol = identical(expr, what), 89 | call = call_contains(expr, what), 90 | language = call_contains(expr, what), 91 | FALSE 92 | ) 93 | } 94 | 95 | call_contains <- function(expr, what, call = rlang::caller_env()) { 96 | if (length(expr) == 0L) { 97 | abort("Internal error, `expr` should be at least length 1.", call = call) 98 | } 99 | 100 | # Recurse into elements 101 | contains <- map_lgl(as.list(expr), expr_contains, what = what) 102 | any(contains) 103 | } 104 | 105 | -------------------------------------------------------------------------------- /R/ranger.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.ranger <- function(model) { 4 | glue("A ranger {tolower(model$forest$treetype)} model") 5 | } 6 | 7 | #' @rdname vetiver_create_meta 8 | #' @export 9 | vetiver_create_meta.ranger <- function(model, metadata) { 10 | vetiver_meta(metadata, required_pkgs = "ranger") 11 | } 12 | 13 | #' @rdname vetiver_create_description 14 | #' @export 15 | vetiver_prepare_model.ranger <- function(model) { 16 | butcher::butcher(model) 17 | } 18 | 19 | #' @rdname vetiver_create_ptype 20 | #' @export 21 | vetiver_ptype.ranger <- function(model, ...) { 22 | rlang::check_dots_used() 23 | dots <- list(...) 24 | check_ptype_data(dots) 25 | ptype <- vctrs::vec_ptype(dots$prototype_data) 26 | tibble::as_tibble(ptype) 27 | } 28 | 29 | #' @rdname handler_startup 30 | #' @export 31 | handler_startup.ranger <- function(vetiver_model) { 32 | attach_pkgs(vetiver_model$metadata$required_pkgs) 33 | } 34 | 35 | #' @rdname handler_startup 36 | #' @export 37 | handler_predict.ranger <- function(vetiver_model, ...) { 38 | 39 | ptype <- vetiver_model$prototype 40 | 41 | function(req) { 42 | new_data <- req$body 43 | if (!is_null(ptype)) { 44 | new_data <- vetiver_type_convert(new_data, ptype) 45 | new_data <- hardhat::scream(new_data, ptype) 46 | } 47 | ret <- predict(vetiver_model$model, data = new_data, ...) 48 | list(.pred = ret$predictions) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /R/recipe.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.recipe <- function(model) { 4 | num_steps <- length(model$steps) 5 | cli::pluralize("A feature engineering recipe with {num_steps} step{?s}") 6 | } 7 | 8 | #' @rdname vetiver_create_meta 9 | #' @export 10 | vetiver_create_meta.recipe <- function(model, metadata) { 11 | reqs <- required_pkgs(model) 12 | reqs <- sort(unique(c(reqs, "recipes"))) 13 | vetiver_meta(metadata, required_pkgs = reqs) 14 | } 15 | 16 | #' @rdname vetiver_create_ptype 17 | #' @export 18 | vetiver_ptype.recipe <- function(model, ...) { 19 | recipes::recipes_ptype(model, stage = "bake") 20 | } 21 | 22 | #' @rdname vetiver_create_description 23 | #' @export 24 | vetiver_prepare_model.recipe <- function(model) { 25 | if (!recipes::fully_trained(model)) { 26 | rlang::abort("Your `model` object is not a trained recipe.") 27 | } 28 | ret <- butcher::butcher(model) 29 | ret <- bundle::bundle(ret) 30 | ret 31 | } 32 | 33 | #' @rdname handler_startup 34 | #' @export 35 | handler_startup.recipe <- function(vetiver_model) { 36 | attach_pkgs(vetiver_model$metadata$required_pkgs) 37 | } 38 | 39 | #' @rdname handler_startup 40 | #' @export 41 | handler_predict.recipe <- function(vetiver_model, ...) { 42 | 43 | function(req) { 44 | new_data <- req$body 45 | new_data <- vetiver_type_convert(new_data, vetiver_model$prototype) 46 | recipes::bake(vetiver_model$model, new_data = new_data, ...) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /R/renv.R: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # renv 1.0.3.9000 [rstudio/renv#4b11818]: A dependency management toolkit for R. 4 | # Generated using `renv:::vendor()` at 2023-10-31 16:35:16.988562. 5 | # 6 | 7 | 8 | renv <- new.env(parent = new.env()) 9 | 10 | renv$initialize <- function() { 11 | 12 | # set up renv + imports environments 13 | attr(renv, "name") <- "embedded:renv" 14 | attr(parent.env(renv), "name") <- "imports:renv" 15 | 16 | # get imports 17 | imports <- list( 18 | tools = c( 19 | "file_ext", 20 | "pskill", 21 | "psnice", 22 | "write_PACKAGES" 23 | ), 24 | utils = c( 25 | "Rprof", 26 | "URLencode", 27 | "adist", 28 | "available.packages", 29 | "browseURL", 30 | "citation", 31 | "contrib.url", 32 | "download.file", 33 | "download.packages", 34 | "file.edit", 35 | "getCRANmirrors", 36 | "head", 37 | "help", 38 | "install.packages", 39 | "installed.packages", 40 | "modifyList", 41 | "old.packages", 42 | "packageDescription", 43 | "packageVersion", 44 | "read.table", 45 | "remove.packages", 46 | "sessionInfo", 47 | "str", 48 | "summaryRprof", 49 | "tail", 50 | "tar", 51 | "toBibtex", 52 | "untar", 53 | "unzip", 54 | "update.packages", 55 | "zip" 56 | ) 57 | ) 58 | 59 | # load the imports required by renv 60 | for (package in names(imports)) { 61 | namespace <- asNamespace(package) 62 | functions <- imports[[package]] 63 | list2env(mget(functions, envir = namespace), envir = parent.env(renv)) 64 | } 65 | 66 | # source renv into the aforementioned environment 67 | script <- system.file("vendor/renv.R", package = .packageName) 68 | sys.source(script, envir = renv) 69 | 70 | # initialize metadata 71 | renv$the$metadata <- list( 72 | embedded = TRUE, 73 | version = structure("1.0.3.9000", sha = "4b11818ca81897f10bf1def73db6b69c9fd1af0f") 74 | ) 75 | 76 | # run our load / attach hooks so internal state is initialized 77 | renv$renv_zzz_load() 78 | 79 | # remove our initialize method when we're done 80 | rm(list = "initialize", envir = renv) 81 | 82 | } 83 | -------------------------------------------------------------------------------- /R/stacks.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.model_stack <- function(model) { 4 | num_members <- 5 | tidy(model$coefs) %>% 6 | dplyr::filter(estimate > 0, term != "(Intercept)") %>% 7 | nrow() 8 | glue("A {model$mode} stacked ensemble with {num_members} members") 9 | } 10 | 11 | #' @rdname vetiver_create_meta 12 | #' @export 13 | vetiver_create_meta.model_stack <- function(model, metadata) { 14 | reqs <- map(model$member_fits, required_pkgs) 15 | reqs <- purrr::flatten_chr(reqs) 16 | reqs <- sort(unique(c(reqs, required_pkgs(model$coefs), "workflows", "stacks"))) 17 | vetiver_meta(metadata, required_pkgs = reqs) 18 | } 19 | 20 | #' @rdname vetiver_create_ptype 21 | #' @export 22 | vetiver_ptype.model_stack <- function(model, ...) { 23 | mold <- workflows::extract_mold(model$member_fits[[1]]) 24 | mold$blueprint$ptypes$predictors 25 | } 26 | 27 | #' @rdname vetiver_create_description 28 | #' @export 29 | vetiver_prepare_model.model_stack <- function(model) { 30 | ret <- butcher::butcher(model) 31 | ret <- bundle::bundle(ret) 32 | ret 33 | } 34 | 35 | #' @rdname handler_startup 36 | #' @export 37 | handler_startup.model_stack <- function(vetiver_model) { 38 | attach_pkgs(vetiver_model$metadata$required_pkgs) 39 | } 40 | 41 | #' @rdname handler_startup 42 | #' @export 43 | handler_predict.model_stack <- function(vetiver_model, ...) { 44 | 45 | function(req) { 46 | new_data <- req$body 47 | new_data <- vetiver_type_convert(new_data, vetiver_model$prototype) 48 | predict(vetiver_model$model, new_data = new_data, ...) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /R/tidymodels.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.workflow <- function(model) { 4 | spec <- workflows::extract_spec_parsnip(model) 5 | glue("A {spec$engine} {spec$mode} modeling workflow") 6 | } 7 | 8 | #' @rdname vetiver_create_meta 9 | #' @export 10 | vetiver_create_meta.workflow <- function(model, metadata) { 11 | reqs <- required_pkgs(model) 12 | reqs <- sort(unique(c(reqs, "workflows"))) 13 | vetiver_meta(metadata, required_pkgs = reqs) 14 | } 15 | 16 | #' @rdname vetiver_create_ptype 17 | #' @export 18 | vetiver_ptype.workflow <- function(model, ...) { 19 | mold <- workflows::extract_mold(model) 20 | mold$blueprint$ptypes$predictors 21 | } 22 | 23 | #' @rdname vetiver_create_description 24 | #' @export 25 | vetiver_prepare_model.workflow <- function(model) { 26 | if (!workflows::is_trained_workflow(model)) { 27 | rlang::abort("Your `model` object is not a trained workflow.") 28 | } 29 | ret <- butcher::butcher(model) 30 | ret <- bundle::bundle(ret) 31 | ret 32 | } 33 | 34 | #' @rdname handler_startup 35 | #' @export 36 | handler_startup.workflow <- function(vetiver_model) { 37 | attach_pkgs(vetiver_model$metadata$required_pkgs) 38 | } 39 | 40 | #' @rdname handler_startup 41 | #' @export 42 | handler_predict.workflow <- function(vetiver_model, ...) { 43 | 44 | function(req) { 45 | new_data <- req$body 46 | new_data <- vetiver_type_convert(new_data, vetiver_model$prototype) 47 | predict(vetiver_model$model, new_data = new_data, ...) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /R/vetiver-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | 5 | #' @import rlang 6 | #' @importFrom purrr map map_lgl map_chr 7 | #' @importFrom purrr transpose compact pluck 8 | #' @importFrom purrr pmap safely list_modify 9 | #' @importFrom utils head modifyList flush.console 10 | #' @importFrom stats predict 11 | #' @importFrom vctrs vec_slice vec_sort 12 | #' @importFrom magrittr %>% 13 | #' @importFrom glue glue 14 | #' @importFrom glue glue_collapse 15 | #' @importFrom generics required_pkgs 16 | #' @importFrom lifecycle deprecated 17 | NULL 18 | 19 | #' @importFrom generics augment 20 | #' @export 21 | generics::augment 22 | 23 | #' @importFrom generics required_pkgs 24 | #' @export 25 | generics::required_pkgs 26 | 27 | globalVariables(c("pr", ".metric", ".pred", "price", "tidy", 28 | "term", "estimate", "terms")) 29 | 30 | ## to avoid NOTE about "All declared Imports should be used." 31 | rapidoc_function_for_note <- function() { 32 | rapidoc::rapidoc_spec() 33 | } 34 | 35 | release_bullets <- function() { 36 | c( 37 | 'Update renv with `renv:::vendor()`' 38 | ) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /R/xgboost.R: -------------------------------------------------------------------------------- 1 | #' @rdname vetiver_create_description 2 | #' @export 3 | vetiver_create_description.xgb.Booster <- function(model) { 4 | if (!is_null(model$params$objective)) { 5 | ret <- glue("An xgboost {model$params$objective} model") 6 | } else { 7 | "An xgboost model" 8 | } 9 | } 10 | 11 | #' @rdname vetiver_create_meta 12 | #' @export 13 | vetiver_create_meta.xgb.Booster <- function(model, metadata) { 14 | vetiver_meta(metadata, required_pkgs = "xgboost") 15 | } 16 | 17 | #' @rdname vetiver_create_ptype 18 | #' @export 19 | vetiver_ptype.xgb.Booster <- function(model, ...) { 20 | pred_names <- matrix(NA_real_, 21 | ncol = model$nfeatures, 22 | dimnames = list("", model$feature_names)) 23 | ptype <- vctrs::vec_ptype(pred_names) 24 | tibble::as_tibble(ptype) 25 | } 26 | 27 | #' @rdname vetiver_create_description 28 | #' @export 29 | vetiver_prepare_model.xgb.Booster <- function(model) { 30 | bundle::bundle(model) 31 | } 32 | 33 | #' @rdname handler_startup 34 | #' @export 35 | handler_startup.xgb.Booster <- function(vetiver_model) { 36 | attach_pkgs(vetiver_model$metadata$required_pkgs) 37 | } 38 | 39 | #' @rdname handler_startup 40 | #' @export 41 | handler_predict.xgb.Booster <- function(vetiver_model, ...) { 42 | 43 | ptype <- vetiver_model$prototype 44 | 45 | function(req) { 46 | newdata <- req$body 47 | if (!is_null(ptype)) { 48 | newdata <- vetiver_type_convert(newdata, ptype) 49 | newdata <- hardhat::scream(newdata, ptype) 50 | } 51 | newdata <- xgboost::xgb.DMatrix(as.matrix(newdata)) 52 | ret <- predict(vetiver_model$model, newdata = newdata, ...) 53 | list(.pred = ret) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # nocov start 2 | 3 | .onLoad <- function(libname, pkgname) { 4 | renv$initialize() 5 | } 6 | 7 | # nocov end 8 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://rstudio.github.io/vetiver-r/ 2 | template: 3 | bootstrap: 5 4 | package: tidytemplate 5 | 6 | navbar: 7 | structure: 8 | left: [intro, reference, news, vetiver] 9 | components: 10 | vetiver: 11 | text: Learn more 12 | menu: 13 | - text: "vetiver.rstudio.com" 14 | href: https://vetiver.rstudio.com 15 | target: "_blank" 16 | - text: "Python package documentation" 17 | href: https://rstudio.github.io/vetiver-python/ 18 | target: "_blank" 19 | 20 | 21 | development: 22 | mode: auto 23 | 24 | reference: 25 | - title: Handling vetiver objects 26 | desc: > 27 | A `vetiver_model()` collects the information needed to store, version, and 28 | deploy a trained model. 29 | contents: 30 | - vetiver_model 31 | - vetiver_pin_write 32 | - vetiver_api 33 | - vetiver_write_plumber 34 | - vetiver_write_docker 35 | - vetiver_prepare_docker 36 | - vetiver_endpoint 37 | - predict.vetiver_endpoint 38 | - augment.vetiver_endpoint 39 | 40 | - title: Posit Connect 41 | desc: Deploy your vetiver model to Posit Connect. 42 | contents: 43 | - vetiver_deploy_rsconnect 44 | - vetiver_create_rsconnect_bundle 45 | 46 | - title: SageMaker 47 | desc: Deploy your vetiver model to Amazon SageMaker. 48 | contents: 49 | - vetiver_deploy_sagemaker 50 | - vetiver_endpoint_sagemaker 51 | - predict.vetiver_endpoint_sagemaker 52 | - augment.vetiver_endpoint_sagemaker 53 | - vetiver_sm_build 54 | - vetiver_sm_delete 55 | 56 | - title: Monitoring deployed models 57 | desc: > 58 | Monitor a deployed `vetiver_model()` with a dashboard. 59 | contents: 60 | - vetiver_compute_metrics 61 | - vetiver_pin_metrics 62 | - vetiver_plot_metrics 63 | - vetiver_dashboard 64 | 65 | - title: Developer functions 66 | desc: > 67 | These functions are helpful for developers extending vetiver for other 68 | types of models. 69 | contents: 70 | - api_spec 71 | - attach_pkgs 72 | - handler_startup 73 | - map_request_body 74 | - vetiver_create_description 75 | - vetiver_create_meta 76 | - vetiver_create_ptype 77 | - vetiver_type_convert 78 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 1 reverse dependency, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | -------------------------------------------------------------------------------- /inst/biv_tidymodels.R: -------------------------------------------------------------------------------- 1 | library(tidymodels) 2 | data(bivariate) 3 | 4 | biv_rec <- 5 | recipe(Class ~ ., data = bivariate_train) %>% 6 | step_BoxCox(all_predictors())%>% 7 | step_normalize(all_predictors()) 8 | 9 | svm_spec <- 10 | svm_linear(mode = "classification") %>% 11 | set_engine("LiblineaR") 12 | 13 | svm_fit <- workflow() %>% 14 | add_recipe(biv_rec) %>% 15 | add_model(svm_spec) %>% 16 | fit(bivariate_train) 17 | 18 | library(vetiver) 19 | v <- vetiver_model(svm_fit, "biv_svm") 20 | v 21 | 22 | library(pins) 23 | model_board <- board_folder(path = "/tmp/test", versioned = TRUE) 24 | vetiver_pin_write(model_board, v) 25 | 26 | library(plumber) 27 | pr() %>% 28 | vetiver_api(v, type = "class", debug = TRUE) 29 | ## next pipe to pr_run(port = 8088) to see visual documentation 30 | 31 | vetiver_write_plumber(model_board, "biv_svm", file = "inst/plumber/biv-svm/plumber.R") 32 | -------------------------------------------------------------------------------- /inst/chicago_rpart.R: -------------------------------------------------------------------------------- 1 | library(tidymodels) 2 | data(Chicago) 3 | 4 | chicago_small <- Chicago %>% slice(1:365) 5 | 6 | splits <- 7 | sliding_period( 8 | chicago_small, 9 | date, 10 | "day", 11 | lookback = 300, # Each resample has 300 days for modeling 12 | assess_stop = 7, # One week for performance assessment 13 | step = 7 # Ensure non-overlapping weeks for assessment 14 | ) 15 | 16 | chicago_rec <- 17 | recipe(ridership ~ ., data = Chicago) %>% 18 | step_date(date) %>% 19 | step_holiday(date, keep_original_cols = FALSE) %>% 20 | step_dummy(all_nominal_predictors()) %>% 21 | step_zv(all_predictors()) %>% 22 | step_normalize(all_predictors()) %>% 23 | step_pca(all_of(stations), num_comp = 4) 24 | 25 | tree_spec <- 26 | decision_tree() %>% 27 | set_engine("rpart") %>% 28 | set_mode("regression") 29 | 30 | chicago_fit <- 31 | workflow(chicago_rec, tree_spec) %>% 32 | fit(chicago_small) 33 | 34 | library(vetiver) 35 | v <- vetiver_model(chicago_fit, "julia.silge/chicago_ridership") 36 | v 37 | 38 | 39 | vetiver_pin_write(model_board, v, check_renv = TRUE) 40 | 41 | #* -------------------------------------------------------------------------- *# 42 | 43 | library(vetiver) 44 | library(pins) 45 | model_board <- board_connect() 46 | vetiver_deploy_rsconnect( 47 | model_board, 48 | "julia.silge/chicago_ridership", 49 | predict_args = list(debug = TRUE), 50 | account = "julia.silge" 51 | ) 52 | 53 | #* -------------------------------------------------------------------------- *# 54 | 55 | library(plumber) 56 | pr() %>% 57 | vetiver_api(v, debug = TRUE) 58 | ## next pipe to pr_run(port = 8088) to see visual documentation 59 | 60 | vetiver_write_plumber( 61 | model_board, 62 | "julia.silge/chicago_ridership", 63 | debug = TRUE, 64 | file = "inst/plumber/chicago-rpart/plumber.R" 65 | ) 66 | -------------------------------------------------------------------------------- /inst/mtcars_keras.R: -------------------------------------------------------------------------------- 1 | library(vetiver) 2 | library(plumber) 3 | library(keras) 4 | 5 | scaled_cars <- as.matrix(mtcars) %>% scale() 6 | x_test <- scaled_cars[26:32, 2:ncol(scaled_cars)] 7 | x_train <- scaled_cars[1:25, 2:ncol(scaled_cars)] 8 | y_train <- scaled_cars[1:25, 1, drop = FALSE] 9 | 10 | set.seed(1) 11 | 12 | keras_fit <- 13 | keras_model_sequential(input_shape = ncol(x_train)) %>% 14 | layer_dense(units = 1, activation = 'linear') %>% 15 | compile( 16 | loss = 'mean_squared_error', 17 | optimizer = optimizer_adam(learning_rate = .01) 18 | ) 19 | 20 | keras_fit %>% 21 | fit( 22 | x = x_train, y = y_train, 23 | epochs = 100, batch_size = 1, 24 | verbose = 0 25 | ) 26 | 27 | v <- vetiver_model(keras_fit, "cars-keras", prototype_data = data.frame(x_train)[1,]) 28 | ## pr() %>% vetiver_api(v, debug = TRUE) %>% pr_run() 29 | 30 | library(pins) 31 | b <- board_connect() 32 | b %>% vetiver_pin_write(v) 33 | 34 | vetiver_deploy_rsconnect( 35 | b, 36 | "julia.silge/cars-keras", 37 | predict_args = list(debug = TRUE), 38 | account = "julia.silge" 39 | ) 40 | 41 | 42 | -------------------------------------------------------------------------------- /inst/mtcars_lm.R: -------------------------------------------------------------------------------- 1 | library(vetiver) 2 | cars_lm <- lm(mpg ~ ., data = mtcars) 3 | v <- vetiver_model(cars_lm, "cars_linear") 4 | 5 | library(pins) 6 | model_board <- board_folder(path = "/tmp/test", versioned = TRUE) 7 | vetiver_pin_write(model_board, v) 8 | 9 | library(plumber) 10 | pr() %>% 11 | vetiver_api(v, debug = TRUE) 12 | ## next pipe to pr_run(port = 8088) to see visual documentation 13 | 14 | vetiver_write_plumber(model_board, "cars_linear", file = "inst/plumber/mtcars-lm/plumber.R") 15 | -------------------------------------------------------------------------------- /inst/mtcars_luz.R: -------------------------------------------------------------------------------- 1 | library(vetiver) 2 | library(plumber) 3 | library(torch) 4 | 5 | scaled_cars <- as.matrix(mtcars) %>% scale() 6 | x_test <- scaled_cars[26:32, 2:ncol(scaled_cars)] 7 | x_train <- scaled_cars[1:25, 2:ncol(scaled_cars)] 8 | y_train <- scaled_cars[1:25, 1, drop = FALSE] 9 | 10 | set.seed(1) 11 | 12 | luz_module <- torch::nn_module( 13 | initialize = function(in_features, out_features) { 14 | self$linear <- nn_linear(in_features, out_features) 15 | }, 16 | forward = function(x) { 17 | if (self$training) { 18 | self$linear(x) 19 | } else { 20 | torch_randn(dim(x)[1], 3, 64, 64, device = self$linear$weight$device) 21 | } 22 | 23 | } 24 | ) 25 | 26 | luz_fit <- luz_module %>% 27 | luz::setup(loss = torch::nnf_mse_loss, optimizer = torch::optim_sgd) %>% 28 | luz::set_hparams(in_features = ncol(x_train), out_features = 1) %>% 29 | luz::set_opt_hparams(lr = 0.01) %>% 30 | luz::fit(list(x_train, y_train), verbose = FALSE, dataloader_options = list(batch_size = 5)) 31 | 32 | v <- vetiver_model(luz_fit, "cars-luz", prototype_data = data.frame(x_train)[1,]) 33 | pr() %>% vetiver_api(v, debug = TRUE) %>% pr_run(port = 8080) 34 | 35 | ##### in new session: ########################################################## 36 | # library(vetiver) 37 | # endpoint <- vetiver_endpoint("http://127.0.0.1:8080/predict") 38 | # x_test <- dplyr::slice(data.frame(scale(mtcars)), 26:32) 39 | # predict(endpoint, x_test[1:2,]) 40 | -------------------------------------------------------------------------------- /inst/mtcars_ranger.R: -------------------------------------------------------------------------------- 1 | library(vetiver) 2 | library(pins) 3 | library(plumber) 4 | 5 | cars_rf <- ranger::ranger(mpg ~ ., data = mtcars, quantreg = TRUE) 6 | v <- vetiver_model(cars_rf, "cars_ranger", prototype_data = mtcars[,-1]) 7 | 8 | model_board <- board_folder(path = "/tmp/test") 9 | vetiver_pin_write(model_board, v) 10 | 11 | pr() %>% vetiver_api(v, debug = TRUE, type = "quantiles") 12 | ## next pipe to pr_run(port = 8088) to see visual documentation 13 | 14 | vetiver_write_plumber( 15 | model_board, 16 | "cars_ranger", 17 | debug = TRUE, 18 | type = "quantiles", 19 | file = "inst/plumber/mtcars-ranger/plumber.R" 20 | ) 21 | -------------------------------------------------------------------------------- /inst/mtcars_xgb.R: -------------------------------------------------------------------------------- 1 | library(vetiver) 2 | library(pins) 3 | library(plumber) 4 | 5 | model_board <- board_folder(path = "/tmp/test", versioned = TRUE) 6 | cars_xgb <- xgboost::xgboost(as.matrix(mtcars[,-1]), 7 | mtcars$mpg, 8 | nrounds = 3, 9 | objective = "reg:squarederror") 10 | v <- vetiver_model(cars_xgb, "cars_xgb") 11 | model_board %>% vetiver_pin_write(v) 12 | 13 | pr() %>% 14 | vetiver_api(v, debug = TRUE) 15 | ## next pipe to pr_run(port = 8088) to see visual documentation 16 | 17 | vetiver_write_plumber(model_board, "cars_xgb", file = "inst/plumber/mtcars-xgb/plumber.R") 18 | -------------------------------------------------------------------------------- /inst/netflix_tidymodels.R: -------------------------------------------------------------------------------- 1 | library(tidymodels) 2 | library(textrecipes) 3 | library(themis) 4 | 5 | url <- "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-04-20/netflix_titles.csv" 6 | 7 | netflix_types <- readr::read_csv(url) %>% 8 | select(type, description) 9 | 10 | set.seed(123) 11 | netflix_split <- netflix_types %>% 12 | select(type, description) %>% 13 | initial_split(strata = type) 14 | 15 | netflix_train <- training(netflix_split) 16 | netflix_test <- testing(netflix_split) 17 | 18 | netflix_rec <- recipe(type ~ description, data = netflix_train) %>% 19 | step_tokenize(description) %>% 20 | step_tokenfilter(description, max_tokens = 1e3) %>% 21 | step_tfidf(description) %>% 22 | step_normalize(all_numeric_predictors()) %>% 23 | step_smote(type) 24 | 25 | svm_spec <- svm_linear() %>% 26 | set_mode("classification") %>% 27 | set_engine("LiblineaR") 28 | 29 | netflix_fit <- 30 | workflow(netflix_rec, svm_spec) %>% 31 | fit(netflix_train) 32 | 33 | library(vetiver) 34 | v <- vetiver_model(netflix_fit, "netflix_descriptions") 35 | v 36 | 37 | library(pins) 38 | model_board <- board_connect() 39 | vetiver_pin_write(model_board, v) 40 | 41 | library(plumber) 42 | pr() %>% 43 | vetiver_api(v, debug = TRUE) 44 | ## next pipe to pr_run(port = 8088) to see visual documentation 45 | 46 | vetiver_write_plumber( 47 | model_board, 48 | "julia.silge/netflix_descriptions", 49 | debug = TRUE, 50 | file = "inst/plumber/netflix-descriptions/plumber.R" 51 | ) 52 | 53 | -------------------------------------------------------------------------------- /inst/pima_Learner.R: -------------------------------------------------------------------------------- 1 | library(mlr3) 2 | library(vetiver) 3 | library(plumber) 4 | 5 | task = tsk("pima") 6 | learner = lrn("classif.rpart") 7 | 8 | learner$train(task) 9 | 10 | v <- vetiver_model(learner, "pima_rpart") 11 | v 12 | 13 | pr() %>% 14 | vetiver_api(v) %>% 15 | pr_run(port = 8088) 16 | 17 | library(mlr3) 18 | library(plumber) 19 | library(vetiver) 20 | library(tibble) 21 | 22 | task = tsk("pima") 23 | newdata = as_tibble(task$data())[, task$feature_names] 24 | 25 | endpoint <- vetiver_endpoint("http://127.0.0.1:8088/predict") 26 | predict(endpoint, newdata) 27 | -------------------------------------------------------------------------------- /inst/plumber/biv-svm/plumber.R: -------------------------------------------------------------------------------- 1 | # Generated by the vetiver package; edit with care 2 | 3 | library(pins) 4 | library(plumber) 5 | library(rapidoc) 6 | library(vetiver) 7 | 8 | # Packages needed to generate model predictions 9 | if (FALSE) { 10 | library(dials) 11 | library(dplyr) 12 | library(LiblineaR) 13 | library(parsnip) 14 | library(purrr) 15 | library(recipes) 16 | library(rlang) 17 | library(rsample) 18 | library(tibble) 19 | library(tidyr) 20 | library(tune) 21 | library(vctrs) 22 | library(workflows) 23 | library(yardstick) 24 | } 25 | b <- board_folder(path = "/tmp/test") 26 | v <- vetiver_pin_read(b, "biv_svm", version = "20220211T213615Z-182ed") 27 | 28 | #* @plumber 29 | function(pr) { 30 | pr %>% vetiver_api(v) 31 | } 32 | -------------------------------------------------------------------------------- /inst/plumber/chicago-rpart/plumber.R: -------------------------------------------------------------------------------- 1 | # Generated by the vetiver package; edit with care 2 | 3 | library(pins) 4 | library(plumber) 5 | library(rapidoc) 6 | library(vetiver) 7 | 8 | # Packages needed to generate model predictions 9 | if (FALSE) { 10 | library(dials) 11 | library(dplyr) 12 | library(parsnip) 13 | library(purrr) 14 | library(recipes) 15 | library(rlang) 16 | library(rpart) 17 | library(rsample) 18 | library(tibble) 19 | library(tidyr) 20 | library(tune) 21 | library(vctrs) 22 | library(workflows) 23 | library(yardstick) 24 | } 25 | b <- board_rsconnect("envvar", server = "https://colorado.rstudio.com/rsc") 26 | v <- vetiver_pin_read(b, "julia.silge/chicago_ridership", version = "53335") 27 | 28 | #* @plumber 29 | function(pr) { 30 | pr %>% vetiver_api(v, debug = TRUE) 31 | } 32 | -------------------------------------------------------------------------------- /inst/plumber/mtcars-lm/plumber.R: -------------------------------------------------------------------------------- 1 | # Generated by the vetiver package; edit with care 2 | 3 | library(pins) 4 | library(plumber) 5 | library(rapidoc) 6 | library(vetiver) 7 | b <- board_folder(path = "/tmp/test") 8 | v <- vetiver_pin_read(b, "cars_linear", version = "20220211T213712Z-522c5") 9 | 10 | #* @plumber 11 | function(pr) { 12 | pr %>% vetiver_api(v) 13 | } 14 | -------------------------------------------------------------------------------- /inst/plumber/mtcars-ranger/plumber.R: -------------------------------------------------------------------------------- 1 | # Generated by the vetiver package; edit with care 2 | 3 | library(pins) 4 | library(plumber) 5 | library(rapidoc) 6 | library(vetiver) 7 | 8 | # Packages needed to generate model predictions 9 | if (FALSE) { 10 | library(ranger) 11 | } 12 | b <- board_folder(path = "/tmp/test") 13 | v <- vetiver_pin_read(b, "cars_ranger") 14 | 15 | #* @plumber 16 | function(pr) { 17 | pr %>% vetiver_api(v, debug = TRUE, type = "quantiles") 18 | } 19 | -------------------------------------------------------------------------------- /inst/plumber/mtcars-xgb/plumber.R: -------------------------------------------------------------------------------- 1 | # Generated by the vetiver package; edit with care 2 | 3 | library(pins) 4 | library(plumber) 5 | library(rapidoc) 6 | library(vetiver) 7 | 8 | # Packages needed to generate model predictions 9 | if (FALSE) { 10 | library(xgboost) 11 | } 12 | b <- board_folder(path = "/tmp/test") 13 | v <- vetiver_pin_read(b, "cars_xgb", version = "20220211T213735Z-cc767") 14 | 15 | #* @plumber 16 | function(pr) { 17 | pr %>% vetiver_api(v) 18 | } 19 | -------------------------------------------------------------------------------- /inst/plumber/netflix-descriptions/plumber.R: -------------------------------------------------------------------------------- 1 | # Generated by the vetiver package; edit with care 2 | 3 | library(pins) 4 | library(plumber) 5 | library(rapidoc) 6 | library(vetiver) 7 | 8 | # Packages needed to generate model predictions 9 | if (FALSE) { 10 | library(dials) 11 | library(dplyr) 12 | library(LiblineaR) 13 | library(parsnip) 14 | library(purrr) 15 | library(recipes) 16 | library(rlang) 17 | library(rsample) 18 | library(textrecipes) 19 | library(themis) 20 | library(tibble) 21 | library(tidyr) 22 | library(tune) 23 | library(vctrs) 24 | library(workflows) 25 | library(yardstick) 26 | } 27 | b <- board_rsconnect("envvar", server = "https://colorado.rstudio.com/rsc") 28 | v <- vetiver_pin_read(b, "julia.silge/netflix_descriptions", version = "53336") 29 | 30 | #* @plumber 31 | function(pr) { 32 | pr %>% vetiver_api(v, debug = TRUE) 33 | } 34 | -------------------------------------------------------------------------------- /inst/requirements/keras-requirements.txt: -------------------------------------------------------------------------------- 1 | keras 2 | tensorflow 3 | -------------------------------------------------------------------------------- /inst/requirements/luz-renviron.txt: -------------------------------------------------------------------------------- 1 | TORCH_INSTALL=1 2 | TORCH_HOME="libtorch/" 3 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/vetiver_dashboard/skeleton/skeleton.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Monitor model" 3 | output: 4 | vetiver::vetiver_dashboard: 5 | pins: 6 | board: !expr pins::board_local() 7 | name: 'seattle_rf' 8 | version: NULL 9 | storyboard: true 10 | theme: 11 | version: 4 12 | bootswatch: cosmo 13 | display_pins: true 14 | --- 15 | 16 | ```{r setup, include = FALSE} 17 | library(flexdashboard) 18 | library(vetiver) 19 | library(pins) 20 | library(plotly) 21 | 22 | knitr::opts_chunk$set(echo = FALSE) 23 | pins <- get_vetiver_dashboard_pins() 24 | metrics_pin_name <- paste(pins$name, "metrics", sep = "_") 25 | ``` 26 | 27 | ```{r set-up-demo-pins, include = FALSE, eval = FALSE} 28 | # Before knitting this example monitoring dashboard, execute: 29 | pin_example_kc_housing_model() 30 | 31 | # This function will set up demo model and metrics pins 32 | # Instead of real pin URLs, you will see only demo links 33 | ``` 34 | 35 | ```{r load-vetiver-model, include = FALSE} 36 | # Load deployed model from pin: 37 | v <- vetiver_pin_read(pins$board, pins$name, version = pins$version) 38 | meta <- pin_meta(pins$board, pins$name, version = pins$version) 39 | days_old <- difftime(Sys.Date(), as.Date(meta$created), units = "days") 40 | 41 | # Attaches packages needed for prediction: 42 | attach_pkgs(v$metadata$required_pkgs) 43 | ``` 44 | 45 | ```{r validation, include = FALSE} 46 | # Load new validation data, for example from database or API: 47 | validation_df <- mlr3data::kc_housing %>% 48 | arrange(date) %>% 49 | filter(date >= "2015-01-01") 50 | 51 | validation_aug <- augment(v, validation_df) 52 | 53 | new_metrics <- 54 | validation_aug %>% 55 | vetiver_compute_metrics(date, "week", price, .pred) 56 | 57 | vetiver_pin_metrics(pins$board, new_metrics, metrics_pin_name, overwrite = TRUE) 58 | ``` 59 | 60 | 61 | ### Model metrics 62 | 63 | ```{r} 64 | p1 <- new_metrics %>% 65 | ## you can operate on your metrics as needed: 66 | filter(.metric %in% c("rmse", "mae"), .n > 20) %>% 67 | vetiver_plot_metrics() + 68 | ## you can also operate on the ggplot: 69 | scale_size(range = c(2, 5)) 70 | 71 | p1 <- ggplotly(p1) 72 | hide_legend(p1) 73 | ``` 74 | 75 | *** 76 | 77 | This model was published `r as.numeric(days_old)` days ago. 78 | 79 | Plot model metrics over time to *monitor* your model. 80 | 81 | ### Explore validation data 82 | 83 | ```{r} 84 | p2 <- 85 | validation_df %>% 86 | ggplot(aes(price, after_stat(density), fill = waterfront)) + 87 | geom_histogram(alpha = 0.7, position = "identity") 88 | 89 | ggplotly(p2) 90 | ``` 91 | 92 | 93 | *** 94 | 95 | Write your own code to make visualizations or tables with the new validation data, and/or the new predictions. 96 | 97 | ### API visual documentation 98 | 99 | ```{r echo=FALSE, out.width="100%"} 100 | ## use your own vetiver model API URL here: 101 | knitr::include_url("https://colorado.rstudio.com/rsc/seattle-housing/", height = "600px") 102 | ``` 103 | 104 | *** 105 | 106 | Interact directly with your model via its visual documentation, and get `curl` examples. 107 | 108 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/vetiver_dashboard/template.yaml: -------------------------------------------------------------------------------- 1 | name: Vetiver Dashboard 2 | description: > 3 | A dashboard for model monitoring 4 | create_dir: FALSE 5 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/vetiver_model_card/skeleton/skeleton.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Model Card: your model title" 3 | date: '`r Sys.Date()`' 4 | output: 5 | html_document 6 | params: 7 | board: !r pins::board_connect() 8 | name: julia.silge/sacramento-rf 9 | version: NULL 10 | --- 11 | 12 | ```{r setup, include=FALSE} 13 | library(tidyverse) 14 | library(vetiver) 15 | library(pins) 16 | library(yardstick) 17 | knitr::opts_chunk$set(echo = FALSE) 18 | v <- vetiver_pin_read(params$board, params$name, version = params$version) 19 | v_meta <- pin_meta(params$board, params$name) 20 | theme_set(theme_light()) 21 | ``` 22 | 23 | A [model card](https://doi.org/10.1145/3287560.3287596) provides brief, transparent, responsible reporting for a trained machine learning model. 24 | 25 | ## Model details 26 | 27 | - Developed by PERSON AND/OR TEAM 28 | - `r cli::pluralize("{v$description} using {ncol(v$prototype)} feature{?s}")` 29 | - More details about how model was developed and what it is predicting 30 | - More details on feature engineering and/or data preprocessing for model 31 | - Version `r v$metadata$version` of this model was published at `r v_meta$created` 32 | - Citation and/or license details for the model 33 | - If you have questions about this model, please contact PERSON@ORG.ORG 34 | 35 | ## Intended use 36 | 37 | - The primary intended uses of this model are ... 38 | - The primary intended users of this model are ... 39 | - Some use cases are out of scope for this model, such as ... 40 | 41 | ## Important aspects/factors 42 | 43 | - Aspects or factors (demographic, environmental, technical) that are relevant to the context of this model are ... 44 | - In evaluating this model, we examined aspects such as ... 45 | 46 | ## Metrics 47 | 48 | - The metrics used to evaluate this model are ... 49 | - These metrics are computed via ... 50 | - We chose these metrics because ... 51 | 52 | ## Training data & evaluation data 53 | 54 | - The training dataset for this model was ... 55 | - The training dataset for this model has the "prototype" or signature: 56 | 57 | ```{r} 58 | glimpse(v$prototype) 59 | ``` 60 | 61 | - The evaluation dataset used in this model card is ... 62 | - We chose this evaluation data because ... 63 | 64 | ```{r} 65 | ## EVALUATION DATA: 66 | 67 | data(Sacramento, package = "modeldata") 68 | 69 | ## consider using a package like skimr or DataExplorer for automated 70 | ## presentation of evaluation data characteristics 71 | ``` 72 | 73 | 74 | ## Quantitative analyses {.tabset} 75 | 76 | ```{r} 77 | ## compute predictions for your evaluation data 78 | ## load packages needed for prediction: 79 | library(parsnip) 80 | library(workflows) 81 | preds <- augment(v, Sacramento) 82 | ``` 83 | 84 | 85 | ### Overall model performance 86 | 87 | ```{r} 88 | preds %>% 89 | metrics(price, .pred) 90 | ``` 91 | 92 | ### Disaggregated model performance 93 | 94 | ```{r} 95 | preds %>% 96 | group_by(type) %>% 97 | metrics(price, .pred) 98 | ``` 99 | 100 | ### Visualize model performance 101 | 102 | ```{r, fig.height=3} 103 | preds %>% 104 | ggplot(aes(price, .pred, color = type)) + 105 | geom_abline(slope = 1, lty = 2, color = "gray60", size = 1.2) + 106 | geom_point(alpha = 0.5, show.legend = FALSE) + 107 | facet_wrap(vars(type)) 108 | ``` 109 | 110 | ### Make a custom plot 111 | 112 | ```{r} 113 | preds %>% 114 | mutate(.resid = price - .pred) %>% 115 | ggplot(aes(longitude, latitude, color = .resid)) + 116 | geom_point(alpha = 0.8) + 117 | scale_color_gradient2() + 118 | coord_fixed() 119 | ``` 120 | 121 | 122 | ## Ethical considerations 123 | 124 | - We considered ... 125 | 126 | ## Caveats & recommendations 127 | 128 | - This model does ... 129 | - This model does not ... 130 | - We recommend ... 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/vetiver_model_card/template.yaml: -------------------------------------------------------------------------------- 1 | name: Vetiver Model Card 2 | description: > 3 | A model card for responsible, transparent model reporting 4 | create_dir: FALSE 5 | -------------------------------------------------------------------------------- /inst/vetiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/vetiver-r/54996d51a9df3e157e2e94c60dd6872b43aa44f7/inst/vetiver.png -------------------------------------------------------------------------------- /man/api_spec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/open-api-spec.R 3 | \name{api_spec} 4 | \alias{api_spec} 5 | \alias{glue_spec_summary} 6 | \alias{glue_spec_summary.default} 7 | \alias{glue_spec_summary.data.frame} 8 | \alias{glue_spec_summary.array} 9 | \title{Update the OpenAPI specification using model metadata} 10 | \usage{ 11 | api_spec(spec, vetiver_model, path, all_docs = TRUE) 12 | 13 | glue_spec_summary(prototype, return_type) 14 | 15 | \method{glue_spec_summary}{default}(prototype, return_type = NULL) 16 | 17 | \method{glue_spec_summary}{data.frame}(prototype, return_type = "predictions") 18 | 19 | \method{glue_spec_summary}{array}(prototype, return_type = "predictions") 20 | } 21 | \arguments{ 22 | \item{spec}{An OpenAPI Specification formatted list object} 23 | 24 | \item{vetiver_model}{A deployable \code{\link[=vetiver_model]{vetiver_model()}} object} 25 | 26 | \item{path}{The endpoint path} 27 | 28 | \item{all_docs}{Should the interactive visual API documentation be created 29 | for \emph{all} POST endpoints in the router \code{pr}? This defaults to \code{TRUE}, and 30 | assumes that all POST endpoints use the \code{\link[=vetiver_model]{vetiver_model()}} input data 31 | prototype.} 32 | 33 | \item{prototype}{An input data prototype from a model} 34 | 35 | \item{return_type}{Character string to describe what endpoint returns, such 36 | as "predictions"} 37 | } 38 | \value{ 39 | \code{api_spec()} returns the updated OpenAPI Specification object. This 40 | function uses \code{glue_spec_summary()} internally, which returns a \code{glue} 41 | character string. 42 | } 43 | \description{ 44 | Update the OpenAPI specification using model metadata 45 | } 46 | \examples{ 47 | \dontshow{if (rlang::is_installed("plumber")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 48 | library(plumber) 49 | cars_lm <- lm(mpg ~ ., data = mtcars) 50 | v <- vetiver_model(cars_lm, "cars_linear") 51 | 52 | glue_spec_summary(v$prototype) 53 | 54 | modify_spec <- function(spec) api_spec(spec, v, "/predict") 55 | pr() \%>\% pr_set_api_spec(api = modify_spec) 56 | \dontshow{\}) # examplesIf} 57 | } 58 | -------------------------------------------------------------------------------- /man/attach_pkgs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/attach-pkgs.R 3 | \name{attach_pkgs} 4 | \alias{attach_pkgs} 5 | \alias{load_pkgs} 6 | \title{Fully attach or load packages for making model predictions} 7 | \usage{ 8 | attach_pkgs(pkgs) 9 | 10 | load_pkgs(pkgs) 11 | } 12 | \arguments{ 13 | \item{pkgs}{A character vector of package names to load or fully attach.} 14 | } 15 | \value{ 16 | An invisible \code{TRUE}. 17 | } 18 | \description{ 19 | These are developer-facing functions, useful for supporting new model types. 20 | Some models require one or more R packages to be fully attached to make 21 | predictions, and some require only that the namespace of one or more R 22 | packages is loaded. 23 | } 24 | \details{ 25 | These two functions will attempt either to: 26 | \itemize{ 27 | \item fully attach or 28 | \item load 29 | } 30 | 31 | the namespace of the \code{pkgs} vector of package names, preserving the current 32 | random seed. 33 | 34 | To learn more about load vs. attach, read the \href{https://r-pkgs.org/dependencies-mindset-background.html#sec-dependencies-attach-vs-load}{"Dependencies" chapter of \emph{R Packages}}. 35 | For deploying a model, it is likely safer to fully attach needed packages 36 | but that comes with the risk of naming conflicts between packages. 37 | } 38 | \examples{ 39 | ## succeed 40 | load_pkgs(c("knitr", "readr")) 41 | attach_pkgs(c("knitr", "readr")) 42 | 43 | ## fail 44 | try(load_pkgs(c("bloopy", "readr"))) 45 | try(attach_pkgs(c("bloopy", "readr"))) 46 | 47 | } 48 | \concept{namespace} 49 | -------------------------------------------------------------------------------- /man/augment.vetiver_endpoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/endpoint.R 3 | \name{augment.vetiver_endpoint} 4 | \alias{augment.vetiver_endpoint} 5 | \title{Post new data to a deployed model API endpoint and augment with predictions} 6 | \usage{ 7 | \method{augment}{vetiver_endpoint}(x, new_data, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A model API endpoint object created with \code{\link[=vetiver_endpoint]{vetiver_endpoint()}}.} 11 | 12 | \item{new_data}{New data for making predictions, such as a data frame.} 13 | 14 | \item{...}{Extra arguments passed to \code{\link[httr:POST]{httr::POST()}}} 15 | } 16 | \value{ 17 | The \code{new_data} with added prediction column(s). 18 | } 19 | \description{ 20 | Post new data to a deployed model API endpoint and augment with predictions 21 | } 22 | \examples{ 23 | 24 | if (FALSE) { 25 | endpoint <- vetiver_endpoint("http://127.0.0.1:8088/predict") 26 | augment(endpoint, mtcars[4:7, -1]) 27 | } 28 | 29 | } 30 | \seealso{ 31 | \code{\link[=predict.vetiver_endpoint]{predict.vetiver_endpoint()}} 32 | } 33 | -------------------------------------------------------------------------------- /man/augment.vetiver_endpoint_sagemaker.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sagemaker.R 3 | \name{augment.vetiver_endpoint_sagemaker} 4 | \alias{augment.vetiver_endpoint_sagemaker} 5 | \title{Post new data to a deployed SageMaker model endpoint and augment with predictions} 6 | \usage{ 7 | \method{augment}{vetiver_endpoint_sagemaker}(x, new_data, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A SageMaker model endpoint object created with \code{\link[=vetiver_endpoint_sagemaker]{vetiver_endpoint_sagemaker()}}.} 11 | 12 | \item{new_data}{New data for making predictions, such as a data frame.} 13 | 14 | \item{...}{Extra arguments passed to \code{\link[paws.machine.learning:sagemakerruntime_invoke_endpoint]{paws.machine.learning::sagemakerruntime_invoke_endpoint()}}} 15 | } 16 | \value{ 17 | The \code{new_data} with added prediction column(s). 18 | } 19 | \description{ 20 | Post new data to a deployed SageMaker model endpoint and augment with predictions 21 | } 22 | \examples{ 23 | 24 | if (FALSE) { 25 | endpoint <- vetiver_endpoint_sagemaker("sagemaker-demo-model") 26 | augment(endpoint, mtcars[4:7, -1]) 27 | } 28 | 29 | } 30 | \seealso{ 31 | \code{\link[=predict.vetiver_endpoint_sagemaker]{predict.vetiver_endpoint_sagemaker()}} 32 | } 33 | -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: archived 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | archived 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: defunct 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | defunct 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: experimental 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | experimental 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: maturing 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | maturing 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: questioning 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | questioning 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: soft-deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | soft-deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: stable 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | lifecycle 21 | 22 | 25 | 26 | stable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: superseded 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | superseded 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/vetiver-r/54996d51a9df3e157e2e94c60dd6872b43aa44f7/man/figures/logo.png -------------------------------------------------------------------------------- /man/map_request_body.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/open-api-spec.R 3 | \name{map_request_body} 4 | \alias{map_request_body} 5 | \title{Identify data types for each column in an input data prototype} 6 | \usage{ 7 | map_request_body(prototype) 8 | } 9 | \arguments{ 10 | \item{prototype}{An input data prototype from a model} 11 | } 12 | \value{ 13 | A list to be used within \code{\link[plumber:pr_set_api_spec]{plumber::pr_set_api_spec()}} 14 | } 15 | \description{ 16 | The OpenAPI specification of a Plumber API created via \code{\link[plumber:pr]{plumber::pr()}} can 17 | be modified via \code{\link[plumber:pr_set_api_spec]{plumber::pr_set_api_spec()}}, and this helper function will 18 | identify data types of predictors and create a list to use in this 19 | specification. These are \emph{not} R data types, but instead basic JSON data 20 | types. For example, factors in R will be documented as strings in the 21 | OpenAPI specification. 22 | } 23 | \details{ 24 | This is a developer-facing function, useful for supporting new model types. 25 | It is called by \code{\link[=api_spec]{api_spec()}}. 26 | } 27 | \examples{ 28 | map_request_body(vctrs::vec_slice(chickwts, 0)) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /man/predict.vetiver_endpoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/endpoint.R 3 | \name{predict.vetiver_endpoint} 4 | \alias{predict.vetiver_endpoint} 5 | \title{Post new data to a deployed model API endpoint and return predictions} 6 | \usage{ 7 | \method{predict}{vetiver_endpoint}(object, new_data, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A model API endpoint object created with \code{\link[=vetiver_endpoint]{vetiver_endpoint()}}.} 11 | 12 | \item{new_data}{New data for making predictions, such as a data frame.} 13 | 14 | \item{...}{Extra arguments passed to \code{\link[httr:POST]{httr::POST()}}} 15 | } 16 | \value{ 17 | A tibble of model predictions with as many rows as in \code{new_data}. 18 | } 19 | \description{ 20 | Post new data to a deployed model API endpoint and return predictions 21 | } 22 | \examples{ 23 | 24 | if (FALSE) { 25 | endpoint <- vetiver_endpoint("http://127.0.0.1:8088/predict") 26 | predict(endpoint, mtcars[4:7, -1]) 27 | } 28 | 29 | 30 | } 31 | \seealso{ 32 | \code{\link[=augment.vetiver_endpoint]{augment.vetiver_endpoint()}} 33 | } 34 | -------------------------------------------------------------------------------- /man/predict.vetiver_endpoint_sagemaker.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sagemaker.R 3 | \name{predict.vetiver_endpoint_sagemaker} 4 | \alias{predict.vetiver_endpoint_sagemaker} 5 | \title{Post new data to a deployed SageMaker model endpoint and return predictions} 6 | \usage{ 7 | \method{predict}{vetiver_endpoint_sagemaker}(object, new_data, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A SageMaker model endpoint object created with \code{\link[=vetiver_endpoint_sagemaker]{vetiver_endpoint_sagemaker()}}.} 11 | 12 | \item{new_data}{New data for making predictions, such as a data frame.} 13 | 14 | \item{...}{Extra arguments passed to \code{\link[paws.machine.learning:sagemakerruntime_invoke_endpoint]{paws.machine.learning::sagemakerruntime_invoke_endpoint()}}} 15 | } 16 | \value{ 17 | A tibble of model predictions with as many rows as in \code{new_data}. 18 | } 19 | \description{ 20 | Post new data to a deployed SageMaker model endpoint and return predictions 21 | } 22 | \examples{ 23 | if (FALSE) { 24 | endpoint <- vetiver_endpoint_sagemaker("sagemaker-demo-model") 25 | predict(endpoint, mtcars[4:7, -1]) 26 | } 27 | } 28 | \seealso{ 29 | \code{\link[=augment.vetiver_endpoint_sagemaker]{augment.vetiver_endpoint_sagemaker()}} 30 | } 31 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vetiver-package.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{augment} 7 | \alias{required_pkgs} 8 | \title{Objects exported from other packages} 9 | \keyword{internal} 10 | \description{ 11 | These objects are imported from other packages. Follow the links 12 | below to see their documentation. 13 | 14 | \describe{ 15 | \item{generics}{\code{\link[generics]{augment}}, \code{\link[generics]{required_pkgs}}} 16 | }} 17 | 18 | -------------------------------------------------------------------------------- /man/vetiver-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vetiver-package.R 3 | \docType{package} 4 | \name{vetiver-package} 5 | \alias{vetiver} 6 | \alias{vetiver-package} 7 | \title{vetiver: Version, Share, Deploy, and Monitor Models} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | The goal of 'vetiver' is to provide fluent tooling to version, share, deploy, and monitor a trained model. Functions handle both recording and checking the model's input data prototype, and predicting from a remote API endpoint. The 'vetiver' package is extensible, with generics that can support many kinds of models. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{https://vetiver.rstudio.com} 17 | \item \url{https://rstudio.github.io/vetiver-r/} 18 | \item \url{https://github.com/rstudio/vetiver-r/} 19 | \item Report bugs at \url{https://github.com/rstudio/vetiver-r/issues} 20 | } 21 | 22 | } 23 | \author{ 24 | \strong{Maintainer}: Julia Silge \email{julia.silge@posit.co} (\href{https://orcid.org/0000-0002-3671-836X}{ORCID}) 25 | 26 | Other contributors: 27 | \itemize{ 28 | \item Posit Software, PBC [copyright holder, funder] 29 | } 30 | 31 | } 32 | \keyword{internal} 33 | -------------------------------------------------------------------------------- /man/vetiver_api.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/api.R 3 | \name{vetiver_api} 4 | \alias{vetiver_api} 5 | \alias{vetiver_pr_post} 6 | \alias{vetiver_pr_docs} 7 | \title{Create a Plumber API to predict with a deployable \code{vetiver_model()} object} 8 | \usage{ 9 | vetiver_api( 10 | pr, 11 | vetiver_model, 12 | path = "/predict", 13 | debug = is_interactive(), 14 | ... 15 | ) 16 | 17 | vetiver_pr_post( 18 | pr, 19 | vetiver_model, 20 | path = "/predict", 21 | debug = is_interactive(), 22 | ..., 23 | check_prototype = TRUE, 24 | check_ptype = deprecated() 25 | ) 26 | 27 | vetiver_pr_docs(pr, vetiver_model, path = "/predict", all_docs = TRUE) 28 | } 29 | \arguments{ 30 | \item{pr}{A Plumber router, such as from \code{\link[plumber:pr]{plumber::pr()}}.} 31 | 32 | \item{vetiver_model}{A deployable \code{\link[=vetiver_model]{vetiver_model()}} object} 33 | 34 | \item{path}{The endpoint path} 35 | 36 | \item{debug}{\code{TRUE} provides more insight into your API errors.} 37 | 38 | \item{...}{Other arguments passed to \code{predict()}, such as prediction \code{type}} 39 | 40 | \item{check_prototype}{Should the input data prototype stored in 41 | \code{vetiver_model} (used for visual API documentation) also be used to check 42 | new data at prediction time? Defaults to \code{TRUE}.} 43 | 44 | \item{check_ptype}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}} 45 | 46 | \item{all_docs}{Should the interactive visual API documentation be created 47 | for \emph{all} POST endpoints in the router \code{pr}? This defaults to \code{TRUE}, and 48 | assumes that all POST endpoints use the \code{\link[=vetiver_model]{vetiver_model()}} input data 49 | prototype.} 50 | } 51 | \value{ 52 | A Plumber router with the prediction endpoint added. 53 | } 54 | \description{ 55 | Use \code{vetiver_api()} to add a POST endpoint for predictions from a 56 | trained \code{\link[=vetiver_model]{vetiver_model()}} to a Plumber router. 57 | } 58 | \details{ 59 | You can first store and version your \code{\link[=vetiver_model]{vetiver_model()}} with 60 | \code{\link[=vetiver_pin_write]{vetiver_pin_write()}}, and then create an API endpoint with \code{vetiver_api()}. 61 | 62 | Setting \code{debug = TRUE} may expose any sensitive data from your model in 63 | API errors. 64 | 65 | Several GET endpoints will also be added to the router \code{pr}, depending on the 66 | characteristics of the model object: 67 | \itemize{ 68 | \item a \verb{/pin-url} endpoint to return the URL of the pinned model 69 | \item a \verb{/metadata} endpoint to return any metadata stored with the model 70 | \item a \verb{/ping} endpoint for the API health 71 | \item a \verb{/prototype} endpoint for the model's input data prototype (use 72 | \code{\link[cereal:cereal_to_json]{cereal::cereal_from_json()}}) to convert this back to a \href{https://vctrs.r-lib.org/articles/type-size.html}{vctrs ptype} 73 | } 74 | 75 | The function \code{vetiver_api()} uses: 76 | \itemize{ 77 | \item \code{vetiver_pr_post()} for endpoint definition and 78 | \item \code{vetiver_pr_docs()} to create visual API documentation 79 | } 80 | 81 | These modular functions are available for more advanced use cases. 82 | } 83 | \examples{ 84 | \dontshow{if (rlang::is_installed("plumber")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 85 | 86 | cars_lm <- lm(mpg ~ ., data = mtcars) 87 | v <- vetiver_model(cars_lm, "cars_linear") 88 | 89 | library(plumber) 90 | pr() \%>\% vetiver_api(v) 91 | ## is the same as: 92 | pr() \%>\% vetiver_pr_post(v) \%>\% vetiver_pr_docs(v) 93 | ## for either, next, pipe to `pr_run()` 94 | \dontshow{\}) # examplesIf} 95 | } 96 | -------------------------------------------------------------------------------- /man/vetiver_create_meta.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/caret.R, R/gam.R, R/keras.R, R/kproto.R, 3 | % R/luz.R, R/meta.R, R/mlr3.R, R/ranger.R, R/recipe.R, R/stacks.R, 4 | % R/tidymodels.R, R/xgboost.R 5 | \name{vetiver_create_meta.train} 6 | \alias{vetiver_create_meta.train} 7 | \alias{vetiver_create_meta.gam} 8 | \alias{vetiver_create_meta.keras.engine.training.Model} 9 | \alias{vetiver_create_meta.kproto} 10 | \alias{vetiver_create_meta.luz_module_fitted} 11 | \alias{vetiver_meta} 12 | \alias{vetiver_create_meta} 13 | \alias{vetiver_create_meta.default} 14 | \alias{vetiver_create_meta.Learner} 15 | \alias{vetiver_create_meta.ranger} 16 | \alias{vetiver_create_meta.recipe} 17 | \alias{vetiver_create_meta.model_stack} 18 | \alias{vetiver_create_meta.workflow} 19 | \alias{vetiver_create_meta.xgb.Booster} 20 | \title{Metadata constructors for \code{vetiver_model()} object} 21 | \usage{ 22 | \method{vetiver_create_meta}{train}(model, metadata) 23 | 24 | \method{vetiver_create_meta}{gam}(model, metadata) 25 | 26 | \method{vetiver_create_meta}{keras.engine.training.Model}(model, metadata) 27 | 28 | \method{vetiver_create_meta}{kproto}(model, metadata) 29 | 30 | \method{vetiver_create_meta}{luz_module_fitted}(model, metadata) 31 | 32 | vetiver_meta(user = list(), version = NULL, url = NULL, required_pkgs = NULL) 33 | 34 | vetiver_create_meta(model, metadata) 35 | 36 | \method{vetiver_create_meta}{default}(model, metadata) 37 | 38 | \method{vetiver_create_meta}{Learner}(model, metadata) 39 | 40 | \method{vetiver_create_meta}{ranger}(model, metadata) 41 | 42 | \method{vetiver_create_meta}{recipe}(model, metadata) 43 | 44 | \method{vetiver_create_meta}{model_stack}(model, metadata) 45 | 46 | \method{vetiver_create_meta}{workflow}(model, metadata) 47 | 48 | \method{vetiver_create_meta}{xgb.Booster}(model, metadata) 49 | } 50 | \arguments{ 51 | \item{model}{A trained model, such as an \code{lm()} model or a tidymodels 52 | \code{\link[workflows:workflow]{workflows::workflow()}}.} 53 | 54 | \item{metadata}{A list containing additional metadata to store with the pin. 55 | When retrieving the pin, this will be stored in the \code{user} key, to 56 | avoid potential clashes with the metadata that pins itself uses.} 57 | 58 | \item{user}{Metadata supplied by the user} 59 | 60 | \item{version}{Version of the pin} 61 | 62 | \item{url}{URL for the pin, if any} 63 | 64 | \item{required_pkgs}{Character string of R packages required for prediction} 65 | } 66 | \value{ 67 | The \code{vetiver_meta()} constructor returns a list. The 68 | \code{vetiver_create_meta} function returns a \code{vetiver_meta()} list. 69 | } 70 | \description{ 71 | These are developer-facing functions, useful for supporting new model types. 72 | The metadata stored in a \code{\link[=vetiver_model]{vetiver_model()}} object has four elements: 73 | \itemize{ 74 | \item \verb{$user}, the metadata supplied by the user 75 | \item \verb{$version}, the version of the pin (which can be \code{NULL} before pinning) 76 | \item \verb{$url}, the URL where the pin is located, if any 77 | \item \verb{$required_pkgs}, a character string of R packages required for prediction 78 | } 79 | } 80 | \examples{ 81 | vetiver_meta() 82 | 83 | cars_lm <- lm(mpg ~ ., data = mtcars) 84 | vetiver_create_meta(cars_lm, list()) 85 | 86 | } 87 | -------------------------------------------------------------------------------- /man/vetiver_create_rsconnect_bundle.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rsconnect.R 3 | \name{vetiver_create_rsconnect_bundle} 4 | \alias{vetiver_create_rsconnect_bundle} 5 | \title{Create an Posit Connect bundle for a vetiver model API} 6 | \usage{ 7 | vetiver_create_rsconnect_bundle( 8 | board, 9 | name, 10 | version = NULL, 11 | predict_args = list(), 12 | filename = fs::file_temp(pattern = "bundle", ext = ".tar.gz"), 13 | additional_pkgs = character(0) 14 | ) 15 | } 16 | \arguments{ 17 | \item{board}{A pin board, created by \code{\link[pins:board_folder]{board_folder()}}, \code{\link[pins:board_connect]{board_connect()}}, 18 | \code{\link[pins:board_url]{board_url()}} or another \code{board_} function.} 19 | 20 | \item{name}{Pin name.} 21 | 22 | \item{version}{Retrieve a specific version of a pin. Use \code{\link[pins:pin_versions]{pin_versions()}} to 23 | find out which versions are available and when they were created.} 24 | 25 | \item{predict_args}{A list of optional arguments passed to \code{\link[=vetiver_api]{vetiver_api()}} 26 | such as the prediction \code{type}.} 27 | 28 | \item{filename}{The path for the model API bundle to be created (can be 29 | used as the argument to \code{connectapi::bundle_path()})} 30 | 31 | \item{additional_pkgs}{Any additional R packages that need to be \strong{attached} 32 | via \code{\link[=library]{library()}} to run your API, as a character vector.} 33 | } 34 | \value{ 35 | The location of the model API bundle \code{filename}, invisibly. 36 | } 37 | \description{ 38 | Use \code{vetiver_create_rsconnect_bundle()} to create a 39 | \href{https://docs.posit.co/connect/}{Posit Connect} model API bundle for a 40 | \code{\link[=vetiver_model]{vetiver_model()}} that has been versioned and stored via 41 | \code{\link[=vetiver_pin_write]{vetiver_pin_write()}}. 42 | } 43 | \details{ 44 | This function creates a deployable bundle. See 45 | \href{https://docs.posit.co/connect/cookbook/deploying/}{Posit Connect docs} 46 | for how to deploy this bundle, as well as the 47 | \href{https://pkgs.rstudio.com/connectapi/}{connectapi} R package for how to 48 | integrate with Connect's API from R. 49 | 50 | The two functions \code{vetiver_create_rsconnect_bundle()} and 51 | \code{\link[=vetiver_deploy_rsconnect]{vetiver_deploy_rsconnect()}} are alternatives to each other, providing 52 | different strategies for deploying a vetiver model API to Posit Connect. 53 | } 54 | \examples{ 55 | \dontshow{if (rlang::is_installed("connectapi") && identical(Sys.getenv("NOT_CRAN"), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 56 | library(pins) 57 | b <- board_temp(versioned = TRUE) 58 | cars_lm <- lm(mpg ~ ., data = mtcars) 59 | v <- vetiver_model(cars_lm, "cars_linear") 60 | vetiver_pin_write(b, v) 61 | 62 | ## when you pin to Posit Connect, your pin name will be typically be like: 63 | ## "user.name/cars_linear" 64 | vetiver_create_rsconnect_bundle( 65 | b, 66 | "cars_linear", 67 | predict_args = list(debug = TRUE) 68 | ) 69 | \dontshow{\}) # examplesIf} 70 | } 71 | \seealso{ 72 | \code{\link[=vetiver_write_plumber]{vetiver_write_plumber()}}, \code{\link[=vetiver_deploy_rsconnect]{vetiver_deploy_rsconnect()}} 73 | } 74 | -------------------------------------------------------------------------------- /man/vetiver_dashboard.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dashboard.R 3 | \name{vetiver_dashboard} 4 | \alias{vetiver_dashboard} 5 | \alias{get_vetiver_dashboard_pins} 6 | \alias{pin_example_kc_housing_model} 7 | \title{R Markdown format for model monitoring dashboards} 8 | \usage{ 9 | vetiver_dashboard(pins = list(), display_pins = TRUE, ...) 10 | 11 | get_vetiver_dashboard_pins() 12 | 13 | pin_example_kc_housing_model(board = pins::board_local(), name = "seattle_rf") 14 | } 15 | \arguments{ 16 | \item{pins}{A list containing \code{board}, \code{name}, and \code{version}, as in 17 | \code{\link[pins:pin_read]{pins::pin_read()}}} 18 | 19 | \item{display_pins}{Should the dashboard display a link to the pin(s)? 20 | Defaults to \code{TRUE}, but only creates a link if the pin contains a URL in 21 | its metadata.} 22 | 23 | \item{...}{Arguments passed to \code{\link[flexdashboard:flex_dashboard]{flexdashboard::flex_dashboard()}}} 24 | 25 | \item{board}{A pin board, created by \code{\link[pins:board_folder]{board_folder()}}, \code{\link[pins:board_connect]{board_connect()}}, 26 | \code{\link[pins:board_url]{board_url()}} or another \code{board_} function.} 27 | 28 | \item{name}{Pin name.} 29 | } 30 | \description{ 31 | R Markdown format for model monitoring dashboards 32 | } 33 | \details{ 34 | The \code{vetiver_dashboard()} function is a specialized type of 35 | \pkg{flexdashboard}. See the flexdashboard website for additional 36 | documentation: 37 | \href{https://pkgs.rstudio.com/flexdashboard/}{https://pkgs.rstudio.com/flexdashboard/} 38 | 39 | Before knitting the example \code{vetiver_dashboard()} template, execute the 40 | helper function \code{pin_example_kc_housing_model()} to set up demonstration 41 | model and metrics pins needed for the monitoring demo. This function will: 42 | \itemize{ 43 | \item fit an example model to training data 44 | \item pin the vetiver model to your own \code{\link[pins:board_folder]{pins::board_local()}} 45 | \item compute metrics from testing data 46 | \item pin these metrics to the same local board 47 | } 48 | 49 | These are the steps you need to complete before setting up monitoring your 50 | real model. 51 | } 52 | -------------------------------------------------------------------------------- /man/vetiver_deploy_rsconnect.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rsconnect.R 3 | \name{vetiver_deploy_rsconnect} 4 | \alias{vetiver_deploy_rsconnect} 5 | \title{Deploy a vetiver model API to Posit Connect} 6 | \usage{ 7 | vetiver_deploy_rsconnect( 8 | board, 9 | name, 10 | version = NULL, 11 | predict_args = list(), 12 | appTitle = glue::glue("{name} model API"), 13 | ..., 14 | additional_pkgs = character(0) 15 | ) 16 | } 17 | \arguments{ 18 | \item{board}{A pin board, created by \code{\link[pins:board_folder]{board_folder()}}, \code{\link[pins:board_connect]{board_connect()}}, 19 | \code{\link[pins:board_url]{board_url()}} or another \code{board_} function.} 20 | 21 | \item{name}{Pin name.} 22 | 23 | \item{version}{Retrieve a specific version of a pin. Use \code{\link[pins:pin_versions]{pin_versions()}} to 24 | find out which versions are available and when they were created.} 25 | 26 | \item{predict_args}{A list of optional arguments passed to \code{\link[=vetiver_api]{vetiver_api()}} 27 | such as the prediction \code{type}.} 28 | 29 | \item{appTitle}{The API title on Posit Connect. Use the default based on 30 | \code{name}, or pass in your own title.} 31 | 32 | \item{...}{Other arguments passed to \code{\link[rsconnect:deployApp]{rsconnect::deployApp()}} such as 33 | \code{appName}, \code{account}, or \code{launch.browser}.} 34 | 35 | \item{additional_pkgs}{Any additional R packages that need to be \strong{attached} 36 | via \code{\link[=library]{library()}} to run your API, as a character vector.} 37 | } 38 | \value{ 39 | The deployment success (\code{TRUE} or \code{FALSE}), invisibly. 40 | } 41 | \description{ 42 | Use \code{vetiver_deploy_rsconnect()} to deploy a \code{\link[=vetiver_model]{vetiver_model()}} that has been 43 | versioned and stored via \code{\link[=vetiver_pin_write]{vetiver_pin_write()}} as a Plumber API on \href{https://docs.posit.co/connect/}{Posit Connect}. 44 | } 45 | \details{ 46 | The two functions \code{vetiver_deploy_rsconnect()} and 47 | \code{\link[=vetiver_create_rsconnect_bundle]{vetiver_create_rsconnect_bundle()}} are alternatives to each other, providing 48 | different strategies for deploying a vetiver model API to Posit Connect. 49 | 50 | When you first deploy to Connect, your API will only be accessible to you. 51 | You can \href{https://docs.posit.co/connect/user/content-settings/#set-viewers}{change the access settings} 52 | so others can also access the API. For all access settings other than 53 | "Anyone - no login required", anyone querying your API (including you) 54 | will need to pass authentication details with your API call, 55 | \href{https://docs.posit.co/connect/user/vetiver/#predict-from-your-model-endpoint}{as shown in the Connect documentation}. 56 | } 57 | \examples{ 58 | \dontshow{if (rlang::is_installed("rsconnect")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 59 | library(pins) 60 | b <- board_temp(versioned = TRUE) 61 | cars_lm <- lm(mpg ~ ., data = mtcars) 62 | v <- vetiver_model(cars_lm, "cars_linear") 63 | vetiver_pin_write(b, v) 64 | 65 | if (FALSE) { 66 | ## pass args for predicting: 67 | vetiver_deploy_rsconnect( 68 | b, 69 | "user.name/cars_linear", 70 | predict_args = list(debug = TRUE) 71 | ) 72 | 73 | ## specify an account name through `...`: 74 | vetiver_deploy_rsconnect( 75 | b, 76 | "user.name/cars_linear", 77 | account = "user.name" 78 | ) 79 | } 80 | \dontshow{\}) # examplesIf} 81 | } 82 | \seealso{ 83 | \code{\link[=vetiver_write_plumber]{vetiver_write_plumber()}}, \code{\link[=vetiver_create_rsconnect_bundle]{vetiver_create_rsconnect_bundle()}} 84 | } 85 | -------------------------------------------------------------------------------- /man/vetiver_deploy_sagemaker.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sagemaker.R 3 | \name{vetiver_deploy_sagemaker} 4 | \alias{vetiver_deploy_sagemaker} 5 | \title{Deploy a vetiver model API to Amazon SageMaker} 6 | \usage{ 7 | vetiver_deploy_sagemaker( 8 | board, 9 | name, 10 | instance_type, 11 | ..., 12 | predict_args = list(), 13 | docker_args = list(), 14 | build_args = list(), 15 | endpoint_args = list(), 16 | repo_name = glue("vetiver-sagemaker-{name}") 17 | ) 18 | } 19 | \arguments{ 20 | \item{board}{An AWS S3 board created with \code{\link[pins:board_s3]{pins::board_s3()}}. This board 21 | must be in the correct region for your SageMaker instance.} 22 | 23 | \item{name}{Pin name.} 24 | 25 | \item{instance_type}{Type of EC2 instance to use; see 26 | \href{https://aws.amazon.com/sagemaker/pricing/}{Amazon SageMaker pricing}.} 27 | 28 | \item{...}{Not currently used.} 29 | 30 | \item{predict_args}{A list of optional arguments passed to \code{\link[=vetiver_api]{vetiver_api()}} 31 | such as the prediction \code{type}.} 32 | 33 | \item{docker_args}{A list of optional arguments passed to 34 | \code{\link[=vetiver_write_docker]{vetiver_write_docker()}} such as the \code{lockfile} name or whether to use 35 | \code{rspm}. Do not pass \code{additional_pkgs} here, as this function uses 36 | \code{additional_pkgs = required_pkgs(board)}.} 37 | 38 | \item{build_args}{A list of optional arguments passed to 39 | \code{\link[=vetiver_sm_build]{vetiver_sm_build()}} such as the model \code{version} or the \code{compute_type}.} 40 | 41 | \item{endpoint_args}{A list of optional arguments passed to 42 | \code{\link[=vetiver_sm_endpoint]{vetiver_sm_endpoint()}} such as \code{accelerator_type} or \code{data_capture_config}.} 43 | 44 | \item{repo_name}{The name for the AWS ECR repository to store the model.} 45 | } 46 | \value{ 47 | The deployed \code{\link[=vetiver_endpoint_sagemaker]{vetiver_endpoint_sagemaker()}}. 48 | } 49 | \description{ 50 | Use \code{vetiver_deploy_sagemaker()} to deploy a \code{\link[=vetiver_model]{vetiver_model()}} 51 | that has been versioned and stored via \code{\link[=vetiver_pin_write]{vetiver_pin_write()}} as a Plumber API 52 | on \href{https://aws.amazon.com/sagemaker/}{Amazon SageMaker}. 53 | } 54 | \details{ 55 | This function stores your model deployment image in the same bucket used 56 | by \code{board}. 57 | 58 | The function \code{vetiver_deploy_sagemaker()} uses: 59 | \itemize{ 60 | \item \code{\link[=vetiver_sm_build]{vetiver_sm_build()}} to build and push a Docker image to ECR, 61 | \item \code{\link[=vetiver_sm_model]{vetiver_sm_model()}} to create a SageMaker model, and 62 | \item \code{\link[=vetiver_sm_endpoint]{vetiver_sm_endpoint()}} to deploy a SageMaker model endpoint. 63 | } 64 | 65 | These modular functions are available for more advanced use cases. 66 | 67 | If you are working locally, you will likely need to explicitly set up 68 | your execution role to work correctly. Check out 69 | \href{https://dyfanjones.r-universe.dev/smdocker}{"Execution role requirements"} 70 | in the smdocker documentation, and especially note that the bucket containing 71 | your vetiver model needs to be added as a resource in your IAM role policy. 72 | } 73 | \examples{ 74 | if (FALSE) { 75 | library(pins) 76 | b <- board_s3(bucket = "my-existing-bucket") 77 | cars_lm <- lm(mpg ~ ., data = mtcars) 78 | v <- vetiver_model(cars_lm, "cars_linear") 79 | vetiver_pin_write(b, v) 80 | 81 | endpoint <- vetiver_deploy_sagemaker( 82 | board = b, 83 | name = "cars_linear", 84 | instance_type = "ml.t2.medium", 85 | predict_args = list(type = "class", debug = TRUE) 86 | ) 87 | } 88 | 89 | } 90 | \seealso{ 91 | \code{\link[=vetiver_sm_build]{vetiver_sm_build()}}, \code{\link[=vetiver_sm_model]{vetiver_sm_model()}}, \code{\link[=vetiver_sm_endpoint]{vetiver_sm_endpoint()}} 92 | } 93 | -------------------------------------------------------------------------------- /man/vetiver_endpoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/endpoint.R 3 | \name{vetiver_endpoint} 4 | \alias{vetiver_endpoint} 5 | \title{Create a model API endpoint object for prediction} 6 | \usage{ 7 | vetiver_endpoint(url) 8 | } 9 | \arguments{ 10 | \item{url}{An API endpoint URL} 11 | } 12 | \value{ 13 | A new \code{vetiver_endpoint} object 14 | } 15 | \description{ 16 | This function creates a model API endpoint for prediction from a URL. No 17 | HTTP calls are made until you actually 18 | \code{\link[=predict.vetiver_endpoint]{predict()}} with your endpoint. 19 | } 20 | \examples{ 21 | vetiver_endpoint("https://colorado.rstudio.com/rsc/seattle-housing/predict") 22 | 23 | } 24 | -------------------------------------------------------------------------------- /man/vetiver_endpoint_sagemaker.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sagemaker.R 3 | \name{vetiver_endpoint_sagemaker} 4 | \alias{vetiver_endpoint_sagemaker} 5 | \title{Create a SageMaker model API endpoint object for prediction} 6 | \usage{ 7 | vetiver_endpoint_sagemaker(model_endpoint) 8 | } 9 | \arguments{ 10 | \item{model_endpoint}{The name of the Amazon SageMaker model endpoint.} 11 | } 12 | \value{ 13 | A new \code{vetiver_endpoint_sagemaker} object 14 | } 15 | \description{ 16 | This function creates a model API endpoint for prediction from a Sagemaker Model. 17 | No HTTP calls are made until you actually 18 | \code{\link[=predict.vetiver_endpoint_sagemaker]{predict()}} with your endpoint. 19 | } 20 | \examples{ 21 | \dontshow{if (rlang::is_installed("smdocker")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 22 | vetiver_endpoint_sagemaker("vetiver-sagemaker-demo-model") 23 | \dontshow{\}) # examplesIf} 24 | } 25 | -------------------------------------------------------------------------------- /man/vetiver_model.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vetiver-model.R 3 | \name{vetiver_model} 4 | \alias{vetiver_model} 5 | \alias{new_vetiver_model} 6 | \title{Create a vetiver object for deployment of a trained model} 7 | \usage{ 8 | vetiver_model( 9 | model, 10 | model_name, 11 | ..., 12 | description = NULL, 13 | metadata = list(), 14 | save_prototype = TRUE, 15 | save_ptype = deprecated(), 16 | versioned = NULL 17 | ) 18 | 19 | new_vetiver_model( 20 | model, 21 | model_name, 22 | description, 23 | metadata, 24 | prototype, 25 | versioned 26 | ) 27 | } 28 | \arguments{ 29 | \item{model}{A trained model, such as an \code{lm()} model or a tidymodels 30 | \code{\link[workflows:workflow]{workflows::workflow()}}.} 31 | 32 | \item{model_name}{Model name or ID.} 33 | 34 | \item{...}{Other method-specific arguments passed to \code{\link[=vetiver_ptype]{vetiver_ptype()}} 35 | to compute an input data prototype, such as \code{prototype_data} (a sample of 36 | training features).} 37 | 38 | \item{description}{A detailed description of the model. If omitted, a brief 39 | description of the model will be generated.} 40 | 41 | \item{metadata}{A list containing additional metadata to store with the pin. 42 | When retrieving the pin, this will be stored in the \code{user} key, to 43 | avoid potential clashes with the metadata that pins itself uses.} 44 | 45 | \item{save_prototype}{Should an input data prototype be stored with the model? 46 | The options are \code{TRUE} (the default, which stores a zero-row slice of the 47 | training data), \code{FALSE} (no input data prototype for visual documentation or 48 | checking), or a dataframe to be used for both checking at prediction time 49 | \emph{and} examples in API visual documentation.} 50 | 51 | \item{save_ptype}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}} 52 | 53 | \item{versioned}{Should the model object be versioned when stored with 54 | \code{\link[=vetiver_pin_write]{vetiver_pin_write()}}? The default, \code{NULL}, will use the default for the 55 | \code{board} where you store the model.} 56 | 57 | \item{prototype}{An input data prototype. If \code{NULL}, there is no checking of 58 | new data at prediction time.} 59 | } 60 | \value{ 61 | A new \code{vetiver_model} object. 62 | } 63 | \description{ 64 | A \code{vetiver_model()} object collects the information needed to store, version, 65 | and deploy a trained model. Once your \code{vetiver_model()} object has been 66 | created, you can: 67 | \itemize{ 68 | \item store and version it as a pin with \code{\link[=vetiver_pin_write]{vetiver_pin_write()}} 69 | \item create an API endpoint for it with \code{\link[=vetiver_api]{vetiver_api()}} 70 | } 71 | } 72 | \details{ 73 | You can provide your own data to \code{save_prototype} to use as examples in the 74 | visual documentation created by \code{\link[=vetiver_api]{vetiver_api()}}. If you do this, 75 | consider checking that your input data prototype has the same structure 76 | as your training data (perhaps with \code{\link[hardhat:scream]{hardhat::scream()}}) and/or simulating 77 | data to avoid leaking PII via your deployed model. 78 | 79 | Some models, like \code{\link[ranger:ranger]{ranger::ranger()}}, \href{https://tensorflow.rstudio.com/}{keras}, 80 | and \href{https://torch.mlverse.org/}{luz (torch)}, 81 | \emph{require} that you pass in example training data as \code{prototype_data} 82 | or else explicitly set \code{save_prototype = FALSE}. For non-rectangular data 83 | input to models, such as image input for a keras or torch model, we currently 84 | recommend that you turn off prototype checking via \code{save_prototype = FALSE}. 85 | } 86 | \examples{ 87 | 88 | cars_lm <- lm(mpg ~ ., data = mtcars) 89 | vetiver_model(cars_lm, "cars-linear") 90 | 91 | } 92 | -------------------------------------------------------------------------------- /man/vetiver_pin_write.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/pin-read-write.R 3 | \name{vetiver_pin_write} 4 | \alias{vetiver_pin_write} 5 | \alias{vetiver_pin_read} 6 | \title{Read and write a trained model to a board of models} 7 | \usage{ 8 | vetiver_pin_write(board, vetiver_model, ..., check_renv = FALSE) 9 | 10 | vetiver_pin_read(board, name, version = NULL, check_renv = FALSE) 11 | } 12 | \arguments{ 13 | \item{board}{A pin board, created by \code{\link[pins:board_folder]{board_folder()}}, \code{\link[pins:board_connect]{board_connect()}}, 14 | \code{\link[pins:board_url]{board_url()}} or another \code{board_} function.} 15 | 16 | \item{vetiver_model}{A deployable \code{\link[=vetiver_model]{vetiver_model()}} object} 17 | 18 | \item{...}{Additional arguments passed on to methods for a specific board.} 19 | 20 | \item{check_renv}{Use \href{https://rstudio.github.io/renv/}{renv} to record the 21 | packages used at training time with \code{vetiver_pin_write()} and check for 22 | differences with \code{vetiver_pin_read()}. Defaults to \code{FALSE}.} 23 | 24 | \item{name}{Pin name.} 25 | 26 | \item{version}{Retrieve a specific version of a pin. Use \code{\link[pins:pin_versions]{pin_versions()}} to 27 | find out which versions are available and when they were created.} 28 | } 29 | \value{ 30 | \code{vetiver_pin_read()} returns a \code{\link[=vetiver_model]{vetiver_model()}}; \code{vetiver_pin_write()} 31 | returns the name of the new pin, invisibly. 32 | } 33 | \description{ 34 | Use \code{vetiver_pin_write()} to pin a trained model to a board of models, 35 | along with an input prototype for new data and other model metadata. Use 36 | \code{vetiver_pin_read()} to retrieve that pinned object. 37 | } 38 | \details{ 39 | These functions read and write a \code{\link[=vetiver_model]{vetiver_model()}} pin on the 40 | specified \code{board} containing the model object itself and other elements 41 | needed for prediction, such as the model's input data prototype or which 42 | packages are needed at prediction time. You may use \code{\link[pins:pin_read]{pins::pin_read()}} or 43 | \code{\link[pins:pin_meta]{pins::pin_meta()}} to handle the pin, but \code{vetiver_pin_read()} returns a 44 | \code{\link[=vetiver_model]{vetiver_model()}} object ready for deployment. 45 | } 46 | \examples{ 47 | library(pins) 48 | model_board <- board_temp() 49 | 50 | cars_lm <- lm(mpg ~ ., data = mtcars) 51 | v <- vetiver_model(cars_lm, "cars_linear") 52 | vetiver_pin_write(model_board, v) 53 | model_board 54 | 55 | vetiver_pin_read(model_board, "cars_linear") 56 | 57 | # can use `version` argument to read a specific version: 58 | pin_versions(model_board, "cars_linear") 59 | \dontshow{if (interactive() || identical(Sys.getenv("IN_PKGDOWN"), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 60 | # can store an renv lockfile as part of the pin: 61 | vetiver_pin_write(model_board, v, check_renv = TRUE) 62 | \dontshow{\}) # examplesIf} 63 | } 64 | -------------------------------------------------------------------------------- /man/vetiver_plot_metrics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/monitor.R 3 | \name{vetiver_plot_metrics} 4 | \alias{vetiver_plot_metrics} 5 | \title{Plot model metrics over time for monitoring} 6 | \usage{ 7 | vetiver_plot_metrics( 8 | df_metrics, 9 | .index = .index, 10 | .estimate = .estimate, 11 | .metric = .metric, 12 | .n = .n 13 | ) 14 | } 15 | \arguments{ 16 | \item{df_metrics}{A tidy dataframe of metrics over time, such as created by 17 | \code{\link[=vetiver_compute_metrics]{vetiver_compute_metrics()}}.} 18 | 19 | \item{.index}{The variable in \code{df_metrics} containing the aggregated dates 20 | or date-times (from \code{time_var} in \code{data}). Defaults to \code{.index}.} 21 | 22 | \item{.estimate}{The variable in \code{df_metrics} containing the metric estimate. 23 | Defaults to \code{.estimate}.} 24 | 25 | \item{.metric}{The variable in \code{df_metrics} containing the metric type. 26 | Defaults to \code{.metric}.} 27 | 28 | \item{.n}{The variable in \code{df_metrics} containing the number of observations 29 | used for estimating the metric.} 30 | } 31 | \value{ 32 | A \code{ggplot2} object. 33 | } 34 | \description{ 35 | These three functions can be used for model monitoring (such as in a 36 | monitoring dashboard): 37 | \itemize{ 38 | \item \code{\link[=vetiver_compute_metrics]{vetiver_compute_metrics()}} computes metrics (such as accuracy for a 39 | classification model or RMSE for a regression model) at a chosen time 40 | aggregation \code{period} 41 | \item \code{\link[=vetiver_pin_metrics]{vetiver_pin_metrics()}} updates an existing pin storing model metrics 42 | over time 43 | \item \code{vetiver_plot_metrics()} creates a plot of metrics over time 44 | } 45 | } 46 | \examples{ 47 | \dontshow{if (rlang::is_installed(c("dplyr", "parsnip", "modeldata", "arrow", "ggplot2"))) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 48 | library(dplyr) 49 | library(parsnip) 50 | data(Chicago, package = "modeldata") 51 | Chicago <- Chicago \%>\% select(ridership, date, all_of(stations)) 52 | training_data <- Chicago \%>\% filter(date < "2009-01-01") 53 | testing_data <- Chicago \%>\% filter(date >= "2009-01-01", date < "2011-01-01") 54 | monitoring <- Chicago \%>\% filter(date >= "2011-01-01", date < "2012-12-31") 55 | lm_fit <- linear_reg() \%>\% fit(ridership ~ ., data = training_data) 56 | 57 | library(pins) 58 | b <- board_temp() 59 | 60 | ## before starting monitoring, initiate the metrics and pin 61 | ## (for example, with the testing data): 62 | original_metrics <- 63 | augment(lm_fit, new_data = testing_data) \%>\% 64 | vetiver_compute_metrics(date, "week", ridership, .pred, every = 4L) 65 | pin_write(b, original_metrics, "lm_fit_metrics", type = "arrow") 66 | 67 | ## to continue monitoring with new data, compute metrics and update pin: 68 | new_metrics <- 69 | augment(lm_fit, new_data = monitoring) \%>\% 70 | vetiver_compute_metrics(date, "week", ridership, .pred, every = 4L) 71 | vetiver_pin_metrics(b, new_metrics, "lm_fit_metrics") 72 | 73 | library(ggplot2) 74 | vetiver_plot_metrics(new_metrics) + 75 | scale_size(range = c(2, 4)) 76 | \dontshow{\}) # examplesIf} 77 | } 78 | \seealso{ 79 | \code{\link[=vetiver_compute_metrics]{vetiver_compute_metrics()}}, \code{\link[=vetiver_pin_metrics]{vetiver_pin_metrics()}} 80 | } 81 | -------------------------------------------------------------------------------- /man/vetiver_pr_predict.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/api.R 3 | \name{vetiver_pr_predict} 4 | \alias{vetiver_pr_predict} 5 | \title{Create a Plumber API to predict with a deployable \code{vetiver_model()} object} 6 | \usage{ 7 | vetiver_pr_predict( 8 | pr, 9 | vetiver_model, 10 | path = "/predict", 11 | debug = is_interactive(), 12 | ... 13 | ) 14 | } 15 | \arguments{ 16 | \item{pr}{A Plumber router, such as from \code{\link[plumber:pr]{plumber::pr()}}.} 17 | 18 | \item{vetiver_model}{A deployable \code{\link[=vetiver_model]{vetiver_model()}} object} 19 | 20 | \item{path}{The endpoint path} 21 | 22 | \item{debug}{\code{TRUE} provides more insight into your API errors.} 23 | 24 | \item{...}{Other arguments passed to \code{predict()}, such as prediction \code{type}} 25 | } 26 | \description{ 27 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 28 | 29 | This function was deprecated to use \link{vetiver_api} directly instead. 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /man/vetiver_prepare_docker.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/write-docker.R 3 | \name{vetiver_prepare_docker} 4 | \alias{vetiver_prepare_docker} 5 | \title{Generate files necessary to build a Docker container for a vetiver model} 6 | \usage{ 7 | vetiver_prepare_docker( 8 | board, 9 | name, 10 | version = NULL, 11 | path = ".", 12 | predict_args = list(), 13 | docker_args = list() 14 | ) 15 | } 16 | \arguments{ 17 | \item{board}{A pin board, created by \code{\link[pins:board_folder]{board_folder()}}, \code{\link[pins:board_connect]{board_connect()}}, 18 | \code{\link[pins:board_url]{board_url()}} or another \code{board_} function.} 19 | 20 | \item{name}{Pin name.} 21 | 22 | \item{version}{Retrieve a specific version of a pin. Use \code{\link[pins:pin_versions]{pin_versions()}} to 23 | find out which versions are available and when they were created.} 24 | 25 | \item{path}{A path to write the Plumber file, Dockerfile, and lockfile, 26 | capturing the model's dependencies.} 27 | 28 | \item{predict_args}{A list of optional arguments passed to \code{\link[=vetiver_api]{vetiver_api()}} 29 | such as the prediction \code{type}.} 30 | 31 | \item{docker_args}{A list of optional arguments passed to 32 | \code{\link[=vetiver_write_docker]{vetiver_write_docker()}} such as the \code{lockfile} name or whether to use 33 | \code{rspm}. Do not pass \code{additional_pkgs} here, as this function uses 34 | \code{additional_pkgs = required_pkgs(board)}.} 35 | } 36 | \value{ 37 | An invisible \code{TRUE}. 38 | } 39 | \description{ 40 | Deploying a vetiver model via Docker requires several files. Use this 41 | function to create these needed files in the directory located at \code{path}. 42 | } 43 | \details{ 44 | The function \code{vetiver_prepare_docker()} uses: 45 | \itemize{ 46 | \item \code{\link[=vetiver_write_plumber]{vetiver_write_plumber()}} to create a Plumber file and 47 | \item \code{\link[=vetiver_write_docker]{vetiver_write_docker()}} to create a Dockerfile and renv lockfile 48 | } 49 | 50 | These modular functions are available for more advanced use cases. For 51 | models such as keras and torch, you will need to edit the generated 52 | Dockerfile to, for example, \verb{COPY requirements.txt requirements.txt} or 53 | similar. 54 | } 55 | \examples{ 56 | \dontshow{if (interactive() || identical(Sys.getenv("IN_PKGDOWN"), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 57 | library(pins) 58 | b <- board_temp(versioned = TRUE) 59 | cars_lm <- lm(mpg ~ ., data = mtcars) 60 | v <- vetiver_model(cars_lm, "cars_linear") 61 | vetiver_pin_write(b, v) 62 | 63 | vetiver_prepare_docker(b, "cars_linear", path = tempdir()) 64 | \dontshow{\}) # examplesIf} 65 | } 66 | -------------------------------------------------------------------------------- /man/vetiver_python_requirements.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/keras.R, R/luz.R, R/write-plumber.R 3 | \name{vetiver_python_requirements.keras.engine.training.Model} 4 | \alias{vetiver_python_requirements.keras.engine.training.Model} 5 | \alias{vetiver_renviron_requirements.luz_module_fitted} 6 | \alias{vetiver_python_requirements} 7 | \alias{vetiver_python_requirements.default} 8 | \alias{vetiver_renviron_requirements} 9 | \alias{vetiver_renviron_requirements.default} 10 | \title{Use extra files required for deployment} 11 | \usage{ 12 | \method{vetiver_python_requirements}{keras.engine.training.Model}(model) 13 | 14 | \method{vetiver_renviron_requirements}{luz_module_fitted}(model) 15 | 16 | vetiver_python_requirements(model) 17 | 18 | \method{vetiver_python_requirements}{default}(model) 19 | 20 | vetiver_renviron_requirements(model) 21 | 22 | \method{vetiver_renviron_requirements}{default}(model) 23 | } 24 | \arguments{ 25 | \item{model}{A trained model, such as an \code{lm()} model or a tidymodels 26 | \code{\link[workflows:workflow]{workflows::workflow()}}.} 27 | } 28 | \description{ 29 | Create files required for deploying an app generated via 30 | \code{\link[=vetiver_write_plumber]{vetiver_write_plumber()}}, such as a Python \code{requirements.txt} or an 31 | \code{.Renviron} 32 | } 33 | \keyword{internal} 34 | -------------------------------------------------------------------------------- /man/vetiver_sm_delete.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sagemaker.R 3 | \name{vetiver_sm_delete} 4 | \alias{vetiver_sm_delete} 5 | \title{Delete Amazon SageMaker model, endpoint, and endpoint configuration} 6 | \usage{ 7 | vetiver_sm_delete(object, delete_model = TRUE, delete_endpoint = TRUE) 8 | } 9 | \arguments{ 10 | \item{object}{The model API endpoint object to be deleted, created with 11 | \code{\link[=vetiver_endpoint_sagemaker]{vetiver_endpoint_sagemaker()}}.} 12 | 13 | \item{delete_model}{Delete the SageMaker model? Defaults to \code{TRUE}.} 14 | 15 | \item{delete_endpoint}{Delete both the endpoint and endpoint configuration? 16 | Defaults to \code{TRUE}.} 17 | } 18 | \value{ 19 | \code{TRUE}, invisibly 20 | } 21 | \description{ 22 | Use this function to delete the Amazon SageMaker components used in a 23 | \code{\link[=vetiver_endpoint_sagemaker]{vetiver_endpoint_sagemaker()}} object. This function does \emph{not} delete 24 | any pinned model object in S3. 25 | } 26 | \seealso{ 27 | \code{\link[=vetiver_deploy_sagemaker]{vetiver_deploy_sagemaker()}}, \code{\link[=vetiver_sm_build]{vetiver_sm_build()}}, \code{\link[=vetiver_endpoint_sagemaker]{vetiver_endpoint_sagemaker()}} 28 | } 29 | -------------------------------------------------------------------------------- /man/vetiver_type_convert.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/handlers.R 3 | \name{vetiver_type_convert} 4 | \alias{vetiver_type_convert} 5 | \title{Convert new data at prediction time using input data prototype} 6 | \usage{ 7 | vetiver_type_convert(new_data, ptype) 8 | } 9 | \arguments{ 10 | \item{new_data}{New data for making predictions, such as a data frame.} 11 | 12 | \item{ptype}{An input data prototype, such as a 0-row slice of the training 13 | data} 14 | } 15 | \value{ 16 | A converted dataframe 17 | } 18 | \description{ 19 | This is a developer-facing function, useful for supporting new model types. 20 | At prediction time, new observations typically must be checked and sometimes 21 | converted to the data types from training time. 22 | } 23 | \examples{ 24 | 25 | library(tibble) 26 | training_df <- tibble(x = as.Date("2021-01-01") + 0:9, 27 | y = LETTERS[1:10], z = letters[11:20]) 28 | training_df 29 | 30 | prototype <- vctrs::vec_slice(training_df, 0) 31 | vetiver_type_convert(tibble(x = "2021-02-01", y = "J", z = "k"), prototype) 32 | 33 | ## unsuccessful conversion generates an error: 34 | try(vetiver_type_convert(tibble(x = "potato", y = "J", z = "k"), prototype)) 35 | 36 | ## error for missing column: 37 | try(vetiver_type_convert(tibble(x = "potato", y = "J"), prototype)) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /man/vetiver_write_docker.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/write-docker.R 3 | \name{vetiver_write_docker} 4 | \alias{vetiver_write_docker} 5 | \title{Write a Dockerfile for a vetiver model} 6 | \usage{ 7 | vetiver_write_docker( 8 | vetiver_model, 9 | plumber_file = "plumber.R", 10 | path = ".", 11 | ..., 12 | lockfile = "vetiver_renv.lock", 13 | rspm = TRUE, 14 | base_image = glue::glue("FROM rocker/r-ver:{getRversion()}"), 15 | port = 8000, 16 | expose = TRUE, 17 | additional_pkgs = character(0) 18 | ) 19 | } 20 | \arguments{ 21 | \item{vetiver_model}{A deployable \code{\link[=vetiver_model]{vetiver_model()}} object} 22 | 23 | \item{plumber_file}{A path for your Plumber file, created via 24 | \code{\link[=vetiver_write_plumber]{vetiver_write_plumber()}}. Defaults to \code{plumber.R} in the working directory.} 25 | 26 | \item{path}{A path to write the Dockerfile and \code{lockfile}, capturing the 27 | model's package dependencies. Defaults to the working directory.} 28 | 29 | \item{...}{Not currently used.} 30 | 31 | \item{lockfile}{The generated lockfile in \code{path}. Defaults to 32 | \code{"vetiver_renv.lock"}.} 33 | 34 | \item{rspm}{A logical to use the 35 | \href{https://packagemanager.rstudio.com/}{RStudio Public Package Manager} for 36 | \code{renv::restore()} in the Docker image. Defaults to \code{TRUE}.} 37 | 38 | \item{base_image}{The base Docker image to start with. Defaults to 39 | \code{rocker/r-ver} for the version of R you are working with, but models like 40 | keras will require a different base image.} 41 | 42 | \item{port}{The server port for listening: a number such as 8080 or an 43 | expression like \code{'as.numeric(Sys.getenv("PORT"))'} when the port is injected 44 | as an environment variable.} 45 | 46 | \item{expose}{Add \code{EXPOSE} to the Dockerfile? This is helpful for using 47 | Docker Desktop but does not work with an expression for \code{port}.} 48 | 49 | \item{additional_pkgs}{A character vector of additional package names to add 50 | to the Docker image. For example, some boards like \code{\link[pins:board_s3]{pins::board_s3()}} require 51 | additional software; you can use \code{required_pkgs(board)} here.} 52 | } 53 | \value{ 54 | The content of the Dockerfile, invisibly. 55 | } 56 | \description{ 57 | After creating a Plumber file with \code{\link[=vetiver_write_plumber]{vetiver_write_plumber()}}, use 58 | \code{vetiver_write_docker()} to create a Dockerfile plus a \code{vetiver_renv.lock} 59 | file for a pinned \code{\link[=vetiver_model]{vetiver_model()}}. 60 | } 61 | \examples{ 62 | \dontshow{if (interactive() || identical(Sys.getenv("IN_PKGDOWN"), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 63 | 64 | library(pins) 65 | tmp_plumber <- tempfile() 66 | b <- board_temp(versioned = TRUE) 67 | cars_lm <- lm(mpg ~ ., data = mtcars) 68 | v <- vetiver_model(cars_lm, "cars_linear") 69 | vetiver_pin_write(b, v) 70 | vetiver_write_plumber(b, "cars_linear", file = tmp_plumber) 71 | 72 | ## default port 73 | vetiver_write_docker(v, tmp_plumber, tempdir()) 74 | ## install more pkgs, like those required to access board 75 | vetiver_write_docker(v, tmp_plumber, tempdir(), 76 | additional_pkgs = required_pkgs(b)) 77 | ## port from env variable 78 | vetiver_write_docker(v, tmp_plumber, tempdir(), 79 | port = 'as.numeric(Sys.getenv("PORT"))', 80 | expose = FALSE) 81 | \dontshow{\}) # examplesIf} 82 | } 83 | -------------------------------------------------------------------------------- /man/vetiver_write_plumber.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/write-plumber.R 3 | \name{vetiver_write_plumber} 4 | \alias{vetiver_write_plumber} 5 | \title{Write a deployable Plumber file for a vetiver model} 6 | \usage{ 7 | vetiver_write_plumber( 8 | board, 9 | name, 10 | version = NULL, 11 | ..., 12 | file = "plumber.R", 13 | rsconnect = TRUE, 14 | additional_pkgs = character(0) 15 | ) 16 | } 17 | \arguments{ 18 | \item{board}{A pin board, created by \code{\link[pins:board_folder]{board_folder()}}, \code{\link[pins:board_connect]{board_connect()}}, 19 | \code{\link[pins:board_url]{board_url()}} or another \code{board_} function.} 20 | 21 | \item{name}{Pin name.} 22 | 23 | \item{version}{Retrieve a specific version of a pin. Use \code{\link[pins:pin_versions]{pin_versions()}} to 24 | find out which versions are available and when they were created.} 25 | 26 | \item{...}{Other arguments passed to \code{\link[=vetiver_api]{vetiver_api()}} such as the endpoint 27 | \code{path} or prediction \code{type}.} 28 | 29 | \item{file}{A path to write the Plumber file. Defaults to \code{plumber.R} in the 30 | working directory. See \code{\link[plumber:plumb]{plumber::plumb()}} for naming precedence rules.} 31 | 32 | \item{rsconnect}{Create a Plumber file with features needed for \href{https://posit.co/products/enterprise/connect/}{Posit Connect}? Defaults to \code{TRUE}.} 33 | 34 | \item{additional_pkgs}{Any additional R packages that need to be \strong{attached} 35 | via \code{\link[=library]{library()}} to run your API, as a character vector.} 36 | } 37 | \value{ 38 | The content of the \code{plumber.R} file, invisibly. 39 | } 40 | \description{ 41 | Use \code{vetiver_write_plumber()} to create a \code{plumber.R} file for a 42 | \code{\link[=vetiver_model]{vetiver_model()}} that has been versioned and stored via 43 | \code{\link[=vetiver_pin_write]{vetiver_pin_write()}}. 44 | } 45 | \details{ 46 | By default, this function will find and use the latest version of your 47 | vetiver model; the model API (when deployed) will be linked to that specific 48 | version. You can override this default behavior by choosing a specific 49 | \code{version}. 50 | } 51 | \examples{ 52 | \dontshow{if (rlang::is_installed("plumber")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 53 | library(pins) 54 | tmp <- tempfile() 55 | b <- board_temp(versioned = TRUE) 56 | cars_lm <- lm(mpg ~ ., data = mtcars) 57 | v <- vetiver_model(cars_lm, "cars_linear") 58 | vetiver_pin_write(b, v) 59 | 60 | vetiver_write_plumber(b, "cars_linear", file = tmp) 61 | \dontshow{\}) # examplesIf} 62 | } 63 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(vetiver) 3 | 4 | test_check("vetiver") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/api.md: -------------------------------------------------------------------------------- 1 | # old function is deprecated 2 | 3 | `vetiver_pr_predict()` was deprecated in vetiver 0.1.2 and is now defunct. 4 | i Please use `vetiver_api()` instead. 5 | 6 | # OpenAPI spec for check_prototype = FALSE 7 | 8 | The `check_ptype` argument of `vetiver_pr_post()` is deprecated as of vetiver 0.2.0. 9 | i Please use the `check_prototype` argument instead. 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/attach-pkgs.md: -------------------------------------------------------------------------------- 1 | # can fail on a single pkg 2 | 3 | Package(s) could not be attached: 4 | * potato 5 | 6 | # can fail on multiple pkgs 7 | 8 | Package(s) could not be attached: 9 | * potato 10 | * bloopy 11 | 12 | --- 13 | 14 | Package(s) could not be attached: 15 | * potato 16 | 17 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/caret.md: -------------------------------------------------------------------------------- 1 | # can print caret model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars_rf - model for deployment 8 | A random forest regression model using 3 features 9 | 10 | # create plumber.R for xgboost 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(caret) 25 | library(dplyr) 26 | library(e1071) 27 | library(ranger) 28 | } 29 | b <- board_folder(path = "") 30 | v <- vetiver_pin_read(b, "cars_rf") 31 | 32 | #* @plumber 33 | function(pr) { 34 | pr %>% vetiver_api(v) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/choose-version.md: -------------------------------------------------------------------------------- 1 | # can warn for strange setups 2 | 3 | Code 4 | p <- choose_version(p) 5 | Condition 6 | Warning: 7 | Pinned vetiver model has no active version and no datetime on versions 8 | * Do you need to check your pinned model? 9 | * Using version 4500 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/create-ptype.md: -------------------------------------------------------------------------------- 1 | # bad ptype 2 | 3 | Code 4 | vetiver_create_ptype(cars_lm, "potato") 5 | Condition 6 | Error in `vetiver_create_ptype()`: 7 | ! The `save_prototype` argument must be TRUE, FALSE, or a dataframe. 8 | 9 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/dashboard.md: -------------------------------------------------------------------------------- 1 | # vetiver_dashboard(): errors without pin 2 | 3 | Code 4 | vetiver_dashboard(pins = list(board = b, name = "seattle_rf", version = NULL)) 5 | Condition 6 | Error in `vetiver_dashboard()`: 7 | ! The pin "seattle_rf" does not exist on your board 8 | * Check the `pins` params in your dashboard YAML 9 | i To knit the example vetiver monitoring dashboard, execute `vetiver::pin_example_kc_housing_model()` to set up demo model and metrics pins 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/gam.md: -------------------------------------------------------------------------------- 1 | # can print gam model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars_gam - model for deployment 8 | A generalized additive model (gaussian family, identity link) using 2 features 9 | 10 | # create plumber.R for gam 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(mgcv) 25 | } 26 | b <- board_folder(path = "") 27 | v <- vetiver_pin_read(b, "cars_gam") 28 | 29 | #* @plumber 30 | function(pr) { 31 | pr %>% vetiver_api(v) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/glm.md: -------------------------------------------------------------------------------- 1 | # can print glm model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars_glm - model for deployment 8 | A generalized linear model (gaussian family, identity link) using 10 features 9 | 10 | # create plumber.R for glm 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | b <- board_folder(path = "") 22 | v <- vetiver_pin_read(b, "cars_glm") 23 | 24 | #* @plumber 25 | function(pr) { 26 | pr %>% vetiver_api(v) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/keras.md: -------------------------------------------------------------------------------- 1 | # can print keras model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars-keras - model for deployment 8 | A sequential keras model with 2 layers using 10 features 9 | 10 | # create plumber.R for keras 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(keras) 25 | } 26 | b <- board_folder(path = "") 27 | v <- vetiver_pin_read(b, "cars-keras") 28 | 29 | #* @plumber 30 | function(pr) { 31 | pr %>% vetiver_api(v) 32 | } 33 | 34 | --- 35 | 36 | Code 37 | cat(readr::read_lines(fs::path(fs::path_dir(tmp), "requirements.txt")), sep = "\n") 38 | Output 39 | keras 40 | tensorflow 41 | 42 | --- 43 | 44 | Code 45 | vetiver_write_docker(v, tmp, tmp_dir) 46 | Condition 47 | Warning: 48 | Your `vetiver_model` object contains a keras model 49 | i Be sure to use an appropriate `base_image` such as `rocker/cuda` 50 | 51 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/kproto.md: -------------------------------------------------------------------------------- 1 | # can print kproto model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- kproto-example - model for deployment 8 | A k-prototypes clustering model (3 clusters) using 3 features 9 | 10 | # create plumber.R for kproto 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(clustMixType) 25 | } 26 | b <- board_folder(path = "") 27 | v <- vetiver_pin_read(b, "kproto-example") 28 | 29 | #* @plumber 30 | function(pr) { 31 | pr %>% vetiver_api(v) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/luz.md: -------------------------------------------------------------------------------- 1 | # can print a `vetiver`ed luz model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars-luz - model for deployment 8 | A luz module with 11 parameters using 10 features 9 | 10 | # create plumber.R for keras 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(luz) 25 | library(torch) 26 | } 27 | b <- board_folder(path = "") 28 | v <- vetiver_pin_read(b, "cars-luz") 29 | 30 | #* @plumber 31 | function(pr) { 32 | pr %>% vetiver_api(v) 33 | } 34 | 35 | --- 36 | 37 | Code 38 | cat(readr::read_lines(fs::path(fs::path_dir(tmp), ".Renviron")), sep = "\n") 39 | Output 40 | TORCH_INSTALL=1 41 | TORCH_HOME="libtorch/" 42 | 43 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/mlr3.md: -------------------------------------------------------------------------------- 1 | # mlr3 learner description can be printed 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- rpart_pima - model for deployment 8 | A mlr3 classif.rpart learner using 8 features 9 | 10 | # create plumber.R for mlr3 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(mlr3) 25 | library(rpart) 26 | } 27 | b <- board_folder(path = "") 28 | v <- vetiver_pin_read(b, "rpart_pima") 29 | 30 | #* @plumber 31 | function(pr) { 32 | pr %>% vetiver_api(v) 33 | } 34 | 35 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/monitor.md: -------------------------------------------------------------------------------- 1 | # vetiver_pin_metrics(): fails without existing pin 2 | 3 | Can't find pin called "metrics1" 4 | i Use `pin_list()` to see all available pins in this board 5 | 6 | # vetiver_pin_metrics(): fails with `overwrite = FALSE` 7 | 8 | The new metrics overlap with dates already stored in 'metrics2' 9 | i Check the aggregated dates or use `overwrite = TRUE` 10 | 11 | # vetiver_pin_metrics(): fails with type = csv 12 | 13 | Metrics must be pinned as "arrow" or "rds", not "csv" 14 | 15 | --- 16 | 17 | Metrics must be pinned as "arrow" or "rds", not "csv" 18 | 19 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/pin-read-write.md: -------------------------------------------------------------------------------- 1 | # can pin a model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars1 - model for deployment 8 | An OLS linear regression model using 2 features 9 | 10 | # can pin a model with no prototype 11 | 12 | The `save_ptype` argument of `vetiver_model()` is deprecated as of vetiver 0.2.0. 13 | i Please use the `save_prototype` argument instead. 14 | 15 | # right message for reading with `check_renv` 16 | 17 | There is no lockfile stored with "cars5": 18 | i Use `check_renv = TRUE` when you save your model to your board 19 | 20 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/predict.md: -------------------------------------------------------------------------------- 1 | # can predict on basic vetiver router 2 | 3 | Code 4 | print(endpoint) 5 | Output 6 | 7 | -- A model API endpoint for prediction: 8 | http://localhost:/predict 9 | 10 | # get correct errors 11 | 12 | Code 13 | predict(endpoint, mtcars[, 2:4]) 14 | Condition 15 | Error in `predict()`: 16 | ! Failed to predict: Error in `hardhat::scream()`: 17 | ! Can't convert from `data` > to > due to loss of precision. 25 | 26 | --- 27 | 28 | Code 29 | predict(endpoint, mtcars[, 3:5]) 30 | Condition 31 | Error in `predict()`: 32 | ! Failed to predict: Error in `hardhat::validate_column_names()`: 33 | ! The following required columns are missing: 'cyl'. 34 | 35 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/ranger.md: -------------------------------------------------------------------------------- 1 | # can print ranger model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars3 - model for deployment 8 | A ranger regression model using 10 features 9 | 10 | # error for no prototype_data with ranger 11 | 12 | Code 13 | vetiver_model(cars_rf, "cars3") 14 | Condition 15 | Error in `vetiver_ptype()`: 16 | ! No `prototype_data` available to create an input data prototype 17 | * Pass at least one row of training features as `prototype_data` 18 | * See the documentation for `vetiver_ptype()` 19 | 20 | # create plumber.R for ranger 21 | 22 | Code 23 | cat(readr::read_lines(tmp), sep = "\n") 24 | Output 25 | # Generated by the vetiver package; edit with care 26 | 27 | library(pins) 28 | library(plumber) 29 | library(rapidoc) 30 | library(vetiver) 31 | 32 | # Packages needed to generate model predictions 33 | if (FALSE) { 34 | library(ranger) 35 | } 36 | b <- board_folder(path = "") 37 | v <- vetiver_pin_read(b, "cars3") 38 | 39 | #* @plumber 40 | function(pr) { 41 | pr %>% vetiver_api(v) 42 | } 43 | 44 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/recipe.md: -------------------------------------------------------------------------------- 1 | # can print recipe 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- car-splines - model for deployment 8 | A feature engineering recipe with 1 step using 2 features 9 | 10 | # create plumber.R for recipe 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(recipes) 25 | } 26 | b <- board_folder(path = "") 27 | v <- vetiver_pin_read(b, "car-splines") 28 | 29 | #* @plumber 30 | function(pr) { 31 | pr %>% vetiver_api(v) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/rsconnect.md: -------------------------------------------------------------------------------- 1 | # create rsconnect bundle: contains correct plumber file 2 | 3 | Code 4 | cat(readr::read_lines(fs::path(tmp_dir, "plumber.R")), sep = "\n") 5 | Output 6 | # Generated by the vetiver package; edit with care 7 | 8 | library(pins) 9 | library(plumber) 10 | library(rapidoc) 11 | library(vetiver) 12 | b <- board_folder(path = "") 13 | v <- vetiver_pin_read(b, "cars1") 14 | 15 | #* @plumber 16 | function(pr) { 17 | pr %>% vetiver_api(v) 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/stacks.md: -------------------------------------------------------------------------------- 1 | # can print stacks model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- frog-stack - model for deployment 8 | A regression stacked ensemble with 3 members using 4 features 9 | 10 | # create plumber.R for stacks 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(glmnet) 25 | library(parsnip) 26 | library(recipes) 27 | library(stacks) 28 | library(stats) 29 | library(workflows) 30 | } 31 | b <- board_folder(path = "") 32 | v <- vetiver_pin_read(b, "frog-stack") 33 | 34 | #* @plumber 35 | function(pr) { 36 | pr %>% vetiver_api(v) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/tidymodels.md: -------------------------------------------------------------------------------- 1 | # can print tidymodels model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars_wf - model for deployment 8 | A ranger regression modeling workflow using 10 features 9 | 10 | # create plumber.R for tidymodels 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(parsnip) 25 | library(ranger) 26 | library(workflows) 27 | } 28 | b <- board_folder(path = "") 29 | v <- vetiver_pin_read(b, "cars_wf") 30 | 31 | #* @plumber 32 | function(pr) { 33 | pr %>% vetiver_api(v) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/type-convert.md: -------------------------------------------------------------------------------- 1 | # missing variables 2 | 3 | The following required columns are missing: 'mpg', 'hp', 'drat', 'wt', 'qsec', 'vs', 'am', 'gear', 'carb'. 4 | 5 | # a factor plus a bad character 6 | 7 | [0, 2]: expected value in level set, but got 'ZZ' 8 | 9 | # a date 10 | 11 | [0, 1]: expected date like , but got 'potato' 12 | 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/write-plumber.md: -------------------------------------------------------------------------------- 1 | # create plumber.R with no packages 2 | 3 | Code 4 | cat(readr::read_lines(tmp), sep = "\n") 5 | Output 6 | # Generated by the vetiver package; edit with care 7 | 8 | library(pins) 9 | library(plumber) 10 | library(rapidoc) 11 | library(vetiver) 12 | b <- board_folder(path = "") 13 | v <- vetiver_pin_read(b, "cars1") 14 | 15 | #* @plumber 16 | function(pr) { 17 | pr %>% vetiver_api(v) 18 | } 19 | 20 | # create plumber.R for complicated board 21 | 22 | Code 23 | cat(readr::read_lines(tmp), sep = "\n") 24 | Output 25 | # Generated by the vetiver package; edit with care 26 | 27 | library(pins) 28 | library(plumber) 29 | library(rapidoc) 30 | library(vetiver) 31 | b <- board_s3(bucket = "foo", region = 6, endpoint = 8) 32 | v <- vetiver_pin_read(b, "cars1") 33 | 34 | #* @plumber 35 | function(pr) { 36 | pr %>% vetiver_api(v) 37 | } 38 | 39 | # create plumber.R for URL board 40 | 41 | Code 42 | cat(readr::read_lines(tmp), sep = "\n") 43 | Output 44 | # Generated by the vetiver package; edit with care 45 | 46 | library(pins) 47 | library(plumber) 48 | library(rapidoc) 49 | library(vetiver) 50 | b <- board_url(c(foo = "foo", bar = "bar", baz = "baz")) 51 | v <- vetiver_pin_read(b, "cars1") 52 | 53 | #* @plumber 54 | function(pr) { 55 | pr %>% vetiver_api(v) 56 | } 57 | 58 | # create plumber.R with packages 59 | 60 | Code 61 | cat(readr::read_lines(tmp), sep = "\n") 62 | Output 63 | # Generated by the vetiver package; edit with care 64 | 65 | library(pins) 66 | library(plumber) 67 | library(rapidoc) 68 | library(vetiver) 69 | 70 | # Packages needed to generate model predictions 71 | if (FALSE) { 72 | library(beepr) 73 | library(janeaustenr) 74 | } 75 | b <- board_folder(path = "") 76 | v <- vetiver_pin_read(b, "cars1") 77 | 78 | #* @plumber 79 | function(pr) { 80 | pr %>% vetiver_api(v) 81 | } 82 | 83 | # create plumber.R with extra infra packages 84 | 85 | Code 86 | cat(readr::read_lines(tmp), sep = "\n") 87 | Output 88 | # Generated by the vetiver package; edit with care 89 | 90 | library(beepr) 91 | library(janeaustenr) 92 | library(pins) 93 | library(plumber) 94 | library(rapidoc) 95 | library(vetiver) 96 | b <- board_folder(path = "") 97 | v <- vetiver_pin_read(b, "cars1") 98 | 99 | #* @plumber 100 | function(pr) { 101 | pr %>% vetiver_api(v) 102 | } 103 | 104 | # create plumber.R with rsconnect = FALSE 105 | 106 | Code 107 | cat(readr::read_lines(tmp), sep = "\n") 108 | Output 109 | # Generated by the vetiver package; edit with care 110 | 111 | library(pins) 112 | library(plumber) 113 | library(rapidoc) 114 | library(vetiver) 115 | b <- board_folder(path = "") 116 | v <- vetiver_pin_read(b, "cars1") 117 | 118 | #* @plumber 119 | function(pr) { 120 | pr %>% vetiver_api(v) 121 | } 122 | 123 | # create plumber.R with args in dots 124 | 125 | Code 126 | cat(readr::read_lines(tmp), sep = "\n") 127 | Output 128 | # Generated by the vetiver package; edit with care 129 | 130 | library(pins) 131 | library(plumber) 132 | library(rapidoc) 133 | library(vetiver) 134 | 135 | # Packages needed to generate model predictions 136 | if (FALSE) { 137 | library(beepr) 138 | library(janeaustenr) 139 | } 140 | b <- board_folder(path = "") 141 | v <- vetiver_pin_read(b, "cars1") 142 | 143 | #* @plumber 144 | function(pr) { 145 | pr %>% vetiver_api(v, debug = TRUE, endpoint = "/predict2", type = "numeric") 146 | } 147 | 148 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/xgboost.md: -------------------------------------------------------------------------------- 1 | # can print xgboost model 2 | 3 | Code 4 | v 5 | Output 6 | 7 | -- cars2 - model for deployment 8 | An xgboost reg:squarederror model using 10 features 9 | 10 | # create plumber.R for xgboost 11 | 12 | Code 13 | cat(readr::read_lines(tmp), sep = "\n") 14 | Output 15 | # Generated by the vetiver package; edit with care 16 | 17 | library(pins) 18 | library(plumber) 19 | library(rapidoc) 20 | library(vetiver) 21 | 22 | # Packages needed to generate model predictions 23 | if (FALSE) { 24 | library(xgboost) 25 | } 26 | b <- board_folder(path = "") 27 | v <- vetiver_pin_read(b, "cars2") 28 | 29 | #* @plumber 30 | function(pr) { 31 | pr %>% vetiver_api(v) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/helper.R: -------------------------------------------------------------------------------- 1 | library(pins) 2 | 3 | cars_lm <- lm(mpg ~ cyl + disp, data = mtcars) 4 | v <- vetiver_model(cars_lm, "cars1") 5 | 6 | root_path <- "http://localhost" 7 | tmp_dir <- fs::path_real(withr::local_tempdir()) 8 | rel_dir <- fs::path_rel(tmp_dir) 9 | port <- ifelse(rlang::is_installed("httpuv"), httpuv::randomPort(), 8088) 10 | 11 | redact_vetiver <- function(snapshot) { 12 | snapshot <- gsub(rel_dir, "", snapshot, fixed = TRUE) 13 | snapshot <- gsub(tmp_dir, "", snapshot, fixed = TRUE) 14 | snapshot <- gsub(getRversion(), "", snapshot, fixed = TRUE) 15 | } 16 | 17 | redact_port <- function(snapshot) { 18 | snapshot <- gsub(port, "", snapshot, fixed = TRUE) 19 | } 20 | 21 | expect_api_routes <- function(routes) { 22 | testthat::expect_equal( 23 | names(routes), 24 | c("metadata", "ping", "predict", "prototype") 25 | ) 26 | testthat::expect_equal( 27 | map_chr(routes, "verbs"), 28 | c(metadata = "GET", ping = "GET", predict = "POST", prototype = "GET") 29 | ) 30 | } 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | options(pins.verbose = FALSE) 2 | options(pins.quiet = TRUE) 3 | options(renv.verbose = FALSE) 4 | Sys.setenv(RENV_CONFIG_SNAPSHOT_VALIDATE = FALSE) 5 | 6 | clean_python_tmp_dir <- function() { 7 | if (!rlang::is_installed("reticulate")) 8 | return() 9 | 10 | if(!reticulate::py_available()) 11 | return() 12 | 13 | tryCatch( 14 | error = function(cnd) { 15 | cli::cli_inform("Cannot clean Python temp directory: {cnd}") 16 | }, 17 | { 18 | python_temp_dir <- dirname(reticulate::py_run_string( 19 | "import tempfile; x=tempfile.NamedTemporaryFile().name", 20 | local = TRUE 21 | )$x) 22 | detritus <- fs::dir_ls( 23 | python_temp_dir, 24 | regexp = "__autograph_generated_file|__pycache__" 25 | ) 26 | fs::file_delete(detritus) 27 | } 28 | ) 29 | } 30 | 31 | withr::defer(clean_python_tmp_dir(), teardown_env()) 32 | -------------------------------------------------------------------------------- /tests/testthat/test-attach-pkgs.R: -------------------------------------------------------------------------------- 1 | test_that("can attach pkgs", { 2 | skip_on_cran() 3 | expect_error(attach_pkgs(c("knitr", "readr")), NA) 4 | }) 5 | 6 | test_that("can fail on a single pkg", { 7 | skip_on_cran() 8 | expect_snapshot_error(attach_pkgs(c("potato"))) 9 | }) 10 | 11 | test_that("can fail on multiple pkgs", { 12 | skip_on_cran() 13 | expect_snapshot_error(attach_pkgs(c("potato", "bloopy"))) 14 | expect_snapshot_error(attach_pkgs(c("potato", "readr"))) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/testthat/test-caret.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("caret") 2 | skip_if_not_installed("ranger") 3 | skip_if_not_installed("plumber") 4 | 5 | library(caret) 6 | library(plumber) 7 | 8 | predictors <- mtcars[, c("cyl", "disp", "hp")] 9 | 10 | set.seed(1) 11 | rf_fit <- 12 | train( 13 | x = predictors, 14 | y = mtcars$mpg, 15 | method = "ranger", 16 | tuneLength = 2, 17 | trControl = trainControl(method = "cv") 18 | ) 19 | 20 | v <- vetiver_model(rf_fit, "cars_rf") 21 | 22 | test_that("can print caret model", { 23 | expect_snapshot(v) 24 | }) 25 | 26 | test_that("can pin a caret model", { 27 | b <- board_temp() 28 | vetiver_pin_write(b, v) 29 | expect_equal( 30 | pin_read(b, "cars_rf"), 31 | list( 32 | model = bundle::bundle(butcher::butcher(rf_fit)), 33 | prototype = vctrs::vec_slice(tibble::as_tibble(mtcars[,2:4]), 0) 34 | ) 35 | ) 36 | expect_equal( 37 | pin_meta(b, "cars_rf")$user$required_pkgs, 38 | c("caret", "dplyr", "e1071", "ranger") 39 | ) 40 | }) 41 | 42 | test_that("default endpoint for caret", { 43 | p <- pr() %>% vetiver_api(v) 44 | p_routes <- p$routes[-1] 45 | expect_api_routes(p_routes) 46 | }) 47 | 48 | test_that("default OpenAPI spec", { 49 | v$metadata <- list(url = "potatoes") 50 | p <- pr() %>% vetiver_api(v) 51 | car_spec <- p$getApiSpec() 52 | expect_equal(car_spec$info$description, 53 | "A random forest regression model") 54 | post_spec <- car_spec$paths$`/predict`$post 55 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 56 | expect_equal(as.character(post_spec$summary), 57 | "Return predictions from model using 3 features") 58 | get_spec <- car_spec$paths$`/pin-url`$get 59 | expect_equal(as.character(get_spec$summary), 60 | "Get URL of pinned vetiver model") 61 | 62 | }) 63 | 64 | test_that("create plumber.R for xgboost", { 65 | skip_on_cran() 66 | b <- board_folder(path = tmp_dir) 67 | vetiver_pin_write(b, v) 68 | tmp <- tempfile() 69 | vetiver_write_plumber(b, "cars_rf", file = tmp) 70 | expect_snapshot( 71 | cat(readr::read_lines(tmp), sep = "\n"), 72 | transform = redact_vetiver 73 | ) 74 | }) 75 | 76 | -------------------------------------------------------------------------------- /tests/testthat/test-choose-version.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("mockery") 2 | 3 | test_that("can choose a version", { 4 | skip_on_cran() 5 | b <- board_temp(versioned = TRUE) 6 | mock_version_name <- mockery::mock( 7 | "20130104T050607Z-xxxxx", 8 | "20130204T050607Z-yyyyy", 9 | "20130304T050607Z-zzzzz", 10 | ) 11 | local_mocked_bindings(version_name = mock_version_name, .package = "pins") 12 | mod1 <- lm(mpg ~ cyl + disp, data = mtcars) 13 | v <- vetiver_model(mod1, "cars1") 14 | vetiver_pin_write(b, v) 15 | mod2 <- lm(mpg ~ cyl + drat, data = mtcars) 16 | v <- vetiver_model(mod2, "cars1") 17 | vetiver_pin_write(b, v) 18 | mod3 <- lm(mpg ~ cyl + wt, data = mtcars) 19 | v <- vetiver_model(mod3, "cars1") 20 | vetiver_pin_write(b, v) 21 | p <- pins::pin_versions(b, "cars1") 22 | expect_equal(p$version[[3]], choose_version(p)) 23 | }) 24 | 25 | test_that("can choose a version by `active` column like on Connect", { 26 | p <- tibble::tibble(version = 4500:4509, 27 | created = NA, 28 | active = c(TRUE, rep(FALSE, 9)), 29 | size = rpois(10, 2000)) 30 | expect_equal(choose_version(p), 4500) 31 | }) 32 | 33 | test_that("can warn for strange setups", { 34 | p <- tibble::tibble(version = 4500:4509, 35 | size = rpois(10, 2000)) 36 | expect_snapshot(p <- choose_version(p)) 37 | expect_equal(p, 4500) 38 | }) 39 | 40 | -------------------------------------------------------------------------------- /tests/testthat/test-create-ptype.R: -------------------------------------------------------------------------------- 1 | b <- board_temp() 2 | cars_lm <- lm(mpg ~ cyl + disp, data = mtcars) 3 | 4 | test_that("default ptype", { 5 | expect_equal( 6 | vetiver_create_ptype(cars_lm, TRUE), 7 | vctrs::vec_slice(tibble::as_tibble(mtcars[,2:3]), 0) 8 | ) 9 | }) 10 | 11 | test_that("default ptype, check vetiver_slice_zero", { 12 | expect_equal( 13 | vetiver_create_ptype(cars_lm, TRUE), 14 | vetiver_ptype(cars_lm) 15 | ) 16 | }) 17 | 18 | 19 | test_that("ptype = FALSE", { 20 | expect_equal( 21 | vetiver_create_ptype(cars_lm, FALSE), 22 | NULL 23 | ) 24 | }) 25 | 26 | test_that("ptype for model with interactions", { 27 | cars_interaction <- lm(mpg ~ cyl * vs + disp, data = mtcars) 28 | expect_equal( 29 | vetiver_create_ptype(cars_interaction, TRUE), 30 | vctrs::vec_slice(tibble::as_tibble(mtcars[, c(2, 8, 3)]), 0) 31 | ) 32 | }) 33 | 34 | test_that("custom ptype", { 35 | expect_equal( 36 | vetiver_create_ptype(cars_lm, mtcars[3:10, 2:3]), 37 | mtcars[3:10, 2:3] 38 | ) 39 | }) 40 | 41 | test_that("bad ptype", { 42 | expect_snapshot( 43 | vetiver_create_ptype(cars_lm, "potato"), 44 | error = TRUE 45 | ) 46 | }) 47 | -------------------------------------------------------------------------------- /tests/testthat/test-dashboard.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("rmarkdown", minimum_version = "2.12") 2 | 3 | test_that("templates exist", { 4 | expect_equal( 5 | rmarkdown::available_templates("vetiver"), 6 | c("vetiver_dashboard", "vetiver_model_card") 7 | ) 8 | }) 9 | 10 | describe("vetiver_dashboard()", { 11 | 12 | # don't run on cran because pandoc is required 13 | skip_on_cran() 14 | skip_if_not_installed("flexdashboard") 15 | 16 | rmd <- withr::local_tempfile(fileext = ".Rmd") 17 | html <- withr::local_tempfile(fileext = ".html") 18 | path <- expect_invisible(rmarkdown::draft( 19 | rmd, 20 | template = "vetiver_dashboard", 21 | package = "vetiver", 22 | create_dir = FALSE, 23 | edit = FALSE 24 | )) 25 | 26 | it("can create dashboard", { 27 | expect_identical(rmd, path) 28 | expect_true(file.exists(rmd)) 29 | }) 30 | 31 | it("errors without pin", { 32 | b <- pins::board_temp() 33 | expect_snapshot( 34 | vetiver_dashboard(pins = list(board = b, name = "seattle_rf", version = NULL)), 35 | error = TRUE 36 | ) 37 | }) 38 | 39 | it("can render dashboard", { 40 | b <- pins::board_temp() 41 | pin_example_kc_housing_model(b) 42 | output_format <- vetiver_dashboard(pins = list(board = b, name = "seattle_rf", version = NULL)) 43 | rmarkdown::render(rmd, output_format = output_format, output_file = html) 44 | expect_true(file.exists(html)) 45 | }) 46 | 47 | }) 48 | -------------------------------------------------------------------------------- /tests/testthat/test-gam.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("mgcv") 2 | skip_if_not_installed("plumber") 3 | 4 | library(plumber) 5 | mtcars_gam <- mgcv::gam(mpg ~ s(disp, k = 3) + s(wt), data = mtcars) 6 | v <- vetiver_model(mtcars_gam, "cars_gam") 7 | 8 | test_that("can print gam model", { 9 | expect_snapshot(v) 10 | }) 11 | 12 | test_that("can predict gam model", { 13 | preds <- predict(v, mtcars) 14 | expect_type(preds, "double") 15 | expect_equal(mean(preds), 20.1, tolerance = 0.1) 16 | }) 17 | 18 | test_that("can pin a gam model", { 19 | b <- board_temp() 20 | vetiver_pin_write(b, v) 21 | pinned <- pin_read(b, "cars_gam") 22 | expect_equal( 23 | pinned, 24 | list( 25 | model = butcher::butcher(mtcars_gam), 26 | prototype = vctrs::vec_ptype(tibble::as_tibble(mtcars[, c(3, 6)])) 27 | ), 28 | ignore_function_env = TRUE, 29 | ignore_formula_env = TRUE 30 | ) 31 | expect_equal( 32 | pin_meta(b, "cars_gam")$user$required_pkgs, 33 | "mgcv" 34 | ) 35 | }) 36 | 37 | test_that("default endpoint for gam", { 38 | p <- pr() %>% vetiver_api(v) 39 | p_routes <- p$routes[-1] 40 | expect_api_routes(p_routes) 41 | }) 42 | 43 | test_that("default OpenAPI spec", { 44 | v$metadata <- list(url = "potatoes") 45 | p <- pr() %>% vetiver_api(v) 46 | car_spec <- p$getApiSpec() 47 | expect_equal(car_spec$info$description, 48 | "A generalized additive model (gaussian family, identity link)") 49 | post_spec <- car_spec$paths$`/predict`$post 50 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 51 | expect_equal(as.character(post_spec$summary), 52 | "Return predictions from model using 2 features") 53 | get_spec <- car_spec$paths$`/pin-url`$get 54 | expect_equal(as.character(get_spec$summary), 55 | "Get URL of pinned vetiver model") 56 | 57 | }) 58 | 59 | test_that("create plumber.R for gam", { 60 | skip_on_cran() 61 | b <- board_folder(path = tmp_dir) 62 | vetiver_pin_write(b, v) 63 | tmp <- tempfile() 64 | vetiver_write_plumber(b, "cars_gam", file = tmp) 65 | expect_snapshot( 66 | cat(readr::read_lines(tmp), sep = "\n"), 67 | transform = redact_vetiver 68 | ) 69 | }) 70 | -------------------------------------------------------------------------------- /tests/testthat/test-glm.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("plumber") 2 | library(plumber) 3 | 4 | mtcars_glm <- glm(mpg ~ ., data = mtcars) 5 | v <- vetiver_model(mtcars_glm, "cars_glm") 6 | 7 | test_that("can print glm model", { 8 | expect_snapshot(v) 9 | }) 10 | 11 | test_that("can predict glm model", { 12 | preds <- predict(v, mtcars) 13 | expect_type(preds, "double") 14 | expect_equal(mean(preds), 20.1, tolerance = 0.1) 15 | }) 16 | 17 | test_that("can pin a glm model", { 18 | b <- board_temp() 19 | vetiver_pin_write(b, v) 20 | pinned <- pin_read(b, "cars_glm") 21 | expect_equal( 22 | pinned, 23 | list( 24 | model = butcher::butcher(mtcars_glm), 25 | prototype = vctrs::vec_slice(tibble::as_tibble(mtcars[,2:11]), 0) 26 | ), 27 | ignore_function_env = TRUE, 28 | ignore_formula_env = TRUE 29 | ) 30 | expect_equal( 31 | pin_meta(b, "cars_glm")$user$required_pkgs, 32 | NULL 33 | ) 34 | }) 35 | 36 | test_that("default endpoint for glm", { 37 | p <- pr() %>% vetiver_api(v) 38 | p_routes <- p$routes[-1] 39 | expect_api_routes(p_routes) 40 | }) 41 | 42 | test_that("default OpenAPI spec", { 43 | v$metadata <- list(url = "potatoes") 44 | p <- pr() %>% vetiver_api(v) 45 | car_spec <- p$getApiSpec() 46 | expect_equal(car_spec$info$description, 47 | "A generalized linear model (gaussian family, identity link)") 48 | post_spec <- car_spec$paths$`/predict`$post 49 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 50 | expect_equal(as.character(post_spec$summary), 51 | "Return predictions from model using 10 features") 52 | get_spec <- car_spec$paths$`/pin-url`$get 53 | expect_equal(as.character(get_spec$summary), 54 | "Get URL of pinned vetiver model") 55 | 56 | }) 57 | 58 | test_that("create plumber.R for glm", { 59 | skip_on_cran() 60 | b <- board_folder(path = tmp_dir) 61 | vetiver_pin_write(b, v) 62 | tmp <- tempfile() 63 | vetiver_write_plumber(b, "cars_glm", file = tmp) 64 | expect_snapshot( 65 | cat(readr::read_lines(tmp), sep = "\n"), 66 | transform = redact_vetiver 67 | ) 68 | }) 69 | 70 | 71 | test_that("prototype for glm with interactions", { 72 | cars_interaction <- glm(mpg ~ cyl * vs + disp, data = mtcars) 73 | expect_equal( 74 | vetiver_create_ptype(cars_interaction, TRUE), 75 | vctrs::vec_slice(tibble::as_tibble(mtcars[, c(2, 8, 3)]), 0) 76 | ) 77 | }) 78 | 79 | -------------------------------------------------------------------------------- /tests/testthat/test-keras.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | skip_if_not_installed("keras") 3 | skip_if_not_installed("plumber") 4 | skip_if(is.null(tensorflow::tf_version())) 5 | 6 | library(plumber) 7 | library(keras) 8 | 9 | scaled_cars <- as.matrix(mtcars) %>% scale() 10 | x_test <- scaled_cars[26:32, 2:ncol(scaled_cars)] 11 | x_train <- scaled_cars[1:25, 2:ncol(scaled_cars)] 12 | y_train <- scaled_cars[1:25, 1] 13 | 14 | set.seed(1) 15 | 16 | keras_fit <- 17 | keras_model_sequential() %>% 18 | layer_dense(units = 1, input_shape = ncol(x_train), activation = 'linear') %>% 19 | compile(loss = 'mean_squared_error') 20 | 21 | keras_fit %>% 22 | fit( 23 | x = x_train, y = y_train, 24 | epochs = 100, batch_size = 1, 25 | verbose = 0 26 | ) 27 | 28 | v <- vetiver_model(keras_fit, "cars-keras", prototype_data = data.frame(x_train)[1,]) 29 | 30 | test_that("can print keras model", { 31 | expect_snapshot(v) 32 | }) 33 | 34 | test_that("can predict keras model", { 35 | preds <- predict(v, x_test) 36 | expect_type(preds, "double") 37 | expect_equal(length(preds), 7) 38 | }) 39 | 40 | test_that("can pin a keras model", { 41 | b <- board_temp() 42 | vetiver_pin_write(b, v) 43 | pinned <- pin_read(b, "cars-keras") 44 | ## STILL NOT EQUAL because of serialization issues, even with bundle 45 | ## expect_equal(pinned$model, bundle::bundle(keras_fit)) 46 | expect_equal(pinned$ptype, NULL) 47 | expect_equal(pin_meta(b, "cars-keras")$user$required_pkgs, "keras") 48 | }) 49 | 50 | test_that("default endpoint for keras", { 51 | p <- pr() %>% vetiver_api(v) 52 | p_routes <- p$routes[-1] 53 | expect_api_routes(p_routes) 54 | }) 55 | 56 | test_that("default OpenAPI spec", { 57 | v$metadata <- list(url = "potatoes") 58 | p <- pr() %>% vetiver_api(v) 59 | car_spec <- p$getApiSpec() 60 | expect_equal(car_spec$info$description, 61 | "A sequential keras model with 2 layers") 62 | post_spec <- car_spec$paths$`/predict`$post 63 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 64 | expect_equal(as.character(post_spec$summary), 65 | "Return predictions from model using 10 features") 66 | get_spec <- car_spec$paths$`/pin-url`$get 67 | expect_equal(as.character(get_spec$summary), 68 | "Get URL of pinned vetiver model") 69 | 70 | }) 71 | 72 | test_that("create plumber.R for keras", { 73 | skip_on_cran() 74 | b <- board_folder(path = tmp_dir) 75 | vetiver_pin_write(b, v) 76 | tmp <- tempfile() 77 | vetiver_write_plumber(b, "cars-keras", file = tmp) 78 | expect_snapshot( 79 | cat(readr::read_lines(tmp), sep = "\n"), 80 | transform = redact_vetiver 81 | ) 82 | expect_snapshot( 83 | cat(readr::read_lines(fs::path(fs::path_dir(tmp), "requirements.txt")), sep = "\n"), 84 | transform = redact_vetiver 85 | ) 86 | expect_snapshot( 87 | vetiver_write_docker(v, tmp, tmp_dir) 88 | ) 89 | }) 90 | 91 | -------------------------------------------------------------------------------- /tests/testthat/test-kproto.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("clustMixType") 2 | skip_if_not_installed("plumber") 3 | 4 | library(plumber) 5 | 6 | data(crickets, package = "modeldata") 7 | ## this model does not work with tibble input: 8 | crickets <- as.data.frame(crickets) 9 | 10 | set.seed(123) 11 | kp_fit <- clustMixType::kproto(crickets, k = 3, verbose = FALSE) 12 | v <- vetiver_model(kp_fit, "kproto-example") 13 | 14 | test_that("can print kproto model", { 15 | expect_snapshot(v) 16 | }) 17 | 18 | test_that("can predict kproto model", { 19 | ## prediction is broken for single observation 20 | 21 | predicted_clusters <- predict(v, crickets[5:6,]) 22 | expect_equal(predicted_clusters$cluster, c(1, 1)) 23 | }) 24 | 25 | test_that("can pin a kproto model", { 26 | b <- pins::board_temp() 27 | vetiver_pin_write(b, v) 28 | pinned <- pins::pin_read(b, "kproto-example") 29 | expect_equal( 30 | pinned, 31 | list( 32 | model = vetiver_prepare_model(kp_fit), 33 | prototype = vctrs::vec_ptype(tibble::as_tibble(crickets)) 34 | ) 35 | ) 36 | expect_equal( 37 | pin_meta(b, "kproto-example")$user$required_pkgs, 38 | "clustMixType" 39 | ) 40 | }) 41 | 42 | test_that("default endpoint for kproto", { 43 | p <- plumber::pr() %>% vetiver_api(v) 44 | p_routes <- p$routes[-1] 45 | expect_api_routes(p_routes) 46 | }) 47 | 48 | test_that("default OpenAPI spec", { 49 | v$metadata <- list(url = "potatoes") 50 | p <- pr() %>% vetiver_api(v) 51 | api_spec <- p$getApiSpec() 52 | expect_equal(api_spec$info$description, 53 | "A k-prototypes clustering model (3 clusters)") 54 | post_spec <- api_spec$paths$`/predict`$post 55 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 56 | expect_equal(as.character(post_spec$summary), 57 | "Return predictions from model using 3 features") 58 | get_spec <- api_spec$paths$`/pin-url`$get 59 | expect_equal(as.character(get_spec$summary), 60 | "Get URL of pinned vetiver model") 61 | 62 | }) 63 | 64 | test_that("create plumber.R for kproto", { 65 | skip_on_cran() 66 | b <- board_folder(path = tmp_dir) 67 | vetiver_pin_write(b, v) 68 | tmp <- tempfile() 69 | vetiver_write_plumber(b, "kproto-example", file = tmp) 70 | expect_snapshot( 71 | cat(readr::read_lines(tmp), sep = "\n"), 72 | transform = redact_vetiver 73 | ) 74 | }) 75 | -------------------------------------------------------------------------------- /tests/testthat/test-luz.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | skip_if_not_installed(pkg = c("torch", "luz", "plumber")) 3 | library(plumber) 4 | torch::install_torch() 5 | 6 | scaled_cars <- as.matrix(mtcars) %>% scale() 7 | x_test <- scaled_cars[26:32, 2:ncol(scaled_cars)] 8 | x_train <- scaled_cars[1:25, 2:ncol(scaled_cars)] 9 | y_train <- scaled_cars[1:25, 1, drop=FALSE] 10 | 11 | set.seed(1) 12 | 13 | acc <- luz::accelerator(cpu = TRUE) 14 | 15 | luz_fit <- torch::nn_linear %>% 16 | luz::setup(loss = torch::nnf_mse_loss, optimizer = torch::optim_sgd) %>% 17 | luz::set_hparams(in_features = ncol(x_train), out_features = 1) %>% 18 | luz::set_opt_hparams(lr = 0.01) %>% 19 | luz::fit( 20 | list(x_train, y_train), verbose = FALSE, 21 | dataloader_options = list(batch_size = 5), 22 | accelerator = acc 23 | ) 24 | 25 | v <- vetiver_model( 26 | luz_fit, 27 | "cars-luz", 28 | prototype_data = data.frame(x_train)[1,] 29 | ) 30 | 31 | test_that("can print a `vetiver`ed luz model", { 32 | expect_snapshot(v) 33 | }) 34 | 35 | test_that("can predict a `vetiver`ed luz model", { 36 | v_preds <- predict(v, x_test, accelerator = acc)$cpu() 37 | l_preds <- predict(luz_fit, x_test, accelerator = acc)$cpu() 38 | 39 | expect_equal(as.array(v_preds), as.array(l_preds)) 40 | }) 41 | 42 | test_that("can pin a luz model", { 43 | b <- board_temp() 44 | vetiver_pin_write(b, v) 45 | pinned <- pin_read(b, "cars-luz") 46 | ## STILL NOT EQUAL because of serialization issues, even with bundle 47 | ## expect_equal(pinned$model, bundle::bundle(luz_fit)) 48 | expect_equal(pinned$prototype, vctrs::vec_ptype(tibble::as_tibble(x_train))) 49 | expect_equal(pin_meta(b, "cars-luz")$user$required_pkgs, c("luz", "torch")) 50 | }) 51 | 52 | test_that("endpoints for luz", { 53 | p <- plumber::pr() %>% vetiver_api(v) 54 | p_routes <- p$routes[-1] 55 | expect_api_routes(p_routes) 56 | }) 57 | 58 | test_that("default OpenAPI spec", { 59 | v$metadata <- list(url = "potatoes") 60 | p <- pr() %>% vetiver_api(v) 61 | car_spec <- p$getApiSpec() 62 | expect_equal(car_spec$info$description, 63 | "A luz module with 11 parameters") 64 | post_spec <- car_spec$paths$`/predict`$post 65 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 66 | expect_equal(as.character(post_spec$summary), 67 | "Return predictions from model using 10 features") 68 | get_spec <- car_spec$paths$`/pin-url`$get 69 | expect_equal(as.character(get_spec$summary), 70 | "Get URL of pinned vetiver model") 71 | 72 | }) 73 | 74 | test_that("create plumber.R for keras", { 75 | skip_on_cran() 76 | b <- board_folder(path = tmp_dir) 77 | vetiver_pin_write(b, v) 78 | tmp <- tempfile() 79 | vetiver_write_plumber(b, "cars-luz", file = tmp) 80 | expect_snapshot( 81 | cat(readr::read_lines(tmp), sep = "\n"), 82 | transform = redact_vetiver 83 | ) 84 | expect_snapshot( 85 | cat(readr::read_lines(fs::path(fs::path_dir(tmp), ".Renviron")), sep = "\n"), 86 | transform = redact_vetiver 87 | ) 88 | }) 89 | -------------------------------------------------------------------------------- /tests/testthat/test-mlr3.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("mlr3") 2 | 3 | test_that("mlr3 learner description can be printed", { 4 | task = mlr3::tsk("pima") 5 | learner = mlr3::lrn("classif.rpart") 6 | learner$train(task) 7 | v <- vetiver_model(learner, "rpart_pima") 8 | expect_snapshot(v) 9 | }) 10 | 11 | test_that("mlr3 learners can be pinned", { 12 | task = mlr3::tsk("pima") 13 | learner = mlr3::lrn("classif.rpart") 14 | learner$train(task) 15 | 16 | v <- vetiver_model(learner, "rpart_pima") 17 | b <- board_temp() 18 | vetiver_pin_write(b, v) 19 | pinned <- pin_read(b, "rpart_pima") 20 | 21 | expect_length(pinned, 2) 22 | expect_equal(class(pinned$model)[1], "LearnerClassifRpart") 23 | expect_equal(nrow(pinned$prototype), 0) 24 | expect_equal(names(pinned$prototype), task$feature_names) 25 | expect_equal(pin_meta(b, "rpart_pima")$user$required_pkgs, 26 | c("mlr3", "rpart")) 27 | }) 28 | 29 | test_that("learners from mlr3learners can be pinned", { 30 | skip_if_not_installed("mlr3learners") 31 | skip_if_not_installed("xgboost") 32 | 33 | task = mlr3::tsk("spam") 34 | learner = mlr3learners::LearnerClassifXgboost$new() 35 | learner$train(task) 36 | 37 | v <- vetiver_model(learner, "logreg_spam") 38 | b <- board_temp() 39 | vetiver_pin_write(b, v) 40 | pinned <- pin_read(b, "logreg_spam") 41 | 42 | expect_length(pinned, 2) 43 | expect_equal(class(pinned$model)[1], "LearnerClassifXgboost") 44 | expect_equal(nrow(pinned$prototype), 0) 45 | expect_equal(names(pinned$prototype), task$feature_names) 46 | expect_equal(pin_meta(b, "logreg_spam")$user$required_pkgs, 47 | c("mlr3", "mlr3learners", "xgboost")) 48 | }) 49 | 50 | 51 | test_that("create plumber.R for mlr3", { 52 | skip_on_cran() 53 | 54 | task = mlr3::tsk("pima") 55 | learner = mlr3::lrn("classif.rpart") 56 | learner$train(task) 57 | 58 | v <- vetiver_model(learner, "rpart_pima") 59 | b <- board_folder(path = tmp_dir) 60 | vetiver_pin_write(b, v) 61 | tmp <- tempfile() 62 | vetiver_write_plumber(b, "rpart_pima", file = tmp) 63 | expect_snapshot( 64 | cat(readr::read_lines(tmp), sep = "\n"), 65 | transform = redact_vetiver 66 | ) 67 | }) 68 | -------------------------------------------------------------------------------- /tests/testthat/test-predict.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | skip_if_not_installed(c("pingr", "httr", "httpuv", "plumber")) 3 | 4 | library(plumber) 5 | 6 | pr <- pr() %>% vetiver_api(v, debug = TRUE) 7 | rs <- local_plumber_session(pr, port) 8 | 9 | ## on GH actions, it can take A WHILE for the API to come up on some architectures: 10 | for (i in 1:100) { 11 | if (pingr::is_up(root_path, port)) break 12 | Sys.sleep(0.1) 13 | } 14 | 15 | test_that("router has health check endpoint", { 16 | r <- httr::GET(root_path, port = port, path = "ping") 17 | expect_equal(r$status_code, 200) 18 | }) 19 | 20 | test_that("router has metadata endpoint", { 21 | r <- httr::GET(root_path, port = port, path = "metadata") 22 | metadata <- jsonlite::fromJSON(httr::content(r, as = "text", encoding = "UTF-8")) 23 | expect_equal(r$status_code, 200) 24 | expect_equal(names(metadata), c("user", "version", "url", "required_pkgs")) 25 | }) 26 | 27 | test_that("router has prototype endpoint", { 28 | r <- httr::GET(root_path, port = port, path = "prototype") 29 | prototype <- httr::content(r, as = "text", encoding = "UTF-8") 30 | expect_equal(r$status_code, 200) 31 | expect_equal( 32 | jsonlite::fromJSON(prototype), 33 | purrr::map(v$prototype, cereal::cereal_encode) 34 | ) 35 | expect_equal(cereal::cereal_from_json(prototype), v$prototype) 36 | }) 37 | 38 | test_that("can predict on basic vetiver router", { 39 | endpoint <- vetiver_endpoint(paste0(root_path, ":", port, "/predict")) 40 | expect_s3_class(endpoint, "vetiver_endpoint") 41 | expect_snapshot(print(endpoint), transform = redact_port) 42 | 43 | preds <- predict(endpoint, mtcars[10:17, 2:3]) 44 | expect_s3_class(preds, "tbl_df") 45 | expect_equal(nrow(preds), 8) 46 | expect_equal(ncol(preds), 1) 47 | 48 | aug <- augment(endpoint, mtcars[10:17, 2:3]) 49 | expect_s3_class(aug, "tbl_df") 50 | expect_equal(nrow(aug), 8) 51 | expect_equal(ncol(aug), 3) 52 | }) 53 | 54 | test_that("can predict with single or NA values", { 55 | endpoint <- vetiver_endpoint(paste0(root_path, ":", port, "/predict")) 56 | 57 | preds1 <- predict(endpoint, mtcars[10, 2:3]) 58 | expect_s3_class(preds1, "tbl_df") 59 | expect_equal(nrow(preds1), 1) 60 | expect_equal(ncol(preds1), 1) 61 | 62 | preds2 <- predict(endpoint, data.frame(cyl = c(NA_real_, NA_real_), 63 | disp = c(100, 200))) 64 | expect_s3_class(preds2, "tbl_df") 65 | expect_equal(nrow(preds2), 2) 66 | expect_equal(ncol(preds2), 1) 67 | }) 68 | 69 | test_that("get correct errors", { 70 | endpoint <- vetiver_endpoint(paste0(root_path, ":", port, "/predict")) 71 | expect_snapshot(predict(endpoint, mtcars[, 2:4]), error = TRUE) 72 | expect_snapshot(predict(endpoint, mtcars[, 3:5]), error = TRUE) 73 | }) 74 | 75 | -------------------------------------------------------------------------------- /tests/testthat/test-ranger.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("ranger") 2 | skip_if_not_installed("plumber") 3 | 4 | library(plumber) 5 | set.seed(123) 6 | cars_rf <- ranger::ranger(mpg ~ ., data = mtcars, quantreg = TRUE) 7 | v <- vetiver_model(cars_rf, "cars3", prototype_data = mtcars[,-1]) 8 | 9 | test_that("can print ranger model", { 10 | expect_snapshot(v) 11 | }) 12 | 13 | test_that("error for no prototype_data with ranger", { 14 | expect_snapshot(vetiver_model(cars_rf, "cars3"), error = TRUE) 15 | }) 16 | 17 | test_that("can predict ranger model", { 18 | preds <- predict(v, mtcars[,-1]) 19 | expect_equal(length(preds$predictions), 32) 20 | expect_equal(mean(preds$predictions), 20.1, tolerance = 0.1) 21 | }) 22 | 23 | 24 | test_that("can pin an ranger model", { 25 | b <- board_temp() 26 | vetiver_pin_write(b, v) 27 | pinned <- pin_read(b, "cars3") 28 | expect_equal(pinned$model, butcher::butcher(cars_rf)) 29 | expect_equal( 30 | pinned$prototype, 31 | vctrs::vec_slice(tibble::as_tibble(mtcars[,-1]), 0) 32 | ) 33 | expect_equal( 34 | pin_meta(b, "cars3")$user$required_pkgs, 35 | "ranger" 36 | ) 37 | }) 38 | 39 | test_that("default endpoint for ranger", { 40 | p <- pr() %>% vetiver_api(v) 41 | p_routes <- p$routes[-1] 42 | expect_api_routes(p_routes) 43 | }) 44 | 45 | test_that("default OpenAPI spec", { 46 | v$metadata <- list(url = "potatoes") 47 | p <- pr() %>% vetiver_api(v) 48 | car_spec <- p$getApiSpec() 49 | expect_equal(car_spec$info$description, 50 | "A ranger regression model") 51 | post_spec <- car_spec$paths$`/predict`$post 52 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 53 | expect_equal(as.character(post_spec$summary), 54 | "Return predictions from model using 10 features") 55 | get_spec <- car_spec$paths$`/pin-url`$get 56 | expect_equal(as.character(get_spec$summary), 57 | "Get URL of pinned vetiver model") 58 | 59 | }) 60 | 61 | test_that("create plumber.R for ranger", { 62 | skip_on_cran() 63 | b <- board_folder(path = tmp_dir) 64 | vetiver_pin_write(b, v) 65 | tmp <- tempfile() 66 | vetiver_write_plumber(b, "cars3", file = tmp) 67 | expect_snapshot( 68 | cat(readr::read_lines(tmp), sep = "\n"), 69 | transform = redact_vetiver 70 | ) 71 | }) 72 | 73 | -------------------------------------------------------------------------------- /tests/testthat/test-recipe.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("recipes") 2 | skip_if_not_installed("plumber") 3 | 4 | library(plumber) 5 | library(recipes) 6 | 7 | trained_rec <- 8 | recipe(mpg ~ disp + wt, mtcars) %>% 9 | step_ns(wt) %>% 10 | prep(retain = FALSE) 11 | 12 | v <- vetiver_model(trained_rec, "car-splines") 13 | 14 | test_that("can print recipe", { 15 | expect_snapshot(v) 16 | }) 17 | 18 | test_that("can pin a recipe", { 19 | b <- board_temp() 20 | vetiver_pin_write(b, v) 21 | pinned <- pin_read(b, "car-splines") 22 | expect_equal( 23 | pinned, 24 | list( 25 | model = bundle::bundle(butcher::butcher(trained_rec)), 26 | prototype = vctrs::vec_slice(tibble::as_tibble(mtcars[c("disp", "wt")]), 0) 27 | ) 28 | ) 29 | expect_equal( 30 | pin_meta(b, "car-splines")$user$required_pkgs, 31 | c("recipes") 32 | ) 33 | }) 34 | 35 | test_that("default endpoint for recipe", { 36 | p <- pr() %>% vetiver_api(v) 37 | p_routes <- p$routes[-1] 38 | expect_api_routes(p_routes) 39 | }) 40 | 41 | test_that("default OpenAPI spec", { 42 | v$metadata <- list(url = "potatoes") 43 | p <- pr() %>% vetiver_api(v) 44 | car_spec <- p$getApiSpec() 45 | expect_equal(car_spec$info$description, 46 | "A feature engineering recipe with 1 step") 47 | post_spec <- car_spec$paths$`/predict`$post 48 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 49 | expect_equal(as.character(post_spec$summary), 50 | "Return predictions from model using 2 features") 51 | get_spec <- car_spec$paths$`/pin-url`$get 52 | expect_equal(as.character(get_spec$summary), 53 | "Get URL of pinned vetiver model") 54 | 55 | }) 56 | 57 | test_that("create plumber.R for recipe", { 58 | skip_on_cran() 59 | b <- board_folder(path = tmp_dir) 60 | vetiver_pin_write(b, v) 61 | tmp <- tempfile() 62 | vetiver_write_plumber(b, "car-splines", file = tmp) 63 | expect_snapshot( 64 | cat(readr::read_lines(tmp), sep = "\n"), 65 | transform = redact_vetiver 66 | ) 67 | }) 68 | 69 | -------------------------------------------------------------------------------- /tests/testthat/test-rsconnect.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("rsconnect") 2 | 3 | describe("create rsconnect bundle", { 4 | local_mocked_bindings(writeManifest = mock_write_manifest, .package = "rsconnect") 5 | tar_file <- fs::file_temp(pattern = "bundle", tmp_dir = tmp_dir, ext = ".tar.gz") 6 | 7 | b <- board_folder(path = tmp_dir) 8 | cars_lm <- lm(mpg ~ cyl + disp, data = mtcars) 9 | v <- vetiver_model(cars_lm, "cars1") 10 | vetiver_pin_write(b, v) 11 | bundle <- vetiver_create_rsconnect_bundle(b, "cars1", filename = tar_file) 12 | 13 | it("can create tar file", { 14 | expect_identical(tar_file, bundle) 15 | expect_true(file.exists(tar_file)) 16 | }) 17 | 18 | it("contains correct plumber file", { 19 | utils::untar(bundle, "plumber.R", exdir = tmp_dir) 20 | expect_snapshot( 21 | cat(readr::read_lines(fs::path(tmp_dir, "plumber.R")), sep = "\n"), 22 | transform = redact_vetiver 23 | ) 24 | }) 25 | 26 | }) 27 | -------------------------------------------------------------------------------- /tests/testthat/test-stacks.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | skip_if_not_installed("stacks") 3 | skip_if_not_installed("plumber") 4 | 5 | library(plumber) 6 | library(stacks) 7 | 8 | data("tree_frogs", package = "stacks") 9 | tree_test <- tree_frogs %>% 10 | dplyr::select(-hatched, -latency, -clutch) 11 | 12 | frog_reg <- 13 | stacks() %>% 14 | add_candidates(reg_res_lr) %>% 15 | add_candidates(reg_res_sp) %>% 16 | blend_predictions(penalty = 20) %>% 17 | fit_members() 18 | 19 | v <- vetiver_model(frog_reg, "frog-stack") 20 | 21 | test_that("can print stacks model", { 22 | expect_snapshot(v) 23 | }) 24 | 25 | test_that("can predict stacks model", { 26 | preds <- predict(v, tree_test[2:10,]) 27 | expect_s3_class(preds, "tbl_df") 28 | expect_equal(mean(preds$.pred), 142, tolerance = 1) 29 | }) 30 | 31 | test_that("can pin a stacks model", { 32 | b <- board_temp() 33 | vetiver_pin_write(b, v) 34 | pinned <- pin_read(b, "frog-stack") 35 | expect_equal( 36 | pinned, 37 | list( 38 | model = bundle::bundle(butcher::butcher(frog_reg)), 39 | prototype = vctrs::vec_ptype(tree_test) 40 | ) 41 | ) 42 | expect_equal( 43 | pin_meta(b, "frog-stack")$user$required_pkgs, 44 | c("glmnet", "parsnip", "recipes", "stacks", "stats", "workflows") 45 | ) 46 | }) 47 | 48 | test_that("default OpenAPI spec", { 49 | v$metadata <- list(url = "potatoes") 50 | p <- pr() %>% vetiver_api(v) 51 | frog_spec <- p$getApiSpec() 52 | expect_equal(frog_spec$info$description, 53 | "A regression stacked ensemble with 3 members") 54 | post_spec <- frog_spec$paths$`/predict`$post 55 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 56 | expect_equal(as.character(post_spec$summary), 57 | "Return predictions from model using 4 features") 58 | get_spec <- frog_spec$paths$`/pin-url`$get 59 | expect_equal(as.character(get_spec$summary), 60 | "Get URL of pinned vetiver model") 61 | 62 | }) 63 | 64 | test_that("create plumber.R for stacks", { 65 | skip_on_cran() 66 | b <- board_folder(path = tmp_dir) 67 | vetiver_pin_write(b, v) 68 | tmp <- tempfile() 69 | vetiver_write_plumber(b, "frog-stack", file = tmp) 70 | expect_snapshot( 71 | cat(readr::read_lines(tmp), sep = "\n"), 72 | transform = redact_vetiver 73 | ) 74 | }) 75 | 76 | -------------------------------------------------------------------------------- /tests/testthat/test-tidymodels.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("workflows") 2 | skip_if_not_installed("parsnip") 3 | skip_if_not_installed("ranger") 4 | skip_if_not_installed("plumber") 5 | 6 | library(plumber) 7 | library(workflows) 8 | library(parsnip) 9 | 10 | rf_spec <- rand_forest(mode = "regression") %>% 11 | set_engine("ranger") 12 | 13 | set.seed(123) 14 | mtcars_wf <- workflow() %>% 15 | add_model(rf_spec) %>% 16 | add_formula(mpg ~ .) %>% 17 | fit(data = mtcars) 18 | 19 | v <- vetiver_model(mtcars_wf, "cars_wf") 20 | 21 | test_that("can print tidymodels model", { 22 | expect_snapshot(v) 23 | }) 24 | 25 | test_that("can predict tidymodels model", { 26 | preds <- predict(v, mtcars) 27 | expect_s3_class(preds, "tbl_df") 28 | expect_equal(mean(preds$.pred), 20.1, tolerance = 0.1) 29 | }) 30 | 31 | test_that("can pin a tidymodels model", { 32 | b <- board_temp() 33 | vetiver_pin_write(b, v) 34 | pinned <- pin_read(b, "cars_wf") 35 | expect_equal( 36 | pinned, 37 | list( 38 | model = bundle::bundle(butcher::butcher(mtcars_wf)), 39 | prototype = vctrs::vec_slice(tibble::as_tibble(mtcars[,2:11]), 0) 40 | ) 41 | ) 42 | expect_equal( 43 | pin_meta(b, "cars_wf")$user$required_pkgs, 44 | c("parsnip", "ranger", "workflows") 45 | ) 46 | }) 47 | 48 | test_that("default endpoint for tidymodels", { 49 | p <- pr() %>% vetiver_api(v) 50 | p_routes <- p$routes[-1] 51 | expect_api_routes(p_routes) 52 | }) 53 | 54 | test_that("default OpenAPI spec", { 55 | v$metadata <- list(url = "potatoes") 56 | p <- pr() %>% vetiver_api(v) 57 | car_spec <- p$getApiSpec() 58 | expect_equal(car_spec$info$description, 59 | "A ranger regression modeling workflow") 60 | post_spec <- car_spec$paths$`/predict`$post 61 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 62 | expect_equal(as.character(post_spec$summary), 63 | "Return predictions from model using 10 features") 64 | get_spec <- car_spec$paths$`/pin-url`$get 65 | expect_equal(as.character(get_spec$summary), 66 | "Get URL of pinned vetiver model") 67 | 68 | }) 69 | 70 | test_that("create plumber.R for tidymodels", { 71 | skip_on_cran() 72 | b <- board_folder(path = tmp_dir) 73 | vetiver_pin_write(b, v) 74 | tmp <- tempfile() 75 | vetiver_write_plumber(b, "cars_wf", file = tmp) 76 | expect_snapshot( 77 | cat(readr::read_lines(tmp), sep = "\n"), 78 | transform = redact_vetiver 79 | ) 80 | }) 81 | 82 | -------------------------------------------------------------------------------- /tests/testthat/test-type-convert.R: -------------------------------------------------------------------------------- 1 | test_that("all numeric", { 2 | expect_equal( 3 | vetiver_type_convert(mtcars, vctrs::vec_slice(mtcars, 0)), 4 | tibble::tibble(mtcars) 5 | ) 6 | }) 7 | 8 | test_that("NULL ptype, for save_ptype = FALSE", { 9 | expect_equal( 10 | vetiver_type_convert(chickwts, NULL), 11 | chickwts 12 | ) 13 | }) 14 | 15 | test_that("missing variables", { 16 | expect_snapshot_error( 17 | vetiver_type_convert(mtcars[,2:3], vctrs::vec_slice(mtcars, 0)), 18 | ) 19 | }) ## extra variables are caught by hardhat::scream() 20 | 21 | test_that("a factor", { 22 | chicks <- chickwts 23 | chicks$feed <- as.character(chicks$feed) 24 | 25 | expect_equal( 26 | vetiver_type_convert(chicks, vctrs::vec_slice(chickwts, 0)), 27 | tibble::tibble(chickwts) 28 | ) 29 | }) 30 | 31 | test_that("a factor plus a bad character", { 32 | teeth <- ToothGrowth 33 | teeth$supp <- as.character(teeth$supp) 34 | teeth$dose <- as.character(teeth$dose) 35 | 36 | expect_equal( 37 | vetiver_type_convert(teeth, vctrs::vec_slice(ToothGrowth, 0)), 38 | tibble::tibble(ToothGrowth) 39 | ) 40 | 41 | expect_snapshot_error( 42 | vetiver_type_convert(tibble::tibble(len = 4.2, supp = "ZZ", dose = 0.1), 43 | vctrs::vec_slice(ToothGrowth, 0)) 44 | ) 45 | }) 46 | 47 | test_that("a date", { 48 | 49 | many_dates <- tibble::tibble( 50 | x = as.Date("2021-01-01") + 0:10, 51 | y = as.Date("1980-02-14") + 0:10, 52 | z = as.POSIXct("1950-06-01", tz = "UTC") + 0:10 53 | ) 54 | 55 | new_data <- tibble::tibble( 56 | x = "2021-01-15", y = "1980-03-01", z = "1950-06-01 00:00:15" 57 | ) 58 | 59 | bad_data <- tibble::tibble( 60 | x = "potato", y = "1980-03-01", z = "1950-06-01 00:00:15" 61 | ) 62 | 63 | expect_equal( 64 | vetiver_type_convert(new_data, vctrs::vec_slice(many_dates, 0)), 65 | tibble::tibble( 66 | x = as.Date("2021-01-15"), 67 | y = as.Date("1980-03-01"), 68 | z = as.POSIXct("1950-06-01 00:00:15", tz = "UTC") 69 | ) 70 | ) 71 | 72 | expect_snapshot_error( 73 | vetiver_type_convert(bad_data, vctrs::vec_slice(many_dates, 0)), 74 | ) 75 | }) 76 | -------------------------------------------------------------------------------- /tests/testthat/test-xgboost.R: -------------------------------------------------------------------------------- 1 | skip_if_not_installed("xgboost") 2 | skip_if_not_installed("plumber") 3 | 4 | library(plumber) 5 | set.seed(123) 6 | cars_xgb <- xgboost::xgboost(as.matrix(mtcars[,-1]), 7 | mtcars$mpg, nrounds = 3, 8 | objective = "reg:squarederror") 9 | v <- vetiver_model(cars_xgb, "cars2") 10 | 11 | test_that("can print xgboost model", { 12 | expect_snapshot(v) 13 | }) 14 | 15 | test_that("can predict xgboost model", { 16 | cars_matrix <- as.matrix(mtcars[,-1]) 17 | preds <- predict(v, cars_matrix) 18 | expect_equal(length(preds), 32) 19 | preds2 <- predict(cars_xgb, cars_matrix) 20 | expect_equal(preds, preds2) 21 | }) 22 | 23 | 24 | test_that("can pin an xgboost model", { 25 | b <- board_temp() 26 | vetiver_pin_write(b, v) 27 | pinned <- pin_read(b, "cars2") 28 | expect_equal( 29 | pinned, 30 | list( 31 | model = bundle::bundle(cars_xgb), 32 | prototype = vctrs::vec_slice(tibble::as_tibble(mtcars[,2:11]), 0) 33 | ) 34 | ) 35 | expect_equal( 36 | pin_meta(b, "cars2")$user$required_pkgs, 37 | "xgboost" 38 | ) 39 | }) 40 | 41 | test_that("default endpoint for xgboost", { 42 | p <- pr() %>% vetiver_api(v) 43 | p_routes <- p$routes[-1] 44 | expect_api_routes(p_routes) 45 | }) 46 | 47 | test_that("default OpenAPI spec", { 48 | v$metadata <- list(url = "potatoes") 49 | p <- pr() %>% vetiver_api(v) 50 | car_spec <- p$getApiSpec() 51 | expect_equal(car_spec$info$description, 52 | "An xgboost reg:squarederror model") 53 | post_spec <- car_spec$paths$`/predict`$post 54 | expect_equal(names(post_spec), c("summary", "requestBody", "responses")) 55 | expect_equal(as.character(post_spec$summary), 56 | "Return predictions from model using 10 features") 57 | get_spec <- car_spec$paths$`/pin-url`$get 58 | expect_equal(as.character(get_spec$summary), 59 | "Get URL of pinned vetiver model") 60 | 61 | }) 62 | 63 | test_that("create plumber.R for xgboost", { 64 | skip_on_cran() 65 | b <- board_folder(path = tmp_dir) 66 | vetiver_pin_write(b, v) 67 | tmp <- tempfile() 68 | vetiver_write_plumber(b, "cars2", file = tmp) 69 | expect_snapshot( 70 | cat(readr::read_lines(tmp), sep = "\n"), 71 | transform = redact_vetiver 72 | ) 73 | }) 74 | 75 | -------------------------------------------------------------------------------- /vetiver-r.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 4 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | --------------------------------------------------------------------------------