From b364c6f371a9bbf49e5111b2c4902a7cad064b50 Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Wed, 14 May 2025 17:37:24 -0700 Subject: [PATCH 01/12] Support creating text-mode detectors --- src/groundlight/experimental_api.py | 55 +++++++++++++++++++++++++++++ test/unit/test_experimental.py | 17 +++++++++ test/unit/test_labels.py | 18 ++++++++++ 3 files changed, 90 insertions(+) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index c526b833..ba7d0cfe 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -30,6 +30,7 @@ from groundlight_openapi_client.model.payload_template_request import PayloadTemplateRequest from groundlight_openapi_client.model.rule_request import RuleRequest from groundlight_openapi_client.model.status_enum import StatusEnum +from groundlight_openapi_client.model.text_mode_configuration import TextModeConfiguration from groundlight_openapi_client.model.webhook_action_request import WebhookActionRequest from model import ( ROI, @@ -1053,6 +1054,60 @@ def create_bounding_box_detector( # noqa: PLR0913 # pylint: disable=too-many-ar obj = self.detectors_api.create_detector(detector_creation_input, _request_timeout=DEFAULT_REQUEST_TIMEOUT) return Detector.parse_obj(obj.to_dict()) + def create_text_recognition_detector( + self, + name: str, + query: str, + *, + group_name: Optional[str] = None, + confidence_threshold: Optional[float] = None, + patience_time: Optional[float] = None, + pipeline_config: Optional[str] = None, + metadata: Union[dict, str, None] = None, + ) -> Detector: + """ + Creates a text recognition detector that can read specified spans of text from images. + + **Example usage**:: + + gl = ExperimentalApi() + + # Create a text recognition detector + detector = gl.create_text_recognition_detector( + name="date_and_time_detector", + query="Read the date and time from the bottom left corner of the image.", + ) + + :param name: A short, descriptive name for the detector. + :param query: A question about the object to detect in the image. + :param group_name: Optional name of a group to organize related detectors together. + :param confidence_threshold: A value that sets the minimum confidence level required for the ML model's + predictions. If confidence is below this threshold, the query may be sent for human review. + :param patience_time: The maximum time in seconds that Groundlight will attempt to generate a + confident prediction before falling back to human review. Defaults to 30 seconds. + :param pipeline_config: Advanced usage only. Configuration string needed to instantiate a specific + prediction pipeline for this detector. + :param metadata: A dictionary or JSON string containing custom key/value pairs to associate with + + :return: The created Detector object + """ + + detector_creation_input = self._prep_create_detector( + name=name, + query=query, + group_name=group_name, + confidence_threshold=confidence_threshold, + patience_time=patience_time, + pipeline_config=pipeline_config, + metadata=metadata, + ) + detector_creation_input.mode = ModeEnum.TEXT + mode_config = TextModeConfiguration() + + detector_creation_input.mode_configuration = mode_config + obj = self.detectors_api.create_detector(detector_creation_input, _request_timeout=DEFAULT_REQUEST_TIMEOUT) + return Detector.parse_obj(obj.to_dict()) + def _download_mlbinary_url(self, detector: Union[str, Detector]) -> EdgeModelInfo: """ Gets a temporary presigned URL to download the model binaries for the given detector, along diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index ee5a7940..5715de36 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -147,6 +147,23 @@ def test_multiclass_detector(gl_experimental: ExperimentalApi): assert mc_iq.result.label in class_names +@pytest.mark.skip( + reason=( + "General users currently currently can't use text recognition detectors. If you have questions, reach out" + " to Groundlight support, or upgrade your plan." + ) +) +def test_text_recognition_detector(gl_experimental: ExperimentalApi): + """ + verify that we can create and submit to a text recognition detector + """ + name = f"Test {datetime.utcnow()}" + created_detector = gl_experimental.create_text_recognition_detector(name, "What is the date and time?") + assert created_detector is not None + mc_iq = gl_experimental.submit_image_query(created_detector, "test/assets/dog.jpeg") + assert mc_iq.result.label is not None + + @pytest.mark.skip( reason=( "General users currently currently can't use bounding box detectors. If you have questions, reach out" diff --git a/test/unit/test_labels.py b/test/unit/test_labels.py index 51728503..49f9df5a 100644 --- a/test/unit/test_labels.py +++ b/test/unit/test_labels.py @@ -64,3 +64,21 @@ def test_multiclass_labels(gl_experimental: ExperimentalApi): assert iq1.result.label == "cherry" with pytest.raises(ApiException) as _: gl_experimental.add_label(iq1, "MAYBE") + + +def test_text_recognition_labels(gl_experimental: ExperimentalApi): + name = f"Test text recognition labels{datetime.utcnow()}" + det = gl_experimental.create_text_recognition_detector(name, "test_query") + iq1 = gl_experimental.submit_image_query(det, "test/assets/cat.jpeg") + gl_experimental.add_label(iq1, "apple text") + iq1 = gl_experimental.get_image_query(iq1.id) + assert iq1.result.label == "apple text" + gl_experimental.add_label(iq1, "banana text") + iq1 = gl_experimental.get_image_query(iq1.id) + assert iq1.result.label == "banana text" + gl_experimental.add_label(iq1, "") + iq1 = gl_experimental.get_image_query(iq1.id) + assert iq1.result.label == "" + + with pytest.raises(ApiException) as _: + gl_experimental.add_label(iq1, "MAYBE") From 2a6a61e4430b7d33a4b992f15f5b33a399b1130d Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 10:38:31 -0700 Subject: [PATCH 02/12] fix lint issue --- src/groundlight/experimental_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index ba7d0cfe..3044d934 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -1054,7 +1054,7 @@ def create_bounding_box_detector( # noqa: PLR0913 # pylint: disable=too-many-ar obj = self.detectors_api.create_detector(detector_creation_input, _request_timeout=DEFAULT_REQUEST_TIMEOUT) return Detector.parse_obj(obj.to_dict()) - def create_text_recognition_detector( + def create_text_recognition_detector( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals self, name: str, query: str, From a7267662ac9c06ea7d6d4a3f3cb97b38a695b6e1 Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 11:19:15 -0700 Subject: [PATCH 03/12] Fix test --- test/unit/test_labels.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/test_labels.py b/test/unit/test_labels.py index 49f9df5a..06af588d 100644 --- a/test/unit/test_labels.py +++ b/test/unit/test_labels.py @@ -72,13 +72,13 @@ def test_text_recognition_labels(gl_experimental: ExperimentalApi): iq1 = gl_experimental.submit_image_query(det, "test/assets/cat.jpeg") gl_experimental.add_label(iq1, "apple text") iq1 = gl_experimental.get_image_query(iq1.id) - assert iq1.result.label == "apple text" + assert iq1.result.text == "apple text" gl_experimental.add_label(iq1, "banana text") iq1 = gl_experimental.get_image_query(iq1.id) - assert iq1.result.label == "banana text" + assert iq1.result.text == "banana text" gl_experimental.add_label(iq1, "") iq1 = gl_experimental.get_image_query(iq1.id) - assert iq1.result.label == "" + assert iq1.result.text == "" with pytest.raises(ApiException) as _: gl_experimental.add_label(iq1, "MAYBE") From 31a1a6cc3af785cd53b22730ff6eae24f4cca5b7 Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 12:14:03 -0700 Subject: [PATCH 04/12] Clean up a few testing things --- test/integration/test_groundlight.py | 43 +++++----------------------- test/unit/conftest.py | 31 ++++++++++++++++++-- test/unit/test_experimental.py | 43 +++++++++------------------- test/unit/test_labels.py | 5 ++-- 4 files changed, 53 insertions(+), 69 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index c51187e3..921aa446 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -9,7 +9,7 @@ from typing import Any, Dict, Optional, Union import pytest -from groundlight import Groundlight +from groundlight import Groundlight, ExperimentalApi from groundlight.binary_labels import VALID_DISPLAY_LABELS, Label, convert_internal_label_to_display from groundlight.internalapi import ApiException, InternalApiError, NotFoundError from groundlight.optional_imports import * @@ -72,35 +72,6 @@ def is_valid_display_label(label: str) -> bool: return label in VALID_DISPLAY_LABELS -@pytest.fixture(name="gl") -def fixture_gl() -> Groundlight: - """Creates a Groundlight client object for testing.""" - _gl = Groundlight() - _gl.DEFAULT_WAIT = 10 - return _gl - - -@pytest.fixture(name="detector") -def fixture_detector(gl: Groundlight) -> Detector: - """Creates a new Test detector.""" - name = f"Test {datetime.utcnow()}" # Need a unique name - query = "Is there a dog?" - pipeline_config = "never-review" - return gl.create_detector(name=name, query=query, pipeline_config=pipeline_config) - - -@pytest.fixture(name="image_query_yes") -def fixture_image_query_yes(gl: Groundlight, detector: Detector) -> ImageQuery: - iq = gl.submit_image_query(detector=detector.id, image="test/assets/dog.jpeg", human_review="NEVER") - return iq - - -@pytest.fixture(name="image_query_no") -def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: - iq = gl.submit_image_query(detector=detector.id, image="test/assets/cat.jpeg", human_review="NEVER") - return iq - - @pytest.fixture(name="image") def fixture_image() -> str: return "test/assets/dog.jpeg" @@ -112,9 +83,9 @@ def test_create_detector(gl: Groundlight): _detector = gl.create_detector(name=name, query=query) assert str(_detector) assert isinstance(_detector, Detector) - assert ( - _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD - ), "We expected the default confidence threshold to be used." + assert _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD, ( + "We expected the default confidence threshold to be used." + ) def test_create_detector_with_pipeline_config(gl: Groundlight): @@ -178,9 +149,9 @@ def test_create_detector_with_confidence_threshold(gl: Groundlight): # If the confidence is not provided, we will use the existing detector's confidence. retrieved_detector = gl.get_or_create_detector(name=name, query=query) - assert ( - retrieved_detector.confidence_threshold == confidence_threshold - ), "We expected to retrieve the existing detector's confidence, but got a different value." + assert retrieved_detector.confidence_threshold == confidence_threshold, ( + "We expected to retrieve the existing detector's confidence, but got a different value." + ) @pytest.mark.skip_for_edge_endpoint(reason="The edge-endpoint does not support passing detector metadata.") diff --git a/test/unit/conftest.py b/test/unit/conftest.py index 96ed1aa7..10dc58b7 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -31,6 +31,17 @@ def fixture_detector(gl: Groundlight) -> Detector: return gl.create_detector(name=name, query=query, pipeline_config=pipeline_config) +@pytest.fixture(name="count_detector") +def fixture_count_detector(gl_experimental: ExperimentalApi) -> Detector: + """Creates a new Test detector.""" + name = f"Test {datetime.utcnow()}" # Need a unique name + query = "How many dogs?" + pipeline_config = "never-review-multi" # always predicts 0 + return gl_experimental.create_counting_detector( + name=name, query=query, class_name="dog", pipeline_config=pipeline_config + ) + + @pytest.fixture(name="image_query_yes") def fixture_image_query_yes(gl: Groundlight, detector: Detector) -> ImageQuery: iq = gl.submit_image_query(detector=detector.id, image="test/assets/dog.jpeg", human_review="NEVER") @@ -43,9 +54,25 @@ def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: return iq +@pytest.fixture(name="image_query_one") +def fixture_image_query_one(gl_experimental: Groundlight, detector: Detector) -> ImageQuery: + iq = gl_experimental.submit_image_query(detector=detector.id, image="test/assets/dog.jpeg", human_review="NEVER") + return iq + + +@pytest.fixture(name="image_query_zero") +def fixture_image_query_zero(gl_experimental: Groundlight, detector: Detector) -> ImageQuery: + iq = gl_experimental.submit_image_query( + detector=detector.id, image="test/assets/no_dogs.jpeg", human_review="NEVER" + ) + return iq + + @pytest.fixture(name="gl_experimental") -def _gl() -> ExperimentalApi: - return ExperimentalApi() +def fixture_gl_experimental() -> ExperimentalApi: + _gl = ExperimentalApi() + _gl.DEFAULT_WAIT = 10 + return _gl @pytest.fixture(name="initial_iq") diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index 5715de36..bb677d96 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -2,9 +2,10 @@ from datetime import datetime, timezone import pytest -from groundlight import ExperimentalApi from model import Detector, ImageQuery +from groundlight import ExperimentalApi + def test_detector_groups(gl_experimental: ExperimentalApi): """ @@ -66,19 +67,13 @@ def test_update_detector_escalation_type(gl_experimental: ExperimentalApi): updated_detector.escalation_type == "STANDARD" -@pytest.mark.skip( - reason=( - "Users currently don't have permission to turn object detection on their own. If you have questions, reach out" - " to Groundlight support." - ) -) -def test_submit_roi(gl_experimental: ExperimentalApi, image_query_yes: ImageQuery): +def test_submit_roi(gl_experimental: ExperimentalApi, image_query_one: ImageQuery): """ verify that we can submit an ROI """ label_name = "dog" roi = gl_experimental.create_roi(label_name, (0, 0), (0.5, 0.5)) - gl_experimental.add_label(image_query_yes.id, "YES", [roi]) + gl_experimental.add_label(image_query_one.id, "YES", [roi]) @pytest.mark.skip( @@ -87,13 +82,13 @@ def test_submit_roi(gl_experimental: ExperimentalApi, image_query_yes: ImageQuer " to Groundlight support." ) ) -def test_submit_multiple_rois(gl_experimental: ExperimentalApi, image_query_no: ImageQuery): +def test_submit_multiple_rois(gl_experimental: ExperimentalApi, image_query_one: ImageQuery): """ verify that we can submit multiple ROIs """ label_name = "dog" roi = gl_experimental.create_roi(label_name, (0, 0), (0.5, 0.5)) - gl_experimental.add_label(image_query_no, "YES", [roi] * 3) + gl_experimental.add_label(image_query_one, 3, [roi] * 3) def test_counting_detector(gl_experimental: ExperimentalApi): @@ -101,7 +96,7 @@ def test_counting_detector(gl_experimental: ExperimentalApi): verify that we can create and submit to a counting detector """ name = f"Test {datetime.utcnow()}" - created_detector = gl_experimental.create_counting_detector(name, "How many dogs", "dog") + created_detector = gl_experimental.create_counting_detector(name, "How many dogs", "dog", confidence_threshold=0.0) assert created_detector is not None count_iq = gl_experimental.submit_image_query(created_detector, "test/assets/dog.jpeg") assert count_iq.result.count is not None @@ -112,7 +107,7 @@ def test_counting_detector_async(gl_experimental: ExperimentalApi): verify that we can create and submit to a counting detector """ name = f"Test {datetime.utcnow()}" - created_detector = gl_experimental.create_counting_detector(name, "How many dogs", "dog") + created_detector = gl_experimental.create_counting_detector(name, "How many dogs", "dog", confidence_threshold=0.0) assert created_detector is not None async_iq = gl_experimental.ask_async(created_detector, "test/assets/dog.jpeg") # attempting to access fields within the result should raise an exception @@ -126,12 +121,6 @@ def test_counting_detector_async(gl_experimental: ExperimentalApi): assert _image_query.result is not None -@pytest.mark.skip( - reason=( - "General users currently currently can't use multiclass detectors. If you have questions, reach out" - " to Groundlight support, or upgrade your plan." - ) -) def test_multiclass_detector(gl_experimental: ExperimentalApi): """ verify that we can create and submit to a multi-class detector @@ -139,7 +128,7 @@ def test_multiclass_detector(gl_experimental: ExperimentalApi): name = f"Test {datetime.utcnow()}" class_names = ["Golden Retriever", "Labrador Retriever", "Poodle"] created_detector = gl_experimental.create_multiclass_detector( - name, "What kind of dog is this?", class_names=class_names + name, "What kind of dog is this?", class_names=class_names, confidence_threshold=0.0 ) assert created_detector is not None mc_iq = gl_experimental.submit_image_query(created_detector, "test/assets/dog.jpeg") @@ -147,18 +136,14 @@ def test_multiclass_detector(gl_experimental: ExperimentalApi): assert mc_iq.result.label in class_names -@pytest.mark.skip( - reason=( - "General users currently currently can't use text recognition detectors. If you have questions, reach out" - " to Groundlight support, or upgrade your plan." - ) -) def test_text_recognition_detector(gl_experimental: ExperimentalApi): """ verify that we can create and submit to a text recognition detector """ name = f"Test {datetime.utcnow()}" - created_detector = gl_experimental.create_text_recognition_detector(name, "What is the date and time?") + created_detector = gl_experimental.create_text_recognition_detector( + name, "What is the date and time?", confidence_threshold=0.0 + ) assert created_detector is not None mc_iq = gl_experimental.submit_image_query(created_detector, "test/assets/dog.jpeg") assert mc_iq.result.label is not None @@ -176,7 +161,7 @@ def test_bounding_box_detector(gl_experimental: ExperimentalApi): """ name = f"Test {datetime.now(timezone.utc)}" created_detector = gl_experimental.create_bounding_box_detector( - name, "Draw a bounding box around each dog in the image", "dog" + name, "Draw a bounding box around each dog in the image", "dog", confidence_threshold=0.0 ) assert created_detector is not None bbox_iq = gl_experimental.submit_image_query(created_detector, "test/assets/dog.jpeg") @@ -196,7 +181,7 @@ def test_bounding_box_detector_async(gl_experimental: ExperimentalApi): """ name = f"Test {datetime.now(timezone.utc)}" created_detector = gl_experimental.create_bounding_box_detector( - name, "Draw a bounding box around each dog in the image", "dog" + name, "Draw a bounding box around each dog in the image", "dog", confidence_threshold=0.0 ) assert created_detector is not None async_iq = gl_experimental.ask_async(created_detector, "test/assets/dog.jpeg") diff --git a/test/unit/test_labels.py b/test/unit/test_labels.py index 06af588d..770a379e 100644 --- a/test/unit/test_labels.py +++ b/test/unit/test_labels.py @@ -80,5 +80,6 @@ def test_text_recognition_labels(gl_experimental: ExperimentalApi): iq1 = gl_experimental.get_image_query(iq1.id) assert iq1.result.text == "" - with pytest.raises(ApiException) as _: - gl_experimental.add_label(iq1, "MAYBE") + gl_experimental.add_label(iq1, "UNCLEAR") + iq1 = gl_experimental.get_image_query(iq1.id) + assert iq1.result.text is None From 2e25c983aa3f7a93de9ea0c6d7881f908c47943f Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Thu, 15 May 2025 19:14:42 +0000 Subject: [PATCH 05/12] Automatically reformatting code --- test/integration/test_groundlight.py | 14 +++++++------- test/unit/test_experimental.py | 3 +-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 921aa446..08c0c08a 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -9,7 +9,7 @@ from typing import Any, Dict, Optional, Union import pytest -from groundlight import Groundlight, ExperimentalApi +from groundlight import Groundlight from groundlight.binary_labels import VALID_DISPLAY_LABELS, Label, convert_internal_label_to_display from groundlight.internalapi import ApiException, InternalApiError, NotFoundError from groundlight.optional_imports import * @@ -83,9 +83,9 @@ def test_create_detector(gl: Groundlight): _detector = gl.create_detector(name=name, query=query) assert str(_detector) assert isinstance(_detector, Detector) - assert _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD, ( - "We expected the default confidence threshold to be used." - ) + assert ( + _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD + ), "We expected the default confidence threshold to be used." def test_create_detector_with_pipeline_config(gl: Groundlight): @@ -149,9 +149,9 @@ def test_create_detector_with_confidence_threshold(gl: Groundlight): # If the confidence is not provided, we will use the existing detector's confidence. retrieved_detector = gl.get_or_create_detector(name=name, query=query) - assert retrieved_detector.confidence_threshold == confidence_threshold, ( - "We expected to retrieve the existing detector's confidence, but got a different value." - ) + assert ( + retrieved_detector.confidence_threshold == confidence_threshold + ), "We expected to retrieve the existing detector's confidence, but got a different value." @pytest.mark.skip_for_edge_endpoint(reason="The edge-endpoint does not support passing detector metadata.") diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index bb677d96..e62bfe8e 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -2,9 +2,8 @@ from datetime import datetime, timezone import pytest -from model import Detector, ImageQuery - from groundlight import ExperimentalApi +from model import Detector, ImageQuery def test_detector_groups(gl_experimental: ExperimentalApi): From 840a74f09fb5dc1693e9b55703dd00e156e0e92a Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 12:15:37 -0700 Subject: [PATCH 06/12] fix lint issues --- test/integration/test_groundlight.py | 23 ++++++++++++----------- test/unit/conftest.py | 3 ++- test/unit/test_experimental.py | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 08c0c08a..4be63cfa 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -9,11 +9,6 @@ from typing import Any, Dict, Optional, Union import pytest -from groundlight import Groundlight -from groundlight.binary_labels import VALID_DISPLAY_LABELS, Label, convert_internal_label_to_display -from groundlight.internalapi import ApiException, InternalApiError, NotFoundError -from groundlight.optional_imports import * -from groundlight.status_codes import is_user_error from ksuid import KsuidMs from model import ( BinaryClassificationResult, @@ -26,6 +21,12 @@ PaginatedImageQueryList, ) +from groundlight import Groundlight +from groundlight.binary_labels import VALID_DISPLAY_LABELS, Label, convert_internal_label_to_display +from groundlight.internalapi import ApiException, InternalApiError, NotFoundError +from groundlight.optional_imports import * +from groundlight.status_codes import is_user_error + DEFAULT_CONFIDENCE_THRESHOLD = 0.9 IQ_IMPROVEMENT_THRESHOLD = 0.75 @@ -83,9 +84,9 @@ def test_create_detector(gl: Groundlight): _detector = gl.create_detector(name=name, query=query) assert str(_detector) assert isinstance(_detector, Detector) - assert ( - _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD - ), "We expected the default confidence threshold to be used." + assert _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD, ( + "We expected the default confidence threshold to be used." + ) def test_create_detector_with_pipeline_config(gl: Groundlight): @@ -149,9 +150,9 @@ def test_create_detector_with_confidence_threshold(gl: Groundlight): # If the confidence is not provided, we will use the existing detector's confidence. retrieved_detector = gl.get_or_create_detector(name=name, query=query) - assert ( - retrieved_detector.confidence_threshold == confidence_threshold - ), "We expected to retrieve the existing detector's confidence, but got a different value." + assert retrieved_detector.confidence_threshold == confidence_threshold, ( + "We expected to retrieve the existing detector's confidence, but got a different value." + ) @pytest.mark.skip_for_edge_endpoint(reason="The edge-endpoint does not support passing detector metadata.") diff --git a/test/unit/conftest.py b/test/unit/conftest.py index 10dc58b7..caa081f7 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -1,9 +1,10 @@ from datetime import datetime import pytest -from groundlight import ExperimentalApi, Groundlight from model import Detector, ImageQuery, ImageQueryTypeEnum, ResultTypeEnum +from groundlight import ExperimentalApi, Groundlight + def pytest_configure(config): # Run environment check before tests diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index e62bfe8e..bb677d96 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -2,9 +2,10 @@ from datetime import datetime, timezone import pytest -from groundlight import ExperimentalApi from model import Detector, ImageQuery +from groundlight import ExperimentalApi + def test_detector_groups(gl_experimental: ExperimentalApi): """ From 2e74e6a0389af4878a5532b03cc76d7878f51c20 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Thu, 15 May 2025 19:17:05 +0000 Subject: [PATCH 07/12] Automatically reformatting code --- test/integration/test_groundlight.py | 23 +++++++++++------------ test/unit/conftest.py | 3 +-- test/unit/test_experimental.py | 3 +-- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 4be63cfa..08c0c08a 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -9,6 +9,11 @@ from typing import Any, Dict, Optional, Union import pytest +from groundlight import Groundlight +from groundlight.binary_labels import VALID_DISPLAY_LABELS, Label, convert_internal_label_to_display +from groundlight.internalapi import ApiException, InternalApiError, NotFoundError +from groundlight.optional_imports import * +from groundlight.status_codes import is_user_error from ksuid import KsuidMs from model import ( BinaryClassificationResult, @@ -21,12 +26,6 @@ PaginatedImageQueryList, ) -from groundlight import Groundlight -from groundlight.binary_labels import VALID_DISPLAY_LABELS, Label, convert_internal_label_to_display -from groundlight.internalapi import ApiException, InternalApiError, NotFoundError -from groundlight.optional_imports import * -from groundlight.status_codes import is_user_error - DEFAULT_CONFIDENCE_THRESHOLD = 0.9 IQ_IMPROVEMENT_THRESHOLD = 0.75 @@ -84,9 +83,9 @@ def test_create_detector(gl: Groundlight): _detector = gl.create_detector(name=name, query=query) assert str(_detector) assert isinstance(_detector, Detector) - assert _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD, ( - "We expected the default confidence threshold to be used." - ) + assert ( + _detector.confidence_threshold == DEFAULT_CONFIDENCE_THRESHOLD + ), "We expected the default confidence threshold to be used." def test_create_detector_with_pipeline_config(gl: Groundlight): @@ -150,9 +149,9 @@ def test_create_detector_with_confidence_threshold(gl: Groundlight): # If the confidence is not provided, we will use the existing detector's confidence. retrieved_detector = gl.get_or_create_detector(name=name, query=query) - assert retrieved_detector.confidence_threshold == confidence_threshold, ( - "We expected to retrieve the existing detector's confidence, but got a different value." - ) + assert ( + retrieved_detector.confidence_threshold == confidence_threshold + ), "We expected to retrieve the existing detector's confidence, but got a different value." @pytest.mark.skip_for_edge_endpoint(reason="The edge-endpoint does not support passing detector metadata.") diff --git a/test/unit/conftest.py b/test/unit/conftest.py index caa081f7..10dc58b7 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -1,9 +1,8 @@ from datetime import datetime import pytest -from model import Detector, ImageQuery, ImageQueryTypeEnum, ResultTypeEnum - from groundlight import ExperimentalApi, Groundlight +from model import Detector, ImageQuery, ImageQueryTypeEnum, ResultTypeEnum def pytest_configure(config): diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index bb677d96..e62bfe8e 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -2,9 +2,8 @@ from datetime import datetime, timezone import pytest -from model import Detector, ImageQuery - from groundlight import ExperimentalApi +from model import Detector, ImageQuery def test_detector_groups(gl_experimental: ExperimentalApi): From 34e0df770e2a97ee8601cf403c7c880cef775f6c Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 12:17:37 -0700 Subject: [PATCH 08/12] Relocate conftest --- test/{unit => }/conftest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{unit => }/conftest.py (100%) diff --git a/test/unit/conftest.py b/test/conftest.py similarity index 100% rename from test/unit/conftest.py rename to test/conftest.py From 18dd3bbcd592d288367b13d63c072d58d71fd705 Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 12:44:45 -0700 Subject: [PATCH 09/12] Fix pylint issues --- test/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 10dc58b7..20750bee 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -5,11 +5,11 @@ from model import Detector, ImageQuery, ImageQueryTypeEnum, ResultTypeEnum -def pytest_configure(config): +def pytest_configure(config): # pylint: disable=unused-argument # Run environment check before tests gl = Groundlight() - if gl._user_is_privileged(): - raise Exception( + if gl._user_is_privileged(): # pylint: disable=protected-access + raise RuntimeError( "ERROR: You are running tests with a privileged user. Please run tests with a non-privileged user." ) From 6a6b87c0ba8a3d9fa44201ad53c3d4ea61e6ef86 Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 12:59:22 -0700 Subject: [PATCH 10/12] fix one more issue --- test/unit/test_experimental.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index e62bfe8e..20e24c8d 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -145,7 +145,7 @@ def test_text_recognition_detector(gl_experimental: ExperimentalApi): ) assert created_detector is not None mc_iq = gl_experimental.submit_image_query(created_detector, "test/assets/dog.jpeg") - assert mc_iq.result.label is not None + assert mc_iq.result.text is not None @pytest.mark.skip( From 9a39003ac7f05e4fb847fa10e68da46246e7d208 Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 13:30:05 -0700 Subject: [PATCH 11/12] fix one more issue --- test/conftest.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 20750bee..bde11be8 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -55,15 +55,17 @@ def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery: @pytest.fixture(name="image_query_one") -def fixture_image_query_one(gl_experimental: Groundlight, detector: Detector) -> ImageQuery: - iq = gl_experimental.submit_image_query(detector=detector.id, image="test/assets/dog.jpeg", human_review="NEVER") +def fixture_image_query_one(gl_experimental: Groundlight, count_detector: Detector) -> ImageQuery: + iq = gl_experimental.submit_image_query( + detector=count_detector.id, image="test/assets/dog.jpeg", human_review="NEVER" + ) return iq @pytest.fixture(name="image_query_zero") -def fixture_image_query_zero(gl_experimental: Groundlight, detector: Detector) -> ImageQuery: +def fixture_image_query_zero(gl_experimental: Groundlight, count_detector: Detector) -> ImageQuery: iq = gl_experimental.submit_image_query( - detector=detector.id, image="test/assets/no_dogs.jpeg", human_review="NEVER" + detector=count_detector.id, image="test/assets/no_dogs.jpeg", human_review="NEVER" ) return iq From b5cc1ae668f2715e9a74fe288262b2e231150e7a Mon Sep 17 00:00:00 2001 From: Tyler Romero Date: Thu, 15 May 2025 13:40:09 -0700 Subject: [PATCH 12/12] fix one more issue --- test/unit/test_experimental.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_experimental.py b/test/unit/test_experimental.py index 20e24c8d..fad10299 100644 --- a/test/unit/test_experimental.py +++ b/test/unit/test_experimental.py @@ -72,7 +72,7 @@ def test_submit_roi(gl_experimental: ExperimentalApi, image_query_one: ImageQuer """ label_name = "dog" roi = gl_experimental.create_roi(label_name, (0, 0), (0.5, 0.5)) - gl_experimental.add_label(image_query_one.id, "YES", [roi]) + gl_experimental.add_label(image_query_one.id, 1, [roi]) @pytest.mark.skip(