From cfa597debe1974d7c4ff8f19a2cfd23df2b5a191 Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes Date: Thu, 5 Feb 2026 14:45:05 -0500 Subject: [PATCH 1/9] add method to data_client for updating bounding box, finish adding confidence param --- src/viam/app/data_client.py | 79 ++++++++++++++++++++++++++++++++++++- tests/test_data_client.py | 27 +++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index d255995e9..2d0a8e0bc 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -14,6 +14,8 @@ AddBinaryDataToDatasetByIDsRequest, AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse, + UpdateBoundingBoxRequest, + UpdateBoundingBoxResponse, AddTagsToBinaryDataByFilterRequest, AddTagsToBinaryDataByIDsRequest, BinaryData, @@ -1131,6 +1133,7 @@ async def add_bounding_box_to_image_by_id( y_min_normalized: float, x_max_normalized: float, y_max_normalized: float, + confidence_score=None, ) -> str: """Add a bounding box to an image. @@ -1142,7 +1145,8 @@ async def add_bounding_box_to_image_by_id( x_min_normalized=0, y_min_normalized=.1, x_max_normalized=.2, - y_max_normalized=.3 + y_max_normalized=.3, + confidence_score=.95 ) print(bbox_id) @@ -1156,6 +1160,7 @@ async def add_bounding_box_to_image_by_id( y_min_normalized (float): Min Y value of the bounding box normalized from 0 to 1. x_max_normalized (float): Max X value of the bounding box normalized from 0 to 1. y_max_normalized (float): Max Y value of the bounding box normalized from 0 to 1. + confidence_score (float): Confidence level of the bounding box being correct. Raises: GRPCError: If the X or Y values are outside of the [0, 1] range. @@ -1174,6 +1179,7 @@ async def add_bounding_box_to_image_by_id( x_min_normalized=x_min_normalized, y_max_normalized=y_max_normalized, y_min_normalized=y_min_normalized, + confidence=confidence_score, ) else: request = AddBoundingBoxToImageByIDRequest( @@ -1183,10 +1189,81 @@ async def add_bounding_box_to_image_by_id( x_min_normalized=x_min_normalized, y_max_normalized=y_max_normalized, y_min_normalized=y_min_normalized, + confidence=confidence_score, ) response: AddBoundingBoxToImageByIDResponse = await self._data_client.AddBoundingBoxToImageByID(request, metadata=self._metadata) return response.bbox_id + async def update_bounding_box_to_image_by_id( + self, + binary_id: Union[BinaryID, str], + bbox_id: str, + label: str, + x_min_normalized: float, + y_min_normalized: float, + x_max_normalized: float, + y_max_normalized: float, + confidence_score=None, + ) -> None: + """Update a bounding box in an image. + + :: + + bbox_id = await data_client.update_bounding_box_to_image_by_id( + binary_id="", + bbox_id="2" + label="label", + x_min_normalized=0, + y_min_normalized=.1, + x_max_normalized=.2, + y_max_normalized=.3, + confidence_score=.95 + ) + + Args: + binary_id (Union[~viam.proto.app.data.BinaryID, str]): The binary data ID or :class:`BinaryID` of the image to add the bounding + box to. *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + bbox_id (str): The ID of the bounding box to be updated + label (str): A label for the bounding box. + x_min_normalized (float): Min X value of the bounding box normalized from 0 to 1. + y_min_normalized (float): Min Y value of the bounding box normalized from 0 to 1. + x_max_normalized (float): Max X value of the bounding box normalized from 0 to 1. + y_max_normalized (float): Max Y value of the bounding box normalized from 0 to 1. + confidence_score (float): Confidence level of the bounding box being correct. + + Raises: + GRPCError: If the X or Y values are outside of the [0, 1] range. + + Returns: + N/A + + For more information, see `Data Client API `_. + """ + UpdateBoundingBoxRequest() + if isinstance(binary_id, str): + UpdateBoundingBoxRequest( + binary_data_id=binary_id, + bbox_id=bbox_id, + label=label, + x_max_normalized=x_max_normalized, + x_min_normalized=x_min_normalized, + y_max_normalized=y_max_normalized, + y_min_normalized=y_min_normalized, + confidence=confidence_score, + ) + else: + UpdateBoundingBoxRequest( + binary_id=binary_id, + bbox_id=bbox_id, + label=label, + x_max_normalized=x_max_normalized, + x_min_normalized=x_min_normalized, + y_max_normalized=y_max_normalized, + y_min_normalized=y_min_normalized, + confidence=confidence_score, + ) + async def remove_bounding_box_from_image_by_id(self, bbox_id: str, binary_id: Union[BinaryID, str]) -> None: """Removes a bounding box from an image. diff --git a/tests/test_data_client.py b/tests/test_data_client.py index b5183c88b..c4f21af31 100644 --- a/tests/test_data_client.py +++ b/tests/test_data_client.py @@ -398,6 +398,7 @@ async def test_add_bounding_box_to_image_by_id(self, service: MockData): y_min_normalized=0.1, x_max_normalized=0.2, y_max_normalized=0.3, + confidence_score=0.95, ) assert bbox_label == BBOX_LABEL bbox_label = await client.add_bounding_box_to_image_by_id( @@ -407,9 +408,35 @@ async def test_add_bounding_box_to_image_by_id(self, service: MockData): y_min_normalized=0.1, x_max_normalized=0.2, y_max_normalized=0.3, + confidence_score=0.95, ) assert bbox_label == BBOX_LABEL + async def test_update_bounding_box_to_image_by_id(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.update_bounding_box_to_image_by_id( + binary_id=BINARY_ID, + bbox_id="2", + label="label", + x_min_normalized=0, + y_min_normalized=0.1, + x_max_normalized=0.2, + y_max_normalized=0.3, + confidence_score=0.95, + ) + + await client.update_bounding_box_to_image_by_id( + binary_id=BINARY_DATA_ID, + label="label", + bbox_id="2", + x_min_normalized=0, + y_min_normalized=0.1, + x_max_normalized=0.2, + y_max_normalized=0.3, + confidence_score=0.95, + ) + async def test_remove_bounding_box_from_image_by_id(self, service: MockData): async with ChannelFor([service]) as channel: client = DataClient(channel, DATA_SERVICE_METADATA) From 8e721349e18a6379e9b600d2cc9808530ba03940 Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes Date: Thu, 5 Feb 2026 14:49:43 -0500 Subject: [PATCH 2/9] remove unnecessary import --- src/viam/app/data_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index 2d0a8e0bc..b23895384 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -15,7 +15,6 @@ AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse, UpdateBoundingBoxRequest, - UpdateBoundingBoxResponse, AddTagsToBinaryDataByFilterRequest, AddTagsToBinaryDataByIDsRequest, BinaryData, From b4d37335cf09920c233d2f27111206503efedf2d Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes <34491386+lvhg@users.noreply.github.com> Date: Fri, 6 Feb 2026 16:39:20 -0500 Subject: [PATCH 3/9] specify confidence_score to be an optional float Co-authored-by: Naveed Jooma --- src/viam/app/data_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index b23895384..44d43d8c2 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -1202,7 +1202,7 @@ async def update_bounding_box_to_image_by_id( y_min_normalized: float, x_max_normalized: float, y_max_normalized: float, - confidence_score=None, + confidence_score: Optional[float] = None, ) -> None: """Update a bounding box in an image. From a701b4a00e8278e5d3ae58b00b12a576f340652a Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes Date: Tue, 10 Feb 2026 16:06:39 -0500 Subject: [PATCH 4/9] remove deprecated binaryID from new update bounding box method --- src/viam/app/data_client.py | 39 +++++++++++++------------------------ tests/test_data_client.py | 10 ---------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index b23895384..90e9e9513 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -1195,7 +1195,7 @@ async def add_bounding_box_to_image_by_id( async def update_bounding_box_to_image_by_id( self, - binary_id: Union[BinaryID, str], + binary_id: str, bbox_id: str, label: str, x_min_normalized: float, @@ -1220,9 +1220,8 @@ async def update_bounding_box_to_image_by_id( ) Args: - binary_id (Union[~viam.proto.app.data.BinaryID, str]): The binary data ID or :class:`BinaryID` of the image to add the bounding - box to. *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a - list of strings.* + binary_id (str): The binary data ID of the image to add the bounding + box to. bbox_id (str): The ID of the bounding box to be updated label (str): A label for the bounding box. x_min_normalized (float): Min X value of the bounding box normalized from 0 to 1. @@ -1240,28 +1239,16 @@ async def update_bounding_box_to_image_by_id( For more information, see `Data Client API `_. """ UpdateBoundingBoxRequest() - if isinstance(binary_id, str): - UpdateBoundingBoxRequest( - binary_data_id=binary_id, - bbox_id=bbox_id, - label=label, - x_max_normalized=x_max_normalized, - x_min_normalized=x_min_normalized, - y_max_normalized=y_max_normalized, - y_min_normalized=y_min_normalized, - confidence=confidence_score, - ) - else: - UpdateBoundingBoxRequest( - binary_id=binary_id, - bbox_id=bbox_id, - label=label, - x_max_normalized=x_max_normalized, - x_min_normalized=x_min_normalized, - y_max_normalized=y_max_normalized, - y_min_normalized=y_min_normalized, - confidence=confidence_score, - ) + UpdateBoundingBoxRequest( + binary_data_id=binary_id, + bbox_id=bbox_id, + label=label, + x_max_normalized=x_max_normalized, + x_min_normalized=x_min_normalized, + y_max_normalized=y_max_normalized, + y_min_normalized=y_min_normalized, + confidence=confidence_score, + ) async def remove_bounding_box_from_image_by_id(self, bbox_id: str, binary_id: Union[BinaryID, str]) -> None: """Removes a bounding box from an image. diff --git a/tests/test_data_client.py b/tests/test_data_client.py index c4f21af31..6d69fed25 100644 --- a/tests/test_data_client.py +++ b/tests/test_data_client.py @@ -415,16 +415,6 @@ async def test_add_bounding_box_to_image_by_id(self, service: MockData): async def test_update_bounding_box_to_image_by_id(self, service: MockData): async with ChannelFor([service]) as channel: client = DataClient(channel, DATA_SERVICE_METADATA) - await client.update_bounding_box_to_image_by_id( - binary_id=BINARY_ID, - bbox_id="2", - label="label", - x_min_normalized=0, - y_min_normalized=0.1, - x_max_normalized=0.2, - y_max_normalized=0.3, - confidence_score=0.95, - ) await client.update_bounding_box_to_image_by_id( binary_id=BINARY_DATA_ID, From 29edad78d76ea7e5a64df7aecb3400adef30de8e Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes <34491386+lvhg@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:12:32 -0500 Subject: [PATCH 5/9] change confidence_score to optional Co-authored-by: Naveed Jooma --- src/viam/app/data_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index 7a864b785..b025d2645 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -1132,7 +1132,7 @@ async def add_bounding_box_to_image_by_id( y_min_normalized: float, x_max_normalized: float, y_max_normalized: float, - confidence_score=None, + confidence_score: Optional[float] = None, ) -> str: """Add a bounding box to an image. From 64122ea4e72ecc8490acaa7252d9627f6621cb68 Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes <34491386+lvhg@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:13:51 -0500 Subject: [PATCH 6/9] call UpdateBoundingBox Co-authored-by: Naveed Jooma --- src/viam/app/data_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index b025d2645..3672768bc 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -1239,7 +1239,7 @@ async def update_bounding_box_to_image_by_id( For more information, see `Data Client API `_. """ UpdateBoundingBoxRequest() - UpdateBoundingBoxRequest( + request = UpdateBoundingBoxRequest( binary_data_id=binary_id, bbox_id=bbox_id, label=label, @@ -1249,6 +1249,7 @@ async def update_bounding_box_to_image_by_id( y_min_normalized=y_min_normalized, confidence=confidence_score, ) + await self._data_client.UpdateBoundingBox(request, metadata=self._metadata) async def remove_bounding_box_from_image_by_id(self, bbox_id: str, binary_id: Union[BinaryID, str]) -> None: """Removes a bounding box from an image. From e1fbd2fd1d5998795f2a5d069946b5abcfbbdd9f Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes Date: Thu, 12 Feb 2026 12:01:44 -0500 Subject: [PATCH 7/9] add update box function to mock data --- docs/examples/_server.py | 5 +++++ src/viam/app/data_client.py | 1 - tests/mocks/services.py | 8 ++++++++ tests/test_data_client.py | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/examples/_server.py b/docs/examples/_server.py index a44084680..4902ab5d1 100644 --- a/docs/examples/_server.py +++ b/docs/examples/_server.py @@ -156,6 +156,8 @@ AddBinaryDataToDatasetByIDsResponse, AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse, + UpdateBoundingBoxRequest, + UpdateBoundingBoxResponse, AddTagsToBinaryDataByFilterRequest, AddTagsToBinaryDataByFilterResponse, AddTagsToBinaryDataByIDsRequest, @@ -293,6 +295,9 @@ async def TagsByFilter(self, stream: Stream[TagsByFilterRequest, TagsByFilterRes async def AddBoundingBoxToImageByID(self, stream: Stream[AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse]) -> None: pass + async def UpdateBoundingBoxToImageByID(self, stream: Stream[UpdateBoundingBoxRequest, UpdateBoundingBoxResponse]) -> None: + pass + async def RemoveBoundingBoxFromImageByID( self, stream: Stream[RemoveBoundingBoxFromImageByIDRequest, RemoveBoundingBoxFromImageByIDResponse] ) -> None: diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index 3672768bc..ba61af2d9 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -1238,7 +1238,6 @@ async def update_bounding_box_to_image_by_id( For more information, see `Data Client API `_. """ - UpdateBoundingBoxRequest() request = UpdateBoundingBoxRequest( binary_data_id=binary_id, bbox_id=bbox_id, diff --git a/tests/mocks/services.py b/tests/mocks/services.py index 73ae7a7d5..bbb7176b8 100644 --- a/tests/mocks/services.py +++ b/tests/mocks/services.py @@ -201,6 +201,8 @@ AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse, AddTagsToBinaryDataByFilterRequest, + UpdateBoundingBoxRequest, + UpdateBoundingBoxResponse, AddTagsToBinaryDataByFilterResponse, AddTagsToBinaryDataByIDsRequest, AddTagsToBinaryDataByIDsResponse, @@ -998,6 +1000,12 @@ async def AddBoundingBoxToImageByID(self, stream: Stream[AddBoundingBoxToImageBy assert request is not None await stream.send_message(AddBoundingBoxToImageByIDResponse(bbox_id=self.bbox_labels_response[0])) + async def UpdateBoundingBoxToImageByID(self, stream: Stream[UpdateBoundingBoxRequest, UpdateBoundingBoxResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.updated_label = request.bbox_id + await stream.send_message(UpdateBoundingBoxResponse()) + async def RemoveBoundingBoxFromImageByID( self, stream: Stream[RemoveBoundingBoxFromImageByIDRequest, RemoveBoundingBoxFromImageByIDResponse] ) -> None: diff --git a/tests/test_data_client.py b/tests/test_data_client.py index 6d69fed25..9e4938511 100644 --- a/tests/test_data_client.py +++ b/tests/test_data_client.py @@ -426,6 +426,7 @@ async def test_update_bounding_box_to_image_by_id(self, service: MockData): y_max_normalized=0.3, confidence_score=0.95, ) + assert service.updated_label == BBOX_LABEL async def test_remove_bounding_box_from_image_by_id(self, service: MockData): async with ChannelFor([service]) as channel: From 4c35e153d2b0e0e1a973a7d9a94d3641dee4bc8e Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes Date: Thu, 12 Feb 2026 13:51:37 -0500 Subject: [PATCH 8/9] fix mock data and tests --- tests/mocks/services.py | 2 +- tests/test_data_client.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mocks/services.py b/tests/mocks/services.py index bbb7176b8..4da66a201 100644 --- a/tests/mocks/services.py +++ b/tests/mocks/services.py @@ -1000,7 +1000,7 @@ async def AddBoundingBoxToImageByID(self, stream: Stream[AddBoundingBoxToImageBy assert request is not None await stream.send_message(AddBoundingBoxToImageByIDResponse(bbox_id=self.bbox_labels_response[0])) - async def UpdateBoundingBoxToImageByID(self, stream: Stream[UpdateBoundingBoxRequest, UpdateBoundingBoxResponse]) -> None: + async def UpdateBoundingBox(self, stream: Stream[UpdateBoundingBoxRequest, UpdateBoundingBoxResponse]) -> None: request = await stream.recv_message() assert request is not None self.updated_label = request.bbox_id diff --git a/tests/test_data_client.py b/tests/test_data_client.py index 9e4938511..caea4484f 100644 --- a/tests/test_data_client.py +++ b/tests/test_data_client.py @@ -419,7 +419,7 @@ async def test_update_bounding_box_to_image_by_id(self, service: MockData): await client.update_bounding_box_to_image_by_id( binary_id=BINARY_DATA_ID, label="label", - bbox_id="2", + bbox_id=BBOX_LABEL, x_min_normalized=0, y_min_normalized=0.1, x_max_normalized=0.2, From 88baa9b558dbf5d28365994206239fb152248a50 Mon Sep 17 00:00:00 2001 From: Lillian Hwang-Geddes Date: Thu, 19 Feb 2026 16:11:14 -0500 Subject: [PATCH 9/9] change functionname --- docs/examples/_server.py | 2 +- src/viam/app/data_client.py | 6 +++--- tests/test_data_client.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/examples/_server.py b/docs/examples/_server.py index 4902ab5d1..ed408604a 100644 --- a/docs/examples/_server.py +++ b/docs/examples/_server.py @@ -295,7 +295,7 @@ async def TagsByFilter(self, stream: Stream[TagsByFilterRequest, TagsByFilterRes async def AddBoundingBoxToImageByID(self, stream: Stream[AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse]) -> None: pass - async def UpdateBoundingBoxToImageByID(self, stream: Stream[UpdateBoundingBoxRequest, UpdateBoundingBoxResponse]) -> None: + async def UpdateBoundingBox(self, stream: Stream[UpdateBoundingBoxRequest, UpdateBoundingBoxResponse]) -> None: pass async def RemoveBoundingBoxFromImageByID( diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py index ba61af2d9..51d61916d 100644 --- a/src/viam/app/data_client.py +++ b/src/viam/app/data_client.py @@ -1193,7 +1193,7 @@ async def add_bounding_box_to_image_by_id( response: AddBoundingBoxToImageByIDResponse = await self._data_client.AddBoundingBoxToImageByID(request, metadata=self._metadata) return response.bbox_id - async def update_bounding_box_to_image_by_id( + async def update_bounding_box( self, binary_id: str, bbox_id: str, @@ -1208,7 +1208,7 @@ async def update_bounding_box_to_image_by_id( :: - bbox_id = await data_client.update_bounding_box_to_image_by_id( + bbox_id = await data_client.update_bounding_box( binary_id="", bbox_id="2" label="label", @@ -1236,7 +1236,7 @@ async def update_bounding_box_to_image_by_id( Returns: N/A - For more information, see `Data Client API `_. + For more information, see `Data Client API `_. """ request = UpdateBoundingBoxRequest( binary_data_id=binary_id, diff --git a/tests/test_data_client.py b/tests/test_data_client.py index caea4484f..01adca2e4 100644 --- a/tests/test_data_client.py +++ b/tests/test_data_client.py @@ -412,11 +412,11 @@ async def test_add_bounding_box_to_image_by_id(self, service: MockData): ) assert bbox_label == BBOX_LABEL - async def test_update_bounding_box_to_image_by_id(self, service: MockData): + async def test_update_bounding_box(self, service: MockData): async with ChannelFor([service]) as channel: client = DataClient(channel, DATA_SERVICE_METADATA) - await client.update_bounding_box_to_image_by_id( + await client.update_bounding_box( binary_id=BINARY_DATA_ID, label="label", bbox_id=BBOX_LABEL,