
    Q                     
   d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlmZ ddlZddlmZ eZe	j$                  dkD  rej&                  ZdZdZd	Zd
ZdZdZdZdZdZdZdZdZdZ dZ!dZ"e e!fZ#dZ$dZ%dZ&dZ'dZ(dZ)dZ*dZ+dZ,dZ-d Z.d!Z/ ej`                  d"d#      Z1d$Z2dZ&d%Z3 G d& d'e4      Z5d(Z6d)Z7 G d* d+e8      Z9 G d, d-e:      Z; G d. d/e      Z<d0 Z=d1 Z>d<d2Z?d3 Z@d4 ZAd5 ZBd6 ZCd7 ZDd8 ZEd9 ZFd: ZGd; ZHy)=z)Common utilities for running predictions.    N   )Model)dtypes)      zPrediction-EnginezPrediction-Engine-Run-Time	Frameworkmodelprepared_modelscikit_learnsk_xgbxgboost
tensorflowcustomzPrediction-Preprocess-TimezPrediction-Postprocess-Timezmodel.joblibz	model.pkl)zsaved_model.pbzsaved_model.pbtxt)z	model.bstinputsoutputssignature_namezPrediction-Columnarize-TimezPrediction-Unalias-TimezPrediction-Encode-TimezPrediction-Session-Run-TimezPrediction-Alias-TimezPrediction-Rowify-TimeTF_SESSION_RUNz
/tmp/modelPredictionErrorTypemessagecodepredictions	instancesc                       e Zd ZdZ edd      Z edd      Z edd	      Z ed
d      Z edd      Z	 edd      Z
ed        Zed        Zed        Zd Zy)PredictionErrorz2Customer exception for known prediction exception.zFailed to load modelr   r   zInvalid inputsr   )r   z Failed to run the provided model   z*There was a problem processing the outputsr   z,There was a problem processing the user code   z6Could not get an access token from the metadata server   c                 4    | j                   d   j                  S Nr   )argsr   selfs    >lib/third_party/ml_sdk/cloud/ml/prediction/prediction_utils.py
error_codezPredictionError.error_codeo   s    99Q<    c                 4    | j                   d   j                  S r    )r!   r   r"   s    r$   error_messagezPredictionError.error_messages   s    99Q<r&   c                      | j                   d   S )Nr   )r!   r"   s    r$   error_detailzPredictionError.error_detailw   s    99Q<r&   c                 N    d| j                   | j                  | j                  fz  S )Nz%s: %s (Error code: %d))r(   r*   r%   r"   s    r$   __str__zPredictionError.__str__{   s.    %););)-):):DOO)M M Nr&   N)__name__
__module____qualname____doc__r   FAILED_TO_LOAD_MODELINVALID_INPUTSFAILED_TO_RUN_MODELINVALID_OUTPUTSINVALID_USER_CODE FAILED_TO_ACCESS_METADATA_SERVERpropertyr%   r(   r*   r,    r&   r$   r   r   \   s    : -$1.&'7a@.+0q:':D/)<1F%8F&"        Nr&   r   i@B i  c                   T    e Zd ZdZd	dZd Zd Zed        Zed        Z	ed        Z
y)
TimeraP  Context manager for timing code blocks.

  The object is intended to be used solely as a context manager and not
  as a general purpose object.

  The timer starts when __enter__ is invoked on the context manager
  and stopped when __exit__ is invoked. After __exit__ is called,
  the duration properties report the amount of time between
  __enter__ and __exit__ and thus do not change. However, if any of the
  duration properties are called between the call to __enter__ and __exit__,
  then they will return the "live" value of the timer.

  If the same Timer object is re-used in multiple with statements, the values
  reported will reflect the latest call. Do not use the same Timer object in
  nested with blocks with the same Timer context manager.

  Example usage:

    with Timer() as timer:
      foo()
    print(timer.duration_secs)
  Nc                 R    d | _         d | _        |xs t        j                  | _        y N)startendtimeitdefault_timer	_get_time)r#   timer_fns     r$   __init__zTimer.__init__   s"    DJDH5!5!5DNr&   c                 >    d | _         | j                         | _        | S r<   )r>   rA   r=   r"   s    r$   	__enter__zTimer.__enter__   s    DH!DJKr&   c                 .    | j                         | _        y)NF)rA   r>   )r#   exc_typevalue	tracebacks       r$   __exit__zTimer.__exit__   s    ~~DHr&   c                 d    | j                         }| j                  xs || j                  xs |z
  S r<   )rA   r>   r=   )r#   nows     r$   secondszTimer.seconds   s*    
..
CHHO

 1c22r&   c                 :    t        t        | j                  z        S r<   )intMICROrM   r"   s    r$   microsecondszTimer.microseconds       ut||#$$r&   c                 :    t        t        | j                  z        S r<   )rO   MILLIrM   r"   s    r$   millisecondszTimer.milliseconds   rR   r&   r<   )r-   r.   r/   r0   rC   rE   rJ   r7   rM   rQ   rU   r8   r&   r$   r:   r:      sR    .6

 3 3 % % % %r&   r:   c                   6    e Zd ZdZej
                  dd       Zy)Statsa  An object for tracking stats.

  This class is dict-like, so stats are accessed/stored like so:

    stats = Stats()
    stats["count"] = 1
    stats["foo"] = "bar"

  This class also facilitates collecting timing information via the
  context manager obtained using the "time" method. Reported timings
  are in microseconds.

  Example usage:

    with stats.time("foo_time"):
      foo()
    print(stats["foo_time"])
  Nc              #   r   K   t        |      5 }| d d d        j                  | |<   y # 1 sw Y   xY wwr<   )r:   rQ   )r#   namerB   timers       r$   timez
Stats.time   s/     	xEk 
##DJ 
s   7+747r<   )r-   r.   r/   r0   
contextlibcontextmanagerr[   r8   r&   r$   rW   rW      s!    & $ $r&   rW   c                   <    e Zd ZdZd Zd	dZd
dZd	dZd Zd	dZ	y)	BaseModelz3The base definition of an internal Model interface.c                      || _         d| _        y)zpConstructs a BaseModel.

    Args:
      client: An instance of PredictionClient for performing prediction.
    N)_client_user_processor)r#   clients     r$   rC   zBaseModel.__init__   s     DLDr&   Nc                      y)a|  Runs the preprocessing function on the instances.

    Args:
      instances: list of instances as provided to the predict() method.
      stats: Stats object for recording timing information.
      **kwargs: Additional keyword arguments for preprocessing.

    Returns:
      A new list of preprocessed instances. Each instance is as described
      in the predict() method.
    Nr8   )r#   r   statskwargss       r$   
preprocesszBaseModel.preprocess   s     	r&   c                      y)a  Runs the postprocessing function on the instances.

    Args:
      predicted_output: list of instances returned by the predict() method on
        preprocessed instances.
      original_input: List of instances, before any pre-processing was applied.
      stats: Stats object for recording timing information.
      **kwargs: Additional keyword arguments for postprocessing.

    Returns:
      A new list of postprocessed instances.
    Nr8   )r#   predicted_outputoriginal_inputre   rf   s        r$   postprocesszBaseModel.postprocess   s     	r&   c                    |xs
 t               }| j                  |       |j                  t              5   | j                  |fd|i|}ddd       |j                  t
              5   | j                  j                  fd|i|}ddd       |j                  t              5   | j                  f||d|}ddd       |S # 1 sw Y   |xY w# 1 sw Y   KxY w# 1 sw Y   S xY w)z=Runs preprocessing, predict, and postprocessing on the input.re   N)rj   re   )
rW   _validate_kwargsr[   PREPROCESS_TIMErg   ENGINE_RUN_TIMEra   predictPOSTPROCESS_TIMErk   )r#   r   re   rf   preprocessedpredicted_outputspostprocesseds          r$   rp   zBaseModel.predict   s     UWE&!	O	$$T__YFeFvFl 
%	O	$.$,,..
/#/'-/ 
% 
$	%&d&&
N,5UNFLNm 
&  
%	$	$	$ 
& s#   C( C%CCCC(c                      y)a  Validates and sets defaults for extra predict keyword arguments.

    Modifies the keyword args dictionary in-place. Keyword args will be included
    into pre/post-processing and the client predict method.
    Can raise Exception to error out of request on bad keyword args.
    If no additional args are required, pass.

    Args:
      kwargs: Dictionary (str->str) of keyword arguments to check.
    Nr8   )r#   rf   s     r$   rm   zBaseModel._validate_kwargs  s     	r&   c                      y)a"  Gets model signature of inputs and outputs.

    Currently only used for Tensorflow model. May be extended for use with
    XGBoost and Sklearn in the future.

    Args:
      signature_name: str of name of signature

    Returns:
      (str, SignatureDef): signature key, SignatureDef
    NNr8   )r#   r   s     r$   get_signaturezBaseModel.get_signature  s     r&   r<   rw   )
r-   r.   r/   r0   rC   rg   rk   rp   rm   rx   r8   r&   r$   r_   r_      s#    ; 		  	r&   r_   c                 x    | t         k(  xr0 t        |t               xs t        |j	                  |      d         S )a  Determines if base64 decoding is required.

  Returns False if framework is not TF.
  Returns True if framework is TF and is a user model.
  Returns True if framework is TF and model contains a str input.
  Returns False if framework is TF and model does not contain str input.

  Args:
    framework: ML framework of prediction app
    model: model object
    signature_name: str of name of signature

  Returns:
    bool

  r   )TENSORFLOW_FRAMEWORK_NAME
isinstancer_   does_signature_contain_strrx   )	frameworkr	   r   s      r$   should_base64_decoder~   )  sD    " 0
0 O%++ N%e&9&9.&I!&LMPr&   c                 T   t        | t              r| D cg c]  }t        |       c}S t        | t              rct	        j
                  |       dhk(  rt        j                  | d         S t	        j                  |       D ci c]  \  }}|t        |       c}}S | S c c}w c c}}w )Nb64)	r{   listdecode_base64dictsixviewkeysbase64	b64decode	iteritems)datavalkvs       r$   r   r   ?  s    d*./$3M#$//$
||DeW$d5k**.1mmD.AB.Adaaq!!.ABBK 0
 Cs   BB$c                 \    | yt        d | j                  j                         D              S )zReturn true if input signature contains a string dtype.

  This is used to determine if we should proceed with base64 decoding.

  Args:
    signature: SignatureDef protocol buffer

  Returns:
    bool
  Tc              3   j   K   | ]+  }|j                   t        j                  j                  k(   - y wr<   )dtyper   stringas_datatype_enum).0r   s     r$   	<genexpr>z-does_signature_contain_str.<locals>.<genexpr>\  s+      1/ WW666/s   13)anyr   values)	signatures    r$   r|   r|   K  s6     	 1&&--/1 
1 1r&   c                    t        j                          }t        j                  d| |       t        j                  j                  |      st        j                  |       t        j                  j                  | d      } 	 t        j                  ddd| |gt        j                         t        j                  d| |t        j                          |z
         y	# t        j                  $ r t        j                  d        w xY w)
a  Copy files from gcs to a local path.

  Copies files directly to the dest_path.
  Sample behavior:
  dir1/
    file1
    file2
    dir2/
      file3

  copy_model_to_local("dir1", "/tmp")
  After copy:
  tmp/
    file1
    file2
    dir2/
      file3

  Args:
    gcs_path: Source GCS path that we're copying from.
    dest_path: Destination local path that we're copying to.

  Raises:
    Exception: If gsutil is not found.
  z$Starting to copy files from %s to %s*gsutilcpz-R)stdinz"Could not copy model using gsutil.z+Files copied from %s to %s: took %f secondsN)r[   loggingdebugospathexistsmakedirsjoin
subprocess
check_callPIPECalledProcessError	exception)gcs_path	dest_pathcopy_start_times      r$   copy_model_to_localr   `  s    4 IIK/	--6)L			"KK	WW\\(C((
 $h	3:D//K
 
--=x499;8: 
	&	& 
:;	
s   *C *Dc                    | j                  d      rt        | t               t        } 	 t        j                  j                  | t              }t        j                  j                  | t              }t        j                  j                  |      r1|}	 ddl	m
} 	 t        j                   d|       |j#                  |      S t        j                  j                  |      rQ|}t        j                   d|       t-        |d	      5 }	t/        j0                  |	j3                               cddd       S y# t        $ rR}	 ddl
}nB# t        $ r6}d}t        j                  |       t        t        j                  |      d}~ww xY wY d}~d}~ww xY w# t$        $ r@ t        j                   d|       ddl}|j)                         }|j+                  |       |cY S w xY w# 1 sw Y   yxY w# t        $ rz}t5        |      }
d
|
v r%dj7                  |
t8        j:                  d         }ndj7                  |
      }t        j                  |       t        t        j                  |      d}~ww xY w)a_  Loads either a .joblib or .pkl file from GCS or from local.

  Loads one of DEFAULT_MODEL_FILE_NAME_JOBLIB or DEFAULT_MODEL_FILE_NAME_PICKLE
  files if they exist. This is used for both sklearn and xgboost.

  Arguments:
    model_path: The path to the directory that contains the model file. This
      path can be either a local path or a GCS path.

  Raises:
    PredictionError: If there is a problem while loading the file.

  Returns:
    A loaded scikit-learn or xgboost predictor object or None if neither
    DEFAULT_MODEL_FILE_NAME_JOBLIB nor DEFAULT_MODEL_FILE_NAME_PICKLE files are
    found.
  zgs://r   )joblibNzCould not import joblib module.zLoading model %s using joblib.zRLoading model %s using joblib failed. Loading model using xgboost.Booster instead.zLoading model %s using pickle.rbzunsupported pickle protocolzCould not load the model: {}. {}. Please make sure the model was exported using python {}. Otherwise, please specify the correct 'python_version' parameter when deploying the model.z!Could not load the model: {}. {}.)
startswithr   LOCAL_MODEL_PATHr   r   r   DEFAULT_MODEL_FILE_NAME_JOBLIBDEFAULT_MODEL_FILE_NAME_PICKLEr   sklearn.externalsr   	Exceptionr   r   r   r1   infoloadKeyErrorr   Booster
load_modelopenpickleloadsreadstrformatsysversion_info)
model_pathmodel_file_name_joblibmodel_file_name_picklemodel_file_namer   e	error_msgr   boosterfraw_error_msgs              r$   load_joblib_or_pickle_modelr     s"   $ 7#
$45!J7KWW\\**HJWW\\**HJ	ww~~,-.oQ 	-5G{{?++ 
.	/.oll3_E&!||AFFH% '& =  Q	Q  	Q7)


I
& D DiP
P	Q 	Q  	(*9	;
 	//#?+	 ' 	 KFM$5ABH&}c.>.>q.ACC  6<<
=*ii 
/>>	
JJKs   A)G( D2 &F AG( #G'	G( 2	F<E F	F 
1E;;F  FG( FG( AGG( GG( G%!G( %G( (	I+1A5I&&I+c                 D   dt        |       j                  v rt        S dt        |       j                  v rt        S dj	                  t        |       j                  t        |       j
                        }t        j                  |       t        t        j                  |      )a  Distinguish scikit-learn and xgboost using model object.

  Arguments:
    model_obj: A loaded model object

  Raises:
    PredictionError: If there is a problem detecting framework from object.

  Returns:
    Either scikit-learn framework or xgboost framework
  sklearnr   z|Invalid model type detected: {}.{}. Please make sure the model file is an exported sklearn model, xgboost model or pipeline.)
typer.   SCIKIT_LEARN_FRAMEWORK_NAMEXGBOOST_FRAMEWORK_NAMEr   r-   r   criticalr   r1   )	model_objr   s     r$    detect_sk_xgb_framework_from_objr     s     $y/,,,&&DO...!!	%&,fO&&O$$'&  Y
/>>	
JJr&   c                     d}|D ]E  }t         j                  j                  t         j                  j                  | |            sA|dz  }G |S )a!  Count how many specified files exist in model_path.

  Args:
    model_path: The local path to the directory that contains the model file.
    specified_file_names: The file names to be checked

  Returns:
    An integer indicating how many specified_file_names are found in model_path.
  r   r   )r   r   r   r   )r   specified_file_namesnum_matches	file_names       r$   _count_num_files_in_pathr     sD     +'i	ww~~bggll:y9:Qk ( 
r&   c                    t        | t              }t        | t              }t        | t              }||z   |z   }|dkD  r@dj	                  |       }t        j                  |       t        t        j                  |      |dk(  rt        S |dk(  rt        S |dk(  rt        |       }t        |      S t        j                  d       y)ac  Detect framework from model_path by analyzing file extensions.

  Args:
    model_path: The local path to the directory that contains the model file.

  Raises:
    PredictionError: If framework can not be identified from model path.

  Returns:
    A string representing the identified framework or None (custom code is
    assumed in this situation).
  r   z4Multiple model files are found in the model_path: {}zFModel files are not found in the model_path.Assumed to be custom code.N)r   $TENSORFLOW_SPECIFIC_MODEL_FILE_NAMES!XGBOOST_SPECIFIC_MODEL_FILE_NAMESSCIKIT_LEARN_MODEL_FILE_NAMESr   r   r   r   r1   rz   r   r   r   warning)r   num_tensorflow_modelsnum_xgboost_modelsnum_sklearn_modelsr   r   r   s          r$   detect_frameworkr   	  s     368/35/
0MO &(::=OO+1_FMMIY
/>>	
JJa$$Q!!Q+J7I+I66OO 2 4r&   c                    t         j                  j                  d      syt        j                  t         j                  j                  d            }|rt        |t              sy|j                  d      }|rt        |t              syt        j                  d|j                  |       |        |j                  |       S )aY  Gets the value of field_name in the version being created, if it exists.

  Args:
    field_name: Name of the key used for retrieving the corresponding value from
      version json object.

  Returns:
  The value of the given field in the version object or the user provided create
  version request if it exists. Otherwise None is returned.
  create_version_requestNversionz:Found value: %s, for field: %s from create_version_request)	r   environgetjsonr   r{   r   r   r   )
field_namerequestr   s      r$   get_field_in_version_jsonr   1  s     
0	1JJrzz~~&>?@'	
7D1KK	"'	
7D1	,,K{{:&
4	Z	  r&   c                    t        | t        j                        s#t        dj	                  t        |                   t        | vr(t        dj	                  t        t        |                   | j                  t              S )zParses the predictions from the json response from prediction server.

  Args:
    response_json(Text): The JSON formatted response to parse.

  Returns:
    Predictions from the response json.

  Raises:
    ValueError if response_json is malformed.
  4Invalid response received from prediction server: {}=Required field '{}' missing in prediction server response: {})r{   collections_libMapping
ValueErrorr   reprPREDICTIONS_KEYpopresponse_jsons    r$   parse_predictionsr   J  sy     
M?#:#:	;
>EE	!" " M)
GNNT-0	23 3 
		?	++r&   c                    t        | t        j                        s#t        dj	                  t        |                   t        | vr(t        dj	                  t        t        |                   | j                  t              S )zParses the outputs from the json response from prediction server.

  Args:
    response_json(Text): The JSON formatted response to parse.

  Returns:
    Outputs from the response json.

  Raises:
    ValueError if response_json is malformed.
  r   r   )r{   r   r   r   r   r   OUTPUTS_KEYr   r   s    r$   parse_outputsr   a  sy     
M?#:#:	;
>EE	!" " %
GNNm,	./ / 
		;	''r&   c                    t        | t        j                        s#t        dj	                  t        |                   t        | vr(t        dj	                  t        t        |                   | j                  t              S )zParses instances from the json request sent to prediction server.

  Args:
    request_json(Text): The JSON formatted request to parse.

  Returns:
    Instances from the request json.

  Raises:
    ValueError if request_json is malformed.
  z-Invalid request sent to prediction server: {}z<Required field '{}' missing in prediction server request: {})r{   r   r   r   r   r   INSTANCES_KEYr   )request_jsons    r$   parse_instancesr   x  sw     
L/"9"9	:
DKK\  ,&
FMM4-	/0 0 
		-	((r&   r<   )Ir0   r   collectionsr\   r   r   r   r   r   r   r[   r?   _interfacesr   r   tensorflow.python.frameworkr   r   r   abcENGINEro   	FRAMEWORKMODEL_SUBDIRECTORYPREPARED_MODEL_SUBDIRECTORYr   SK_XGB_FRAMEWORK_NAMEr   rz   CUSTOM_FRAMEWORK_NAMErn   rq   r   r   r   r   r   
INPUTS_KEYr   SIGNATURE_KEYCOLUMNARIZE_TIMEUNALIAS_TIMEENCODE_TIMESESSION_RUN_TIME
ALIAS_TIMEROWIFY_TIMESESSION_RUN_ENGINE_NAMEr   
namedtupler   r   r   r   r   rP   rT   objectr:   r   rW   r_   r~   r   r|   r   r   r   r   r   r   r   r   r   r8   r&   r$   <module>r     s   0      	   
    
 .fOO/
 
.	 . ,   " (   .0  "0 !, ( $
 #"!  %3 ! 
  1 (&0 $
&*    ,k,,.0   !Ni !NH 	1%F 1%h$D $6S SlP,	1*(:VMK`K8$%P!2,.(.)r&   