Skip to content

Commit b89949b

Browse files
Brandonclaude
andcommitted
Add delete_detector method to Groundlight SDK
- Implement delete_detector() method in client.py that accepts both Detector objects and ID strings - Add comprehensive integration test covering deletion by object, by ID, and error handling - Follow existing SDK patterns for parameter handling and error conversion - Include proper documentation with usage examples and warnings about irreversible deletion 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e438031 commit b89949b

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

src/groundlight/client.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,43 @@ def update_detector_escalation_type(self, detector: Union[str, Detector], escala
14601460
patched_detector_request=PatchedDetectorRequest(escalation_type=escalation_type),
14611461
)
14621462

1463+
def delete_detector(self, detector: Union[str, Detector]) -> None:
1464+
"""
1465+
Delete a detector. This permanently removes the detector and all its associated data.
1466+
1467+
.. warning::
1468+
This operation is irreversible. Once a detector is deleted, it cannot be recovered.
1469+
All associated image queries and training data will also be permanently deleted.
1470+
1471+
**Example usage**::
1472+
1473+
gl = Groundlight()
1474+
1475+
# Using a detector object
1476+
detector = gl.get_detector("det_abc123")
1477+
gl.delete_detector(detector)
1478+
1479+
# Using a detector ID string directly
1480+
gl.delete_detector("det_abc123")
1481+
1482+
:param detector: Either a Detector object or a detector ID string starting with "det_".
1483+
The detector to delete.
1484+
1485+
:return: None
1486+
:raises NotFoundError: If the detector with the given ID does not exist
1487+
:raises ApiTokenError: If API token is invalid
1488+
:raises GroundlightClientError: For other API errors
1489+
"""
1490+
if isinstance(detector, Detector):
1491+
detector_id = detector.id
1492+
else:
1493+
detector_id = str(detector)
1494+
1495+
try:
1496+
self.detectors_api.delete_detector(id=detector_id, _request_timeout=DEFAULT_REQUEST_TIMEOUT)
1497+
except NotFoundException as e:
1498+
raise NotFoundError(f"Detector with id '{detector_id}' not found") from e
1499+
14631500
def create_counting_detector( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals
14641501
self,
14651502
name: str,

test/integration/test_groundlight.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,3 +848,41 @@ def test_multiclass_detector(gl: Groundlight):
848848
mc_iq = gl.submit_image_query(created_detector, "test/assets/dog.jpeg")
849849
assert mc_iq.result.label is not None
850850
assert mc_iq.result.label in class_names
851+
852+
853+
def test_delete_detector(gl: Groundlight):
854+
"""
855+
Test deleting a detector by both ID and object, and verify proper error handling.
856+
"""
857+
# Create a detector to delete
858+
name = f"Test delete detector {datetime.utcnow()}"
859+
query = "Is there a dog to delete?"
860+
pipeline_config = "never-review"
861+
detector = gl.create_detector(name=name, query=query, pipeline_config=pipeline_config)
862+
863+
# Verify the detector exists
864+
retrieved_detector = gl.get_detector(detector.id)
865+
assert retrieved_detector.id == detector.id
866+
867+
# Delete using detector object
868+
gl.delete_detector(detector)
869+
870+
# Verify the detector is actually deleted
871+
with pytest.raises(NotFoundError):
872+
gl.get_detector(detector.id)
873+
874+
# Create another detector to test deletion by ID string
875+
name2 = f"Test delete detector 2 {datetime.utcnow()}"
876+
detector2 = gl.create_detector(name=name2, query=query, pipeline_config=pipeline_config)
877+
878+
# Delete using detector ID string
879+
gl.delete_detector(detector2.id)
880+
881+
# Verify the second detector is also deleted
882+
with pytest.raises(NotFoundError):
883+
gl.get_detector(detector2.id)
884+
885+
# Test deleting a non-existent detector raises NotFoundError
886+
fake_detector_id = "det_fake123456789"
887+
with pytest.raises(NotFoundError):
888+
gl.delete_detector(fake_detector_id)

0 commit comments

Comments
 (0)