frogml-cli 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. frogml_cli/__init__.py +10 -0
  2. frogml_cli/cli.py +40 -0
  3. frogml_cli/commands/__init__.py +0 -0
  4. frogml_cli/commands/_logic/__init__.py +0 -0
  5. frogml_cli/commands/_logic/tools.py +6 -0
  6. frogml_cli/commands/alerts/__init__.py +0 -0
  7. frogml_cli/commands/alerts/alerts_commnad_group.py +18 -0
  8. frogml_cli/commands/alerts/delete/__init__.py +0 -0
  9. frogml_cli/commands/alerts/delete/_logic.py +5 -0
  10. frogml_cli/commands/alerts/delete/ui.py +10 -0
  11. frogml_cli/commands/alerts/list/__init__.py +0 -0
  12. frogml_cli/commands/alerts/list/_logic.py +23 -0
  13. frogml_cli/commands/alerts/list/ui.py +17 -0
  14. frogml_cli/commands/alerts/register/__init__.py +0 -0
  15. frogml_cli/commands/alerts/register/_logic.py +72 -0
  16. frogml_cli/commands/alerts/register/ui.py +30 -0
  17. frogml_cli/commands/audience/__init__.py +0 -0
  18. frogml_cli/commands/audience/_logic/__init__.py +0 -0
  19. frogml_cli/commands/audience/_logic/config/__init__.py +0 -0
  20. frogml_cli/commands/audience/_logic/config/config_base.py +15 -0
  21. frogml_cli/commands/audience/_logic/config/parser.py +30 -0
  22. frogml_cli/commands/audience/_logic/config/v1/__init__.py +0 -0
  23. frogml_cli/commands/audience/_logic/config/v1/audience_config.py +25 -0
  24. frogml_cli/commands/audience/_logic/config/v1/conditions_config.py +59 -0
  25. frogml_cli/commands/audience/_logic/config/v1/config_v1.py +23 -0
  26. frogml_cli/commands/audience/_logic/config/v1/route_config.py +15 -0
  27. frogml_cli/commands/audience/_logic/config/v1/spec.py +9 -0
  28. frogml_cli/commands/audience/audience_api_dump.py +86 -0
  29. frogml_cli/commands/audience/audience_commands_group.py +30 -0
  30. frogml_cli/commands/audience/create/__init__.py +0 -0
  31. frogml_cli/commands/audience/create/logic.py +41 -0
  32. frogml_cli/commands/audience/create/ui.py +21 -0
  33. frogml_cli/commands/audience/delete/__init__.py +0 -0
  34. frogml_cli/commands/audience/delete/logic.py +13 -0
  35. frogml_cli/commands/audience/delete/ui.py +17 -0
  36. frogml_cli/commands/audience/get/__init__.py +0 -0
  37. frogml_cli/commands/audience/get/logic.py +14 -0
  38. frogml_cli/commands/audience/get/ui.py +25 -0
  39. frogml_cli/commands/audience/list/__init__.py +0 -0
  40. frogml_cli/commands/audience/list/logic.py +16 -0
  41. frogml_cli/commands/audience/list/ui.py +26 -0
  42. frogml_cli/commands/audience/update/__init__.py +0 -0
  43. frogml_cli/commands/audience/update/logic.py +37 -0
  44. frogml_cli/commands/audience/update/ui.py +26 -0
  45. frogml_cli/commands/auto_scalling/__init__.py +0 -0
  46. frogml_cli/commands/auto_scalling/_logic/__init__.py +0 -0
  47. frogml_cli/commands/auto_scalling/_logic/config/__init__.py +3 -0
  48. frogml_cli/commands/auto_scalling/_logic/config/config.py +152 -0
  49. frogml_cli/commands/auto_scalling/_logic/config/parser.py +21 -0
  50. frogml_cli/commands/auto_scalling/attach/__init__.py +0 -0
  51. frogml_cli/commands/auto_scalling/attach/_logic.py +43 -0
  52. frogml_cli/commands/auto_scalling/attach/ui.py +21 -0
  53. frogml_cli/commands/auto_scalling/autoscaling_commands_group.py +15 -0
  54. frogml_cli/commands/automations/__init__.py +0 -0
  55. frogml_cli/commands/automations/automations_commands_group.py +30 -0
  56. frogml_cli/commands/automations/delete/__init__.py +0 -0
  57. frogml_cli/commands/automations/delete/_logic.py +6 -0
  58. frogml_cli/commands/automations/delete/ui.py +23 -0
  59. frogml_cli/commands/automations/executions/__init__.py +0 -0
  60. frogml_cli/commands/automations/executions/executions_commands_group.py +14 -0
  61. frogml_cli/commands/automations/executions/list/__init__.py +0 -0
  62. frogml_cli/commands/automations/executions/list/_logic.py +8 -0
  63. frogml_cli/commands/automations/executions/list/ui.py +27 -0
  64. frogml_cli/commands/automations/list/__init__.py +0 -0
  65. frogml_cli/commands/automations/list/_logic.py +36 -0
  66. frogml_cli/commands/automations/list/ui.py +21 -0
  67. frogml_cli/commands/automations/register/__init__.py +0 -0
  68. frogml_cli/commands/automations/register/_logic.py +43 -0
  69. frogml_cli/commands/automations/register/ui.py +44 -0
  70. frogml_cli/commands/config/__init__.py +0 -0
  71. frogml_cli/commands/config/add/__init__.py +0 -0
  72. frogml_cli/commands/config/add/ui.py +62 -0
  73. frogml_cli/commands/config/config_commands_group.py +11 -0
  74. frogml_cli/commands/feature_store/__init__.py +0 -0
  75. frogml_cli/commands/feature_store/backfill/__init__.py +0 -0
  76. frogml_cli/commands/feature_store/backfill/_logic.py +140 -0
  77. frogml_cli/commands/feature_store/backfill/ui.py +129 -0
  78. frogml_cli/commands/feature_store/delete/__init__.py +0 -0
  79. frogml_cli/commands/feature_store/delete/_logic.py +107 -0
  80. frogml_cli/commands/feature_store/delete/ui.py +40 -0
  81. frogml_cli/commands/feature_store/execution/__init__.py +0 -0
  82. frogml_cli/commands/feature_store/execution/ui.py +19 -0
  83. frogml_cli/commands/feature_store/feature_store_command_group.py +29 -0
  84. frogml_cli/commands/feature_store/list/__init__.py +0 -0
  85. frogml_cli/commands/feature_store/list/ui.py +140 -0
  86. frogml_cli/commands/feature_store/pause/__init__.py +0 -0
  87. frogml_cli/commands/feature_store/pause/ui.py +18 -0
  88. frogml_cli/commands/feature_store/register/__init__.py +0 -0
  89. frogml_cli/commands/feature_store/register/_logic.py +369 -0
  90. frogml_cli/commands/feature_store/register/ui.py +111 -0
  91. frogml_cli/commands/feature_store/resume/__init__.py +0 -0
  92. frogml_cli/commands/feature_store/resume/ui.py +18 -0
  93. frogml_cli/commands/feature_store/trigger/__init__.py +0 -0
  94. frogml_cli/commands/feature_store/trigger/ui.py +39 -0
  95. frogml_cli/commands/models/__init__.py +0 -0
  96. frogml_cli/commands/models/build/__init__.py +0 -0
  97. frogml_cli/commands/models/build/_logic/__init__.py +0 -0
  98. frogml_cli/commands/models/build/_logic/build_steps.py +42 -0
  99. frogml_cli/commands/models/build/_logic/client_logs/__init__.py +0 -0
  100. frogml_cli/commands/models/build/_logic/client_logs/cli_phase_run_handler.py +123 -0
  101. frogml_cli/commands/models/build/_logic/client_logs/cli_trigger_build_logger.py +19 -0
  102. frogml_cli/commands/models/build/_logic/client_logs/logger.py +88 -0
  103. frogml_cli/commands/models/build/_logic/client_logs/messages.py +36 -0
  104. frogml_cli/commands/models/build/_logic/client_logs/spinner.py +14 -0
  105. frogml_cli/commands/models/build/_logic/client_logs/trigger_build_logger.py +54 -0
  106. frogml_cli/commands/models/build/_logic/client_logs/utils.py +12 -0
  107. frogml_cli/commands/models/build/_logic/phase/__init__.py +0 -0
  108. frogml_cli/commands/models/build/_logic/phase/a_fetch_model_code/__init__.py +20 -0
  109. frogml_cli/commands/models/build/_logic/phase/a_fetch_model_code/get_sdk_version_step.py +15 -0
  110. frogml_cli/commands/models/build/_logic/phase/b_remote_register_frogml_build/__init__.py +16 -0
  111. frogml_cli/commands/models/build/_logic/phase/c_deploy/__init__.py +6 -0
  112. frogml_cli/commands/models/build/_logic/phase/c_deploy/build_polling_status.py +55 -0
  113. frogml_cli/commands/models/build/_logic/phase/c_deploy/deploy_build.py +61 -0
  114. frogml_cli/commands/models/build/_logic/util/__init__.py +0 -0
  115. frogml_cli/commands/models/build/_logic/util/protobuf_factory.py +45 -0
  116. frogml_cli/commands/models/build/_logic/util/step_decorator.py +60 -0
  117. frogml_cli/commands/models/build/_logic/util/text.py +9 -0
  118. frogml_cli/commands/models/build/_logic/wait_until_finished.py +27 -0
  119. frogml_cli/commands/models/build/ui.py +337 -0
  120. frogml_cli/commands/models/builds/__init__.py +0 -0
  121. frogml_cli/commands/models/builds/builds_commands_group.py +16 -0
  122. frogml_cli/commands/models/builds/cancel/__init__.py +0 -0
  123. frogml_cli/commands/models/builds/cancel/_logic.py +5 -0
  124. frogml_cli/commands/models/builds/cancel/ui.py +15 -0
  125. frogml_cli/commands/models/builds/logs/__init__.py +0 -0
  126. frogml_cli/commands/models/builds/logs/ui.py +35 -0
  127. frogml_cli/commands/models/builds/status/__init__.py +0 -0
  128. frogml_cli/commands/models/builds/status/_logic.py +6 -0
  129. frogml_cli/commands/models/builds/status/ui.py +39 -0
  130. frogml_cli/commands/models/create/__init__.py +0 -0
  131. frogml_cli/commands/models/create/_logic.py +40 -0
  132. frogml_cli/commands/models/create/ui.py +46 -0
  133. frogml_cli/commands/models/delete/__init__.py +0 -0
  134. frogml_cli/commands/models/delete/_logic.py +18 -0
  135. frogml_cli/commands/models/delete/ui.py +25 -0
  136. frogml_cli/commands/models/deployments/__init__.py +0 -0
  137. frogml_cli/commands/models/deployments/deploy/__init__.py +0 -0
  138. frogml_cli/commands/models/deployments/deploy/_logic/__init__.py +0 -0
  139. frogml_cli/commands/models/deployments/deploy/_logic/advance_deployment_options_handler.py +31 -0
  140. frogml_cli/commands/models/deployments/deploy/_logic/base_deploy_executor.py +70 -0
  141. frogml_cli/commands/models/deployments/deploy/_logic/deploy_config.py +261 -0
  142. frogml_cli/commands/models/deployments/deploy/_logic/deployment.py +407 -0
  143. frogml_cli/commands/models/deployments/deploy/_logic/deployment_message_helpers.py +116 -0
  144. frogml_cli/commands/models/deployments/deploy/_logic/deployment_response_handler.py +156 -0
  145. frogml_cli/commands/models/deployments/deploy/_logic/deployment_size_mapper.py +98 -0
  146. frogml_cli/commands/models/deployments/deploy/_logic/get_latest_successful_build.py +28 -0
  147. frogml_cli/commands/models/deployments/deploy/_logic/local_deployment.py +193 -0
  148. frogml_cli/commands/models/deployments/deploy/batch/__init__.py +0 -0
  149. frogml_cli/commands/models/deployments/deploy/batch/_logic/__init__.py +0 -0
  150. frogml_cli/commands/models/deployments/deploy/batch/_logic/advanced_deployment_mapper.py +15 -0
  151. frogml_cli/commands/models/deployments/deploy/batch/_logic/deploy_executor.py +24 -0
  152. frogml_cli/commands/models/deployments/deploy/batch/ui.py +119 -0
  153. frogml_cli/commands/models/deployments/deploy/deploy_commands_group.py +19 -0
  154. frogml_cli/commands/models/deployments/deploy/realtime/__init__.py +0 -0
  155. frogml_cli/commands/models/deployments/deploy/realtime/_logic/__init__.py +0 -0
  156. frogml_cli/commands/models/deployments/deploy/realtime/_logic/advanced_deployment_mapper.py +21 -0
  157. frogml_cli/commands/models/deployments/deploy/realtime/_logic/deploy_executor.py +24 -0
  158. frogml_cli/commands/models/deployments/deploy/realtime/_logic/serving_strategy_mapper.py +75 -0
  159. frogml_cli/commands/models/deployments/deploy/realtime/ui.py +202 -0
  160. frogml_cli/commands/models/deployments/deploy/streaming/__init__.py +0 -0
  161. frogml_cli/commands/models/deployments/deploy/streaming/_logic/__init__.py +0 -0
  162. frogml_cli/commands/models/deployments/deploy/streaming/_logic/deploy_executor.py +24 -0
  163. frogml_cli/commands/models/deployments/deploy/streaming/_logic/serving_strategy_mapper.py +38 -0
  164. frogml_cli/commands/models/deployments/deploy/streaming/ui.py +206 -0
  165. frogml_cli/commands/models/deployments/undeploy/__init__.py +0 -0
  166. frogml_cli/commands/models/deployments/undeploy/_logic/__init__.py +0 -0
  167. frogml_cli/commands/models/deployments/undeploy/_logic/request_undeploy.py +249 -0
  168. frogml_cli/commands/models/deployments/undeploy/ui.py +61 -0
  169. frogml_cli/commands/models/describe/__init__.py +0 -0
  170. frogml_cli/commands/models/describe/_logic.py +169 -0
  171. frogml_cli/commands/models/describe/ui.py +35 -0
  172. frogml_cli/commands/models/executions/__init__.py +0 -0
  173. frogml_cli/commands/models/executions/cancel/__init__.py +0 -0
  174. frogml_cli/commands/models/executions/cancel/_logic.py +9 -0
  175. frogml_cli/commands/models/executions/cancel/ui.py +27 -0
  176. frogml_cli/commands/models/executions/execution_commands_group.py +24 -0
  177. frogml_cli/commands/models/executions/report/__init__.py +0 -0
  178. frogml_cli/commands/models/executions/report/_logic.py +14 -0
  179. frogml_cli/commands/models/executions/report/ui.py +43 -0
  180. frogml_cli/commands/models/executions/start/__init__.py +0 -0
  181. frogml_cli/commands/models/executions/start/_logic.py +83 -0
  182. frogml_cli/commands/models/executions/start/ui.py +208 -0
  183. frogml_cli/commands/models/executions/status/__init__.py +0 -0
  184. frogml_cli/commands/models/executions/status/_logic.py +13 -0
  185. frogml_cli/commands/models/executions/status/ui.py +27 -0
  186. frogml_cli/commands/models/init/__init__.py +0 -0
  187. frogml_cli/commands/models/init/_logic/__init__.py +0 -0
  188. frogml_cli/commands/models/init/_logic/initialize_model_structure.py +40 -0
  189. frogml_cli/commands/models/init/_logic/template/__init__.py +0 -0
  190. frogml_cli/commands/models/init/_logic/template/churn/__init__.py +0 -0
  191. frogml_cli/commands/models/init/_logic/template/churn/cookiecutter.json +3 -0
  192. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/__init__.py +0 -0
  193. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  194. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/conda.yml +10 -0
  195. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/data.csv +1001 -0
  196. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/model.py +99 -0
  197. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
  198. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
  199. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/it/test_churn.py +32 -0
  200. frogml_cli/commands/models/init/_logic/template/credit_risk/__init__.py +0 -0
  201. frogml_cli/commands/models/init/_logic/template/credit_risk/cookiecutter.json +3 -0
  202. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/__init__.py +0 -0
  203. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  204. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/conda.yml +11 -0
  205. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/data.csv +1001 -0
  206. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/model.py +108 -0
  207. frogml_cli/commands/models/init/_logic/template/general/__init__.py +0 -0
  208. frogml_cli/commands/models/init/_logic/template/general/cookiecutter.json +6 -0
  209. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/__init__.py +0 -0
  210. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/__init__.py +5 -0
  211. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/conda.yml +8 -0
  212. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/model.py +66 -0
  213. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.test_directory}}/__init__.py +0 -0
  214. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.test_directory}}/test_qwak_model.py +5 -0
  215. frogml_cli/commands/models/init/_logic/template/titanic/__init__.py +0 -0
  216. frogml_cli/commands/models/init/_logic/template/titanic/cookiecutter.json +3 -0
  217. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/__init__.py +0 -0
  218. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  219. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/conda.yml +11 -0
  220. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/model.py +98 -0
  221. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
  222. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
  223. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/it/test_titanic.py +24 -0
  224. frogml_cli/commands/models/init/_logic/template/titanic_poetry/__init__.py +0 -0
  225. frogml_cli/commands/models/init/_logic/template/titanic_poetry/cookiecutter.json +3 -0
  226. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/__init__.py +0 -0
  227. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  228. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/model.py +98 -0
  229. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/pyproject.toml +20 -0
  230. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
  231. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
  232. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/it/test_titanic.py +25 -0
  233. frogml_cli/commands/models/init/ui.py +61 -0
  234. frogml_cli/commands/models/list/__init__.py +0 -0
  235. frogml_cli/commands/models/list/_logic.py +5 -0
  236. frogml_cli/commands/models/list/ui.py +41 -0
  237. frogml_cli/commands/models/list_models/__init__.py +0 -0
  238. frogml_cli/commands/models/list_models/_logic.py +11 -0
  239. frogml_cli/commands/models/list_models/ui.py +60 -0
  240. frogml_cli/commands/models/metadata/__init__.py +0 -0
  241. frogml_cli/commands/models/metadata/_logic.py +12 -0
  242. frogml_cli/commands/models/metadata/ui.py +60 -0
  243. frogml_cli/commands/models/models_command_group.py +44 -0
  244. frogml_cli/commands/models/runtime/__init__.py +0 -0
  245. frogml_cli/commands/models/runtime/logs/__init__.py +0 -0
  246. frogml_cli/commands/models/runtime/logs/ui.py +63 -0
  247. frogml_cli/commands/models/runtime/runtime_commands_group.py +17 -0
  248. frogml_cli/commands/models/runtime/update/__init__.py +0 -0
  249. frogml_cli/commands/models/runtime/update/_logic.py +9 -0
  250. frogml_cli/commands/models/runtime/update/ui.py +17 -0
  251. frogml_cli/commands/secrets/__init__.py +0 -0
  252. frogml_cli/commands/secrets/delete/__init__.py +0 -0
  253. frogml_cli/commands/secrets/delete/_logic.py +5 -0
  254. frogml_cli/commands/secrets/delete/ui.py +21 -0
  255. frogml_cli/commands/secrets/get/__init__.py +0 -0
  256. frogml_cli/commands/secrets/get/_logic.py +5 -0
  257. frogml_cli/commands/secrets/get/ui.py +17 -0
  258. frogml_cli/commands/secrets/secrets_commands_group.py +19 -0
  259. frogml_cli/commands/secrets/set/__init__.py +0 -0
  260. frogml_cli/commands/secrets/set/_logic.py +5 -0
  261. frogml_cli/commands/secrets/set/ui.py +16 -0
  262. frogml_cli/commands/ui_tools.py +18 -0
  263. frogml_cli/exceptions/__init__.py +14 -0
  264. frogml_cli/exceptions/frogml_command_exception.py +2 -0
  265. frogml_cli/exceptions/frogml_deploy_new_build_failed.py +5 -0
  266. frogml_cli/exceptions/frogml_resource_not_found.py +2 -0
  267. frogml_cli/inner/__init__.py +0 -0
  268. frogml_cli/inner/file_registry.py +98 -0
  269. frogml_cli/inner/tools/__init__.py +0 -0
  270. frogml_cli/inner/tools/cli_tools.py +179 -0
  271. frogml_cli/inner/tools/config_handler.py +29 -0
  272. frogml_cli/inner/tools/dataclasses_utils.py +21 -0
  273. frogml_cli/inner/tools/logger/__init__.py +3 -0
  274. frogml_cli/inner/tools/logger/logger.py +278 -0
  275. frogml_cli/inner/tools/logger/logging.yml +79 -0
  276. frogml_cli/inner/tools/tracking.py +47 -0
  277. frogml_cli/main.py +9 -0
  278. frogml_cli/tools/__init__.py +0 -0
  279. frogml_cli/tools/colors.py +13 -0
  280. frogml_cli/tools/const.py +3 -0
  281. frogml_cli/tools/files.py +63 -0
  282. frogml_cli/tools/log_handling.py +159 -0
  283. frogml_cli/tools/utils.py +45 -0
  284. frogml_cli-0.0.1.dist-info/METADATA +51 -0
  285. frogml_cli-0.0.1.dist-info/RECORD +287 -0
  286. frogml_cli-0.0.1.dist-info/WHEEL +4 -0
  287. frogml_cli-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from frogml._proto.qwak.deployment.deployment_pb2 import DeploymentSize, MemoryUnit
4
+ from frogml._proto.qwak.user_application.common.v0.resources_pb2 import (
5
+ ClientPodComputeResources,
6
+ CpuResources,
7
+ GpuResources,
8
+ PodComputeResourceTemplateSpec,
9
+ )
10
+ from frogml.core.clients.administration.eco_system.client import EcosystemClient
11
+ from frogml.core.clients.instance_template.client import (
12
+ InstanceTemplateManagementClient,
13
+ )
14
+ from frogml.core.exceptions import FrogmlException
15
+ from frogml.core.inner.instance_template.verify_template_id import verify_template_id
16
+ from frogml.core.inner.provider import Provider
17
+
18
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
19
+ DeployConfig,
20
+ )
21
+
22
+
23
+ def deployment_size_from_deploy_config(
24
+ deploy_config: DeployConfig,
25
+ instance_template_client: InstanceTemplateManagementClient,
26
+ ecosystem_client: EcosystemClient,
27
+ ) -> DeploymentSize:
28
+ if deploy_config.resources.instance_size:
29
+ deploy_config.resources.instance_size = (
30
+ deploy_config.resources.instance_size.lower()
31
+ )
32
+ account_details = ecosystem_client.get_account_details()
33
+ if deploy_config.environments:
34
+ environments_config = list(
35
+ ecosystem_client.get_environments_names_to_details(
36
+ deploy_config.environments
37
+ ).values()
38
+ )
39
+ else:
40
+ environments_config = [
41
+ account_details.environment_by_id[
42
+ account_details.default_environment_id
43
+ ]
44
+ ]
45
+ for environment_config in environments_config:
46
+ provider = __get_provider(environment_config)
47
+ try:
48
+ verify_template_id(
49
+ deploy_config.resources.instance_size,
50
+ instance_template_client,
51
+ provider=provider,
52
+ )
53
+ except FrogmlException as e:
54
+ raise FrogmlException(
55
+ f"Error with template {deploy_config.resources.instance_size} for environment {environment_config.name}: {e.message}"
56
+ )
57
+
58
+ return DeploymentSize(
59
+ number_of_pods=deploy_config.resources.pods,
60
+ client_pod_compute_resources=ClientPodComputeResources(
61
+ template_spec=PodComputeResourceTemplateSpec(
62
+ template_id=deploy_config.resources.instance_size
63
+ )
64
+ ),
65
+ )
66
+ elif deploy_config.resources.gpu_type:
67
+ return DeploymentSize(
68
+ number_of_pods=deploy_config.resources.pods,
69
+ client_pod_compute_resources=ClientPodComputeResources(
70
+ gpu_resources=GpuResources(
71
+ gpu_type=deploy_config.resources.gpu_type,
72
+ gpu_amount=deploy_config.resources.gpu_amount,
73
+ )
74
+ ),
75
+ )
76
+ else:
77
+ return DeploymentSize(
78
+ number_of_pods=deploy_config.resources.pods,
79
+ client_pod_compute_resources=ClientPodComputeResources(
80
+ cpu_resources=CpuResources(
81
+ cpu=deploy_config.resources.cpus,
82
+ memory_amount=deploy_config.resources.memory,
83
+ memory_units=MemoryUnit.MIB,
84
+ )
85
+ ),
86
+ )
87
+
88
+
89
+ def __get_provider(environment_config):
90
+ provider = None
91
+ cloud_type = environment_config.configuration.cloud_configuration.WhichOneof(
92
+ "configuration"
93
+ )
94
+ if cloud_type == "aws_cloud_configuration":
95
+ provider = Provider.AWS
96
+ elif cloud_type == "gcp_cloud_configuration":
97
+ provider = Provider.GCP
98
+ return provider
@@ -0,0 +1,28 @@
1
+ from frogml._proto.qwak.builds.builds_pb2 import SUCCESSFUL
2
+ from frogml.core.clients.build_orchestrator import BuildOrchestratorClient
3
+ from frogml.core.clients.model_management.client import ModelsManagementClient
4
+ from frogml.core.exceptions import FrogmlException
5
+
6
+
7
+ def get_latest_successful_build_from_model(model_id: str) -> str:
8
+ """Get the latest successful build from model
9
+
10
+ Args:
11
+ model_id: Model id to search build for.
12
+
13
+ Returns:
14
+ str: Latest build id.
15
+
16
+ Raises:
17
+ FrogmlException: if no successful build exists in model.
18
+ """
19
+ model = ModelsManagementClient().get_model(model_id)
20
+ model_uuid = model.uuid
21
+ all_builds = BuildOrchestratorClient().list_builds(model_uuid).build
22
+ builds = [build for build in all_builds if build.build_status == SUCCESSFUL]
23
+ builds.sort(key=lambda r: r.audit.created_at.seconds)
24
+
25
+ if not builds:
26
+ raise FrogmlException(f"Unable to find successful build for model {model_id}")
27
+
28
+ return builds[-1].buildId
@@ -0,0 +1,193 @@
1
+ import base64
2
+
3
+ from frogml._proto.qwak.ecosystem.v0.ecosystem_runtime_service_pb2 import (
4
+ GetCloudCredentialsParameters,
5
+ GetCloudCredentialsRequest,
6
+ PermissionSet,
7
+ PullModelsContainerRegistry,
8
+ )
9
+ from frogml.core.clients.administration.eco_system.client import EcosystemClient
10
+ from frogml.core.clients.build_orchestrator import BuildOrchestratorClient
11
+ from frogml.core.exceptions import FrogmlException
12
+ from google.protobuf.duration_pb2 import Duration
13
+ from rich.console import Console
14
+ from rich.layout import Layout
15
+ from rich.panel import Panel
16
+ from rich.progress import Progress
17
+
18
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
19
+ DeployConfig,
20
+ )
21
+
22
+ tasks = {}
23
+
24
+
25
+ def local_deploy(config: DeployConfig):
26
+ try:
27
+ import docker
28
+ except ImportError:
29
+ raise FrogmlException(
30
+ "Error: 'docker' package is required to for local model deployment."
31
+ )
32
+
33
+ console = Console()
34
+ console.print(Panel(f"Deploying model {config.model_id} locally", style="green"))
35
+
36
+ client = docker.from_env()
37
+ build_image = _get_build_image(config)
38
+
39
+ if not client.images.list(name=build_image):
40
+ console.print(
41
+ "Pulling serving image from Frogml's remote repository. Might take a few minutes...",
42
+ style="yellow",
43
+ )
44
+ _docker_login(client)
45
+ _image_pull(client, build_image)
46
+
47
+ try:
48
+ container = start_local_frogml_container(build_image, client, config)
49
+ container.reload()
50
+ host_port = container.ports.get("5000/tcp")[0]["HostPort"]
51
+
52
+ layout = example_usage_layout(host_port)
53
+ console.print(layout)
54
+
55
+ for line in container.logs(stream=True):
56
+ console.print(str(line.strip().decode()), style="blue")
57
+
58
+ except KeyboardInterrupt:
59
+ console.print(Panel("Stopping container...", style="red"))
60
+ container.stop()
61
+ container.remove()
62
+
63
+
64
+ def example_usage_layout(host_port: str):
65
+ layout = Layout(size=6)
66
+ layout.split_row(
67
+ Layout(name="left"),
68
+ Layout(name="right"),
69
+ )
70
+ layout["left"].update(
71
+ Panel(
72
+ f"[bold]cURL usage:[bold]\n\n"
73
+ f"curl --location\n"
74
+ f"--request POST http://localhost:{host_port}/predict\n"
75
+ f"--header 'Content-Type: application/json'\n"
76
+ f"--data '...'",
77
+ style="green",
78
+ )
79
+ )
80
+ layout["right"].update(
81
+ Panel(
82
+ f"[bold]Python client usage:[bold]\n\n"
83
+ f"from frogml_inference import RealtimeClient\n\nfeature_vector=[...]\n"
84
+ f"client = RealTimeClient(model_api='http://localhost:{host_port}/predict')\n"
85
+ f"client.predict(feature_vector)",
86
+ style="green",
87
+ )
88
+ )
89
+ return layout
90
+
91
+
92
+ def start_local_frogml_container(build_image: str, docker_client, config: DeployConfig):
93
+ return docker_client.containers.run(
94
+ build_image,
95
+ command=["bentoml", "serve-gunicorn", "./"],
96
+ entrypoint="./docker-entrypoint.sh",
97
+ environment=[
98
+ "BENTOML_GUNICORN_WORKERS=2",
99
+ "QWAK_DEBUG_MODE=True",
100
+ f"QWAK_MODEL_ID={config.model_id}",
101
+ "BENTOML_DO_NOT_TRACK=True",
102
+ "PYTHONPATH=/qwak/model_dir:/qwak/model_dir/main:$PYTHONPATH",
103
+ f"QWAK_BUILD_ID={config.build_id}",
104
+ "BUNDLE_PATH=/home/bentoml/bundle",
105
+ "BENTOML_HOME=/home/bentoml/",
106
+ ],
107
+ stream=True,
108
+ detach=True,
109
+ publish_all_ports=True,
110
+ )
111
+
112
+
113
+ def _get_build_image(config: DeployConfig):
114
+ return (
115
+ BuildOrchestratorClient().get_build(config.build_id).build.build_destined_image
116
+ )
117
+
118
+
119
+ def _show_progress(line, progress):
120
+ if line["status"] == "Downloading":
121
+ idx = f'[red][Download {line["id"]}]'
122
+ elif line["status"] == "Extracting":
123
+ idx = f'[green][Extract {line["id"]}]'
124
+ else:
125
+ # skip other statuses
126
+ return
127
+
128
+ if idx not in tasks.keys():
129
+ tasks[idx] = progress.add_task(f"{idx}", total=line["progressDetail"]["total"])
130
+ else:
131
+ progress.update(tasks[idx], completed=line["progressDetail"]["current"])
132
+
133
+
134
+ def _image_pull(docker_client, image_name: str):
135
+ with Progress() as progress:
136
+ resp = docker_client.api.pull(image_name, stream=True, decode=True)
137
+ for line in resp:
138
+ _show_progress(line, progress)
139
+
140
+
141
+ def _docker_login(docker_client):
142
+ try:
143
+ import boto3
144
+ except ImportError:
145
+ raise FrogmlException(
146
+ "Error: The 'boto3' package is necessary for local model deployment. Install it directly or by running"
147
+ " 'pip install frogml-cli[local-deployment]'"
148
+ )
149
+
150
+ # TODO: block if not an AWS based container registry
151
+ credentials = _get_aws_credentials()
152
+
153
+ aws_credentials = credentials.cloud_credentials.aws_temporary_credentials
154
+ ecr_client = boto3.Session(
155
+ aws_access_key_id=aws_credentials.access_key_id,
156
+ aws_secret_access_key=aws_credentials.secret_access_key,
157
+ aws_session_token=aws_credentials.session_token,
158
+ region_name=aws_credentials.region,
159
+ ).client("ecr")
160
+
161
+ ecr_credentials = ecr_client.get_authorization_token()["authorizationData"][0]
162
+
163
+ try:
164
+ docker_client.login(
165
+ username="AWS",
166
+ registry=ecr_credentials["proxyEndpoint"],
167
+ password=base64.b64decode(ecr_credentials["authorizationToken"])
168
+ .replace(b"AWS:", b"")
169
+ .decode("utf-8"),
170
+ )
171
+
172
+ except Exception as e:
173
+ raise FrogmlException(f"Failed to login to Qwak's container registry: {e}")
174
+
175
+
176
+ def _get_aws_credentials():
177
+ try:
178
+ eco_client = EcosystemClient()
179
+ credentials = eco_client.get_cloud_credentials(
180
+ request=GetCloudCredentialsRequest(
181
+ parameters=GetCloudCredentialsParameters(
182
+ duration=Duration(seconds=60 * 60, nanos=0), # 6 hours
183
+ permission_set=PermissionSet(
184
+ pull_models_container_registry=PullModelsContainerRegistry()
185
+ ),
186
+ )
187
+ )
188
+ )
189
+ return credentials
190
+ except Exception as e:
191
+ raise FrogmlException(
192
+ f"Failed to get credentials to pull image from Frogml's container registry: {e}."
193
+ )
@@ -0,0 +1,15 @@
1
+ from frogml._proto.qwak.deployment.deployment_pb2 import AdvancedDeploymentOptions
2
+
3
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
4
+ DeployConfig,
5
+ )
6
+
7
+
8
+ def batch_advanced_deployment_options_from_deploy_config(
9
+ deploy_config: DeployConfig,
10
+ ) -> AdvancedDeploymentOptions:
11
+ return AdvancedDeploymentOptions(
12
+ custom_iam_role_arn=deploy_config.advanced_options.iam_role_arn,
13
+ purchase_option=deploy_config.advanced_options.purchase_option,
14
+ service_account_key_secret_name=deploy_config.advanced_options.service_account_key_secret_name,
15
+ )
@@ -0,0 +1,24 @@
1
+ from frogml._proto.qwak.deployment.deployment_pb2 import KubeDeploymentType
2
+ from frogml._proto.qwak.deployment.deployment_service_pb2 import DeployModelResponse
3
+
4
+ from frogml_cli.commands.models.deployments.deploy._logic.base_deploy_executor import (
5
+ BaseDeployExecutor,
6
+ )
7
+ from frogml_cli.commands.models.deployments.deploy._logic.deployment_message_helpers import (
8
+ get_env_to_deployment_message,
9
+ )
10
+
11
+
12
+ class BatchDeployExecutor(BaseDeployExecutor):
13
+ def deploy(self) -> DeployModelResponse:
14
+ env_deployment_messages = get_env_to_deployment_message(
15
+ self.config,
16
+ KubeDeploymentType.BATCH,
17
+ self.ecosystem_client,
18
+ self.instance_template_client,
19
+ )
20
+ return self.deploy_client.deploy_model(
21
+ model_id=self.config.model_id,
22
+ build_id=self.config.build_id,
23
+ env_deployment_messages=env_deployment_messages,
24
+ )
@@ -0,0 +1,119 @@
1
+ import click
2
+ from frogml.core.tools.logger.logger import set_frogml_logger_stdout_verbosity_level
3
+
4
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
5
+ DeployConfig,
6
+ PurchaseOption,
7
+ )
8
+ from frogml_cli.commands.models.deployments.deploy._logic.deployment_response_handler import (
9
+ client_deployment,
10
+ )
11
+ from frogml_cli.commands.models.deployments.deploy.batch._logic.deploy_executor import (
12
+ BatchDeployExecutor,
13
+ )
14
+ from frogml_cli.inner.tools.cli_tools import DeprecatedOption, FrogMLCommand
15
+ from frogml_cli.inner.tools.config_handler import config_handler
16
+ from frogml_cli.tools.const import GPU_TYPES
17
+
18
+
19
+ @click.command("batch", help="Deploy batch model.", cls=FrogMLCommand)
20
+ @click.option(
21
+ "--build-id",
22
+ help="Build ID, If not specified latest successful build will deployed.",
23
+ type=str,
24
+ )
25
+ @click.option("--model-id", help="Model ID", type=str, required=True)
26
+ @click.option(
27
+ "--pods",
28
+ "--replicas",
29
+ type=int,
30
+ help="Number of pods to deploy.",
31
+ deprecated=["--pods"],
32
+ preferred="--replicas",
33
+ cls=DeprecatedOption,
34
+ )
35
+ @click.option(
36
+ "--cpus",
37
+ type=float,
38
+ help="Number of CPU cores, can be fraction of a core (e.g. 0.4).",
39
+ )
40
+ @click.option(
41
+ "--memory",
42
+ type=int,
43
+ help="Memory amount in Mib (e.g. 256).",
44
+ )
45
+ @click.option(
46
+ "--gpu-type",
47
+ metavar="NAME",
48
+ required=False,
49
+ help=f"Type of GPU to use on the deployments ({', '.join([x for x in GPU_TYPES])}).",
50
+ type=click.STRING,
51
+ )
52
+ @click.option(
53
+ "--gpu-amount",
54
+ metavar="NAME",
55
+ required=False,
56
+ type=int,
57
+ help="Amount of GPU to use on the deployments.",
58
+ )
59
+ @click.option(
60
+ "--iam-role-arn",
61
+ type=str,
62
+ help="Custom IAM Role ARN.",
63
+ )
64
+ @click.option(
65
+ "--service-account-key-secret-name",
66
+ type=str,
67
+ help="Custom service account for Gcp.",
68
+ )
69
+ @click.option(
70
+ "--sync",
71
+ is_flag=True,
72
+ default=False,
73
+ help="Waiting for deployments to be ready",
74
+ )
75
+ @click.option(
76
+ "-v",
77
+ "--verbose",
78
+ count=True,
79
+ help="Log verbosity level - v: INFO, vv: DEBUG [default: WARNING]",
80
+ )
81
+ @click.option(
82
+ "-f",
83
+ "--from-file",
84
+ help="Deploy by run_config file, Command arguments will overwrite any run_config.",
85
+ required=False,
86
+ type=click.Path(exists=True, resolve_path=True, dir_okay=False),
87
+ )
88
+ @click.option(
89
+ "--out-conf",
90
+ help="Extract deploy conf from commands arguments, the commands will not run it wil only output valid yaml "
91
+ "structure",
92
+ default=False,
93
+ is_flag=True,
94
+ )
95
+ @click.option(
96
+ "--purchase-option",
97
+ required=False,
98
+ type=click.Choice([i.value for i in PurchaseOption], case_sensitive=False),
99
+ help="Indicate the instance deployments type, whether it is spot/ondemand. Default is spot",
100
+ )
101
+ @click.option(
102
+ "--instance",
103
+ required=False,
104
+ type=str,
105
+ help="The instance size to deploy on - 'small', 'medium', 'large', etc...",
106
+ default=None,
107
+ )
108
+ def batch(verbose: bool, from_file: str, out_conf: bool, sync: bool, **kwargs):
109
+ set_frogml_logger_stdout_verbosity_level(verbose + 1)
110
+ config: DeployConfig = config_handler(
111
+ config=DeployConfig,
112
+ from_file=from_file,
113
+ out_conf=out_conf,
114
+ sections=("batch",),
115
+ **kwargs,
116
+ )
117
+ if not out_conf:
118
+ deploy_executor = BatchDeployExecutor(config)
119
+ client_deployment(deploy=deploy_executor, sync=sync)
@@ -0,0 +1,19 @@
1
+ from click import group
2
+
3
+ from frogml_cli.commands.models.deployments.deploy.batch.ui import batch
4
+ from frogml_cli.commands.models.deployments.deploy.realtime.ui import realtime
5
+ from frogml_cli.commands.models.deployments.deploy.streaming.ui import stream
6
+
7
+
8
+ @group(
9
+ name="deploy",
10
+ help="Model deployments",
11
+ )
12
+ def deploy_group():
13
+ # Click group injection
14
+ pass
15
+
16
+
17
+ deploy_group.add_command(realtime, "realtime")
18
+ deploy_group.add_command(stream, "stream")
19
+ deploy_group.add_command(batch, "batch")
@@ -0,0 +1,21 @@
1
+ from frogml._proto.qwak.deployment.deployment_pb2 import AdvancedDeploymentOptions
2
+ from google.protobuf.wrappers_pb2 import BoolValue
3
+
4
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
5
+ DeployConfig,
6
+ )
7
+
8
+
9
+ def realtime_advanced_deployment_options_from_deploy_config(
10
+ deploy_config: DeployConfig,
11
+ ) -> AdvancedDeploymentOptions:
12
+ return AdvancedDeploymentOptions(
13
+ number_of_http_server_workers=deploy_config.realtime.workers,
14
+ http_request_timeout_ms=deploy_config.realtime.timeout,
15
+ daemon_mode=BoolValue(value=deploy_config.realtime.daemon_mode),
16
+ max_batch_size=deploy_config.realtime.max_batch_size,
17
+ custom_iam_role_arn=deploy_config.advanced_options.iam_role_arn,
18
+ purchase_option=deploy_config.advanced_options.purchase_option,
19
+ deployment_process_timeout_limit=deploy_config.realtime.deployment_timeout,
20
+ service_account_key_secret_name=deploy_config.advanced_options.service_account_key_secret_name,
21
+ )
@@ -0,0 +1,24 @@
1
+ from frogml._proto.qwak.deployment.deployment_pb2 import KubeDeploymentType
2
+ from frogml._proto.qwak.deployment.deployment_service_pb2 import DeployModelResponse
3
+
4
+ from frogml_cli.commands.models.deployments.deploy._logic.base_deploy_executor import (
5
+ BaseDeployExecutor,
6
+ )
7
+ from frogml_cli.commands.models.deployments.deploy._logic.deployment_message_helpers import (
8
+ get_env_to_deployment_message,
9
+ )
10
+
11
+
12
+ class RealtimeDeployExecutor(BaseDeployExecutor):
13
+ def deploy(self) -> DeployModelResponse:
14
+ env_deployment_messages = get_env_to_deployment_message(
15
+ self.config,
16
+ KubeDeploymentType.ONLINE,
17
+ self.ecosystem_client,
18
+ self.instance_template_client,
19
+ )
20
+ return self.deploy_client.deploy_model(
21
+ model_id=self.config.model_id,
22
+ build_id=self.config.build_id,
23
+ env_deployment_messages=env_deployment_messages,
24
+ )
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Dict, List
4
+
5
+ from frogml._proto.qwak.auto_scaling.v1.auto_scaling_pb2 import AutoScalingConfig
6
+ from frogml._proto.qwak.deployment.deployment_pb2 import (
7
+ RealTimeConfig,
8
+ ServingStrategy,
9
+ TrafficConfig,
10
+ )
11
+ from frogml._proto.qwak.ecosystem.v0.ecosystem_pb2 import UserContextEnvironmentDetails
12
+ from frogml.core.exceptions import FrogmlException
13
+
14
+ from frogml_cli.commands.audience._logic.config.v1.audience_config import AudienceConfig
15
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
16
+ DeployConfig,
17
+ )
18
+
19
+
20
+ def create_realtime_serving_strategy(
21
+ auto_scaling: AutoScalingConfig,
22
+ audiences: List[AudienceConfig],
23
+ fallback_variation: str,
24
+ variation_name: str,
25
+ variation_protected_state: bool = False,
26
+ ) -> ServingStrategy:
27
+ return ServingStrategy(
28
+ realtime_config=(
29
+ RealTimeConfig(
30
+ auto_scaling_config=auto_scaling,
31
+ traffic_config=TrafficConfig(
32
+ selected_variation_name=variation_name,
33
+ audience_routes_entries=[
34
+ audience.to_audience_route_entry(index)
35
+ for index, audience in enumerate(audiences)
36
+ ],
37
+ fallback_variation=fallback_variation,
38
+ selected_variation_protect_state=variation_protected_state,
39
+ ),
40
+ )
41
+ )
42
+ )
43
+
44
+
45
+ def create_realtime_serving_strategy_from_deploy_config(
46
+ deploy_config: DeployConfig,
47
+ environment_name_to_config: Dict[str, UserContextEnvironmentDetails],
48
+ ) -> Dict[str, ServingStrategy]:
49
+ serving_strategies = {}
50
+ errors = []
51
+ variation_name = deploy_config.realtime.variation_name or "default"
52
+ variation_protected_state = deploy_config.realtime.variation_protected_state
53
+ fallback_variation = deploy_config.realtime.fallback_variation
54
+ auto_scaling = (
55
+ deploy_config.auto_scaling.to_autoscaling_api()
56
+ if deploy_config.auto_scaling
57
+ else None
58
+ )
59
+ for env_name, env_config in environment_name_to_config.items():
60
+ try:
61
+ serving_strategies[env_config.id] = create_realtime_serving_strategy(
62
+ auto_scaling,
63
+ deploy_config.realtime.audiences,
64
+ fallback_variation,
65
+ variation_name,
66
+ variation_protected_state,
67
+ )
68
+
69
+ except FrogmlException as e:
70
+ errors.append(e.message)
71
+
72
+ if errors:
73
+ raise FrogmlException("\n".join(errors))
74
+
75
+ return serving_strategies