def test_make_post_request_to_ai_platform_404_error(
            self, mock_request_header, mock_post_func):
        mock_request_header.return_value = {}

        mock_response = mock.Mock(spec=MockResponse)
        type(mock_response()).status_code = mock.PropertyMock(return_value=404)
        mock_post_func.return_value = mock_response()

        with self.assertRaisesRegex(
                ValueError,
            ('Target URI .* returns HTTP 404 error.\n'
             'Please check if the project, model, and version names '
             'are given correctly.')):
            http_utils.make_post_request_to_ai_platform(
                'uri/test_uri', {'data': 123})
    def test_make_post_request_to_ai_platform_other_erors(
            self, mock_request_header, mock_post_func):
        mock_request_header.return_value = {}

        mock_response = mock.Mock(spec=MockResponse)
        type(mock_response()).status_code = mock.PropertyMock(return_value=403)
        type(mock_response()).text = mock.PropertyMock(
            return_value='test error')
        mock_post_func.return_value = mock_response()

        with self.assertRaisesRegex(ValueError,
                                    ('Target URI .* returns HTTP 403 error.\n'
                                     'Please check the raw error message: \n'
                                     'test error')):
            http_utils.make_post_request_to_ai_platform(
                'uri/test_uri', {'data': 123})
    def test_make_post_request_to_ai_platform(self, mock_request_header,
                                              mock_post_func):
        mock_request_header.return_value = {}

        mock_response = mock.Mock(spec=MockResponse)
        type(mock_response()).status_code = mock.PropertyMock(return_value=200)
        mock_response().json.return_value = 'results'
        mock_post_func.return_value = mock_response()

        res = http_utils.make_post_request_to_ai_platform(
            'uri/test_uri', {'data': 123})
        self.assertTrue(mock_post_func.called)
        self.assertEqual(res, 'results')
    def predict(self, instances, timeout_ms=constants.DEFAULT_TIMEOUT):
        """A method to call prediction services with given instances.

    Args:
       instances: A list of instances for getting predictions.
       timeout_ms: Timeout for each service call to the api (in milliseconds).

    Returns:
       A list of the dictionaries.
    """
        request_body = {'instances': instances}
        response = http_utils.make_post_request_to_ai_platform(
            self._endpoint + ':predict', request_body, self._credentials,
            timeout_ms)

        return response
    def explain(self,
                instances,
                params=None,
                timeout_ms=constants.DEFAULT_TIMEOUT):
        """A method to call explanation services with given instances.

    Args:
       instances: A list of instances for getting explanations.
       params: Overridable parameters for the explain call. Parameters can not
         be overriden in a remote model at the moment.
       timeout_ms: Timeout for each service call to the api (in milliseconds).

    Returns:
       A list of Explanation objects.

    Raises:
      ValueError: When explanation service fails, raise ValueError with the
        returned error message. This is likely due to details or formats of
        the instances are not correct.
    """
        if params:
            logging.warn('Params can not be overriden in a remote model at the'
                         ' moment.')
        del params
        request_body = {'instances': instances}
        response = http_utils.make_post_request_to_ai_platform(
            self._endpoint + ':explain', request_body, self._credentials,
            timeout_ms)

        if 'error' in response:
            error_msg = response['error']
            raise ValueError(('Explanation call failed. This is likely due to '
                              'incorrect instance formats. \nOriginal error '
                              'message: ' + json.dumps(error_msg)))

        explanations = []
        for idx, explanation_dict in enumerate(response['explanations']):
            exp_obj = explanation.Explanation.from_ai_platform_response(
                explanation_dict, instances[idx],
                self._modality_input_list_map)
            explanations.append(exp_obj)

        return explanations
  def explain(self,
              instances: List[Any],
              params: Optional[configs.AttributionParameters] = None,
              timeout_ms: int = constants.DEFAULT_TIMEOUT
             ) -> List[explanation.Explanation]:
    """A method to call explanation services with given instances.

    Args:
       instances: A list of instances for getting explanations.
       params: Overridable parameters for the explain call. Parameters can not
         be overriden in a remote model at the moment.
       timeout_ms: Timeout for each service call to the api (in milliseconds).

    Returns:
       A list of Explanation objects.

    Raises:
      ValueError: When explanation service fails, raise ValueError with the
        returned error message. This is likely due to details or formats of
        the instances are not correct.
      KeyError: When the explanation dictionary doesn't contain 'attributions'
        or 'attributions_by_label' in the response.
    """
    if params:
      logging.warn('Params can not be overriden in a remote model at the'
                   ' moment.')
    del params
    request_body = {'instances': instances}
    response = http_utils.make_post_request_to_ai_platform(
        self._model_endpoint_uri + ':explain',
        request_body,
        self._credentials,
        timeout_ms)

    if 'error' in response:
      error_msg = response['error']
      raise ValueError(('Explanation call failed. This is likely due to '
                        'incorrect instance formats. \nOriginal error '
                        'message: ' + json.dumps(error_msg)))

    explanations = []
    for idx, explanation_dict in enumerate(response['explanations']):
      if _CAIP_ATTRIBUTIONS_KEY in explanation_dict:  # CAIP response.
        attrs_key = _CAIP_ATTRIBUTIONS_KEY
        feature_attrs_key = _CAIP_FEATURE_ATTRS_KEY
        parse_fun = explanation.Explanation.from_ai_platform_response
      elif _VERTEX_ATTRIBUTIONS_KEY in explanation_dict:  # VERTEX response.
        attrs_key = _VERTEX_ATTRIBUTIONS_KEY
        feature_attrs_key = _VERTEX_FEATURE_ATTRS_KEY
        parse_fun = explanation.Explanation.from_vertex_response
      else:
        raise KeyError(
            'Attribution keys are not present in the AI Platform response.')

      attrs = explanation_dict[attrs_key]
      if not self._modality_to_inputs_map:
        self._modality_to_inputs_map = _create_default_modality_map(
            next(iter(attrs))[feature_attrs_key])
      explanations.append(
          parse_fun(attrs, instances[idx], self._modality_to_inputs_map))

    return explanations