1818import sys
1919import subprocess
2020import threading
21+ import yaml
22+ import uuid
23+ import dill
24+ import inspect
25+ from pathlib import Path
2126
2227from typing import List , Dict , Optional , Tuple , Callable , Union
2328from cortex .binary import run_cli , get_cli_path
29+ from cortex import util
2430
2531
2632class Client :
@@ -33,7 +39,107 @@ def __init__(self, env: str):
3339 """
3440 self .env = env
3541
42+ # CORTEX_VERSION_MINOR x5
3643 def deploy (
44+ self ,
45+ api_spec : dict ,
46+ predictor = None ,
47+ pip_dependencies = [],
48+ conda_dependencies = [],
49+ project_dir : Optional [str ] = None ,
50+ force : bool = False ,
51+ wait : bool = False ,
52+ ) -> list :
53+ """
54+ Deploy an API.
55+
56+ Args:
57+ api_spec: A dictionary defining a single Cortex API. Schema can be found here:
58+ → Realtime API: https://docs.cortex.dev/v/master/deployments/realtime-api/api-configuration
59+ → Batch API: https://docs.cortex.dev/v/master/deployments/batch-api/api-configuration
60+ → Traffic Splitter: https://docs.cortex.dev/v/master/deployments/realtime-api/traffic-splitter
61+ predictor: A Cortex Predictor class implementation. Not required when deploying a traffic splitter.
62+ → Realtime API: https://docs.cortex.dev/v/master/deployments/realtime-api/predictors
63+ → Batch API: https://docs.cortex.dev/v/master/deployments/batch-api/predictors
64+ pip_dependencies: A list of PyPI dependencies that will be installed before the predictor class implementation is invoked.
65+ conda_dependencies: A list of Conda dependencies that will be installed before the predictor class implementation is invoked.
66+ project_dir: Path to a python project.
67+ force: Override any in-progress api updates.
68+ wait: Streams logs until the APIs are ready.
69+
70+ Returns:
71+ Deployment status, API specification, and endpoint for each API.
72+ """
73+
74+ if project_dir is not None and predictor is not None :
75+ raise ValueError (
76+ "`predictor` and `project_dir` parameters cannot be specified at the same time, please choose one"
77+ )
78+
79+ if project_dir is not None :
80+ cortex_yaml_path = os .path .join (project_dir , f".cortex-{ uuid .uuid4 ()} .yaml" )
81+
82+ with util .open_temporarily (cortex_yaml_path , "w" ) as f :
83+ yaml .dump ([api_spec ], f ) # write a list
84+ return self ._deploy (cortex_yaml_path , force , wait )
85+
86+ project_dir = Path .home () / ".cortex" / "deployments" / str (uuid .uuid4 ())
87+ with util .open_tempdir (str (project_dir )):
88+ cortex_yaml_path = os .path .join (project_dir , "cortex.yaml" )
89+
90+ if predictor is None :
91+ # for deploying a traffic splitter
92+ with open (cortex_yaml_path , "w" ) as f :
93+ yaml .dump ([api_spec ], f ) # write a list
94+ return self ._deploy (cortex_yaml_path , force = force , wait = wait )
95+
96+ # Change if PYTHONVERSION changes
97+ expected_version = "3.6"
98+ actual_version = f"{ sys .version_info .major } .{ sys .version_info .minor } "
99+ if actual_version < expected_version :
100+ raise Exception ("cortex is only supported for python versions >= 3.6" ) # unexpected
101+ if actual_version > expected_version :
102+ is_python_set = any (
103+ conda_dep .startswith ("python=" ) or "::python=" in conda_dep
104+ for conda_dep in conda_dependencies
105+ )
106+
107+ if not is_python_set :
108+ conda_dependencies = [
109+ f"conda-forge::python={ sys .version_info .major } .{ sys .version_info .minor } .{ sys .version_info .micro } "
110+ ] + conda_dependencies
111+
112+ if len (pip_dependencies ) > 0 :
113+ with open (project_dir / "requirements.txt" , "w" ) as requirements_file :
114+ requirements_file .write ("\n " .join (pip_dependencies ))
115+
116+ if len (conda_dependencies ) > 0 :
117+ with open (project_dir / "conda-packages.txt" , "w" ) as conda_file :
118+ conda_file .write ("\n " .join (conda_dependencies ))
119+
120+ if not inspect .isclass (predictor ):
121+ raise ValueError ("predictor parameter must be a class definition" )
122+
123+ with open (project_dir / "predictor.pickle" , "wb" ) as pickle_file :
124+ dill .dump (predictor , pickle_file )
125+ if api_spec .get ("predictor" ) is None :
126+ api_spec ["predictor" ] = {}
127+
128+ if predictor .__name__ == "PythonPredictor" :
129+ predictor_type = "python"
130+ if predictor .__name__ == "TensorFlowPredictor" :
131+ predictor_type = "tensorflow"
132+ if predictor .__name__ == "ONNXPredictor" :
133+ predictor_type = "onnx"
134+
135+ api_spec ["predictor" ]["path" ] = "predictor.pickle"
136+ api_spec ["predictor" ]["type" ] = predictor_type
137+
138+ with open (cortex_yaml_path , "w" ) as f :
139+ yaml .dump ([api_spec ], f ) # write a list
140+ return self ._deploy (cortex_yaml_path , force = force , wait = wait )
141+
142+ def _deploy (
37143 self ,
38144 config_file : str ,
39145 force : bool = False ,
0 commit comments