From dfd0a17345bddc6be4b49628ade6de7c68fe5a78 Mon Sep 17 00:00:00 2001 From: Tim Huff <89954856+timmarkhuff@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:45:52 -0800 Subject: [PATCH] Deprecate /device-api/posichecks endpoint (#164) * deprecating old posichecks endpoint * adjusting public-api.yaml * Automatically reformatting code * appeasing the linter * Automatically reformatting code * fixing linting issue * Automatically reformatting code * reverting some changes to package.json and package-lock.json * adding retry decorator back to start_inspection --------- Co-authored-by: Tim Huff Co-authored-by: Auto-format Bot --- generated/docs/ImageQueriesApi.md | 4 +- generated/model.py | 2 +- .../openapi_client/api/image_queries_api.py | 5 ++ package-lock.json | 2 +- spec/public-api.yaml | 5 ++ src/groundlight/client.py | 25 ++++---- src/groundlight/internalapi.py | 64 +------------------ 7 files changed, 28 insertions(+), 79 deletions(-) diff --git a/generated/docs/ImageQueriesApi.md b/generated/docs/ImageQueriesApi.md index 04299b52..03549c75 100644 --- a/generated/docs/ImageQueriesApi.md +++ b/generated/docs/ImageQueriesApi.md @@ -205,6 +205,7 @@ with openapi_client.ApiClient(configuration) as api_client: # Create an instance of the API class api_instance = image_queries_api.ImageQueriesApi(api_client) detector_id = "detector_id_example" # str | Choose a detector by its ID. + inspection_id = "inspection_id_example" # str | Associate the image query with an inspection. (optional) human_review = "human_review_example" # str | If set to `DEFAULT`, use the regular escalation logic (i.e., send the image query for human review if the ML model is not confident). If set to `ALWAYS`, always send the image query for human review even if the ML model is confident. If set to `NEVER`, never send the image query for human review even if the ML model is not confident. (optional) patience_time = 3.14 # float | How long to wait for a confident response. (optional) want_async = "want_async_example" # str | If \"true\" then submitting an image query returns immediately without a result. The result will be computed asynchronously and can be retrieved later. (optional) @@ -221,7 +222,7 @@ with openapi_client.ApiClient(configuration) as api_client: # example passing only required values which don't have defaults set # and optional values try: - api_response = api_instance.submit_image_query(detector_id, human_review=human_review, patience_time=patience_time, want_async=want_async, metadata=metadata, body=body) + api_response = api_instance.submit_image_query(detector_id, inspection_id=inspection_id, human_review=human_review, patience_time=patience_time, want_async=want_async, metadata=metadata, body=body) pprint(api_response) except openapi_client.ApiException as e: print("Exception when calling ImageQueriesApi->submit_image_query: %s\n" % e) @@ -233,6 +234,7 @@ with openapi_client.ApiClient(configuration) as api_client: Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **detector_id** | **str**| Choose a detector by its ID. | + **inspection_id** | **str**| Associate the image query with an inspection. | [optional] **human_review** | **str**| If set to `DEFAULT`, use the regular escalation logic (i.e., send the image query for human review if the ML model is not confident). If set to `ALWAYS`, always send the image query for human review even if the ML model is confident. If set to `NEVER`, never send the image query for human review even if the ML model is not confident. | [optional] **patience_time** | **float**| How long to wait for a confident response. | [optional] **want_async** | **str**| If \"true\" then submitting an image query returns immediately without a result. The result will be computed asynchronously and can be retrieved later. | [optional] diff --git a/generated/model.py b/generated/model.py index 507048f7..b3b325c2 100644 --- a/generated/model.py +++ b/generated/model.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: public-api.yaml -# timestamp: 2024-02-19T17:48:07+00:00 +# timestamp: 2024-02-19T18:25:15+00:00 from __future__ import annotations diff --git a/generated/openapi_client/api/image_queries_api.py b/generated/openapi_client/api/image_queries_api.py index 91ba8f0e..4685e4cc 100644 --- a/generated/openapi_client/api/image_queries_api.py +++ b/generated/openapi_client/api/image_queries_api.py @@ -131,6 +131,7 @@ def __init__(self, api_client=None): params_map={ "all": [ "detector_id", + "inspection_id", "human_review", "patience_time", "want_async", @@ -149,6 +150,7 @@ def __init__(self, api_client=None): "allowed_values": {}, "openapi_types": { "detector_id": (str,), + "inspection_id": (str,), "human_review": (str,), "patience_time": (float,), "want_async": (str,), @@ -157,6 +159,7 @@ def __init__(self, api_client=None): }, "attribute_map": { "detector_id": "detector_id", + "inspection_id": "inspection_id", "human_review": "human_review", "patience_time": "patience_time", "want_async": "want_async", @@ -164,6 +167,7 @@ def __init__(self, api_client=None): }, "location_map": { "detector_id": "query", + "inspection_id": "query", "human_review": "query", "patience_time": "query", "want_async": "query", @@ -305,6 +309,7 @@ def submit_image_query(self, detector_id, **kwargs): detector_id (str): Choose a detector by its ID. Keyword Args: + inspection_id (str): Associate the image query with an inspection.. [optional] human_review (str): If set to `DEFAULT`, use the regular escalation logic (i.e., send the image query for human review if the ML model is not confident). If set to `ALWAYS`, always send the image query for human review even if the ML model is confident. If set to `NEVER`, never send the image query for human review even if the ML model is not confident. . [optional] patience_time (float): How long to wait for a confident response.. [optional] want_async (str): If \"true\" then submitting an image query returns immediately without a result. The result will be computed asynchronously and can be retrieved later.. [optional] diff --git a/package-lock.json b/package-lock.json index 320c9282..9072c377 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2038,4 +2038,4 @@ } } } -} +} \ No newline at end of file diff --git a/spec/public-api.yaml b/spec/public-api.yaml index 9ec5957f..69dc5e6a 100644 --- a/spec/public-api.yaml +++ b/spec/public-api.yaml @@ -147,6 +147,11 @@ paths: type: string description: Choose a detector by its ID. required: true + - in: query + name: inspection_id + schema: + type: string + description: Associate the image query with an inspection. - in: query name: human_review schema: diff --git a/src/groundlight/client.py b/src/groundlight/client.py index c03ff9e6..ba63daa7 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -421,6 +421,9 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments, t if human_review is not None: params["human_review"] = human_review + if inspection_id is not None: + params["inspection_id"] = inspection_id + if want_async is True: # If want_async is True, we don't want to wait for a result. As a result wait must be set to 0 to use # want_async. @@ -436,18 +439,8 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments, t # url- and base64-encode the metadata. params["metadata"] = url_encode_dict(metadata, name="metadata", size_limit_bytes=1024) - params["_request_timeout"] = DEFAULT_REQUEST_TIMEOUT - - # If no inspection_id is provided, we submit the image query using image_queries_api (autogenerated via OpenAPI) - # However, our autogenerated code does not currently support inspection_id, so if an inspection_id was - # provided, we use the private API client instead. - if inspection_id is None: - raw_image_query = self.image_queries_api.submit_image_query(**params) - image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) - else: - params["inspection_id"] = inspection_id - iq_id = self.api_client.submit_image_query_with_inspection(**params) - image_query = self.get_image_query(iq_id) + raw_image_query = self.image_queries_api.submit_image_query(**params) + image_query = ImageQuery.parse_obj(raw_image_query.to_dict()) if wait > 0: if confidence_threshold is None: @@ -465,6 +458,7 @@ def ask_confident( # noqa: PLR0913 # pylint: disable=too-many-arguments confidence_threshold: Optional[float] = None, wait: Optional[float] = None, metadata: Union[dict, str, None] = None, + inspection_id: Optional[str] = None, ) -> ImageQuery: """ Evaluates an image with Groundlight waiting until an answer above the confidence threshold @@ -500,14 +494,16 @@ def ask_confident( # noqa: PLR0913 # pylint: disable=too-many-arguments patience_time=wait, human_review=None, metadata=metadata, + inspection_id=inspection_id, ) - def ask_ml( + def ask_ml( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals self, detector: Union[Detector, str], image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], wait: Optional[float] = None, metadata: Union[dict, str, None] = None, + inspection_id: Optional[str] = None, ) -> ImageQuery: """ Evaluates an image with Groundlight, getting the first answer Groundlight can provide. @@ -536,6 +532,7 @@ def ask_ml( image, wait=0, metadata=metadata, + inspection_id=inspection_id, ) if iq_is_answered(iq): return iq @@ -550,6 +547,7 @@ def ask_async( # noqa: PLR0913 # pylint: disable=too-many-arguments confidence_threshold: Optional[float] = None, human_review: Optional[str] = None, metadata: Union[dict, str, None] = None, + inspection_id: Optional[str] = None, ) -> ImageQuery: """ Convenience method for submitting an `ImageQuery` asynchronously. This is equivalent to calling @@ -632,6 +630,7 @@ def ask_async( # noqa: PLR0913 # pylint: disable=too-many-arguments human_review=human_review, want_async=True, metadata=metadata, + inspection_id=inspection_id, ) def wait_for_confident_result( diff --git a/src/groundlight/internalapi.py b/src/groundlight/internalapi.py index e60b624b..2ef6125c 100644 --- a/src/groundlight/internalapi.py +++ b/src/groundlight/internalapi.py @@ -6,14 +6,13 @@ import uuid from enum import Enum from functools import wraps -from typing import Any, Callable, Dict, Optional, Union +from typing import Callable, Optional from urllib.parse import urlsplit, urlunsplit import requests from model import Detector, ImageQuery from openapi_client.api_client import ApiClient, ApiException -from groundlight.images import ByteStreamWrapper from groundlight.status_codes import is_ok from groundlight.version import get_version @@ -248,67 +247,6 @@ def _get_detector_by_name(self, name: str) -> Detector: ) return Detector.parse_obj(parsed["results"][0]) - @RequestsRetryDecorator() - def submit_image_query_with_inspection( # noqa: PLR0913 # pylint: disable=too-many-arguments - self, - detector_id: str, - body: ByteStreamWrapper, - inspection_id: str, - patience_time: Optional[float] = None, - human_review: str = "DEFAULT", - metadata: Optional[dict] = None, - want_async: Optional[bool] = False, - _request_timeout: Optional[float] = None, - ) -> str: - """Submits an image query to the API and returns the ID of the image query. - The image query will be associated to the inspection_id provided. - """ - - url = f"{self.configuration.host}/posichecks" - - params: Dict[str, Union[str, float, bool, Dict[Any, Any], None]] = { - "inspection_id": inspection_id, - "predictor_id": detector_id, - "want_async": want_async, - } - - if metadata is not None: - params["metadata"] = metadata - if patience_time is not None: - params["patience_time"] = float(patience_time) - - # In the API, 'send_notification' is used to control human_review escalation. This will eventually - # be deprecated, but for now we need to support it in the following manner: - if human_review == "ALWAYS": - params["send_notification"] = True - elif human_review == "NEVER": - params["send_notification"] = False - else: - pass # don't send the send_notifications param, allow "DEFAULT" behavior - - headers = self._headers() - headers["Content-Type"] = "image/jpeg" - - response = requests.request( - "POST", - url, - headers=headers, - params=params, - data=body.read(), - verify=self.configuration.verify_ssl, - timeout=_request_timeout, - ) - - if not is_ok(response.status_code): - logger.info(response) - raise InternalApiError( - status=response.status_code, - reason=f"Error submitting image query with inspection ID {inspection_id} on detector {detector_id}", - http_resp=response, - ) - - return response.json()["id"] - @RequestsRetryDecorator() def start_inspection(self) -> str: """Starts an inspection, returns the ID."""