11import logging
22import os
33import time
4+ from functools import partial
45from io import BufferedReader , BytesIO
56from typing import Callable , Optional , Union
67
@@ -304,9 +305,20 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments, t
304305 Any pixel format will get converted to JPEG at high quality before sending to service.
305306 :type image: str or bytes or Image.Image or BytesIO or BufferedReader or np.ndarray
306307
307- :param wait: How long to wait (in seconds) for a confident answer.
308+ :param wait: How long to poll (in seconds) for a confident answer. This is a client-side timeout .
308309 :type wait: float
309310
311+ :param patience_time: How long to wait (in seconds) for a confident answer for this image query.
312+ The longer the patience_time, the more likely Groundlight will arrive at a confident answer.
313+ Within patience_time, Groundlight will update ML predictions based on stronger findings,
314+ and, additionally, Groundlight will prioritize human review of the image query if necessary.
315+ This is a soft server-side timeout. If not set, use the detector's patience_time.
316+ :type patience_time: float
317+
318+ :param confidence_threshold: The confidence threshold to wait for.
319+ If not set, use the detector's confidence threshold.
320+ :type confidence_threshold: float
321+
310322 :param human_review: If `None` or `DEFAULT`, send the image query for human review
311323 only if the ML prediction is not confident.
312324 If set to `ALWAYS`, always send the image query for human review.
@@ -375,8 +387,10 @@ def ask_confident(
375387 confidence_threshold : Optional [float ] = None ,
376388 wait : Optional [float ] = None ,
377389 ) -> ImageQuery :
378- """Evaluates an image with Groundlight waiting until an answer above the confidence threshold
379- of the detector is reached or the wait period has passed.
390+ """
391+ Evaluates an image with Groundlight waiting until an answer above the confidence threshold
392+ of the detector is reached or the wait period has passed.
393+
380394 :param detector: the Detector object, or string id of a detector like `det_12345`
381395 :type detector: Detector or str
382396
@@ -405,6 +419,8 @@ def ask_confident(
405419 image ,
406420 confidence_threshold = confidence_threshold ,
407421 wait = wait ,
422+ patience_time = wait ,
423+ human_review = None ,
408424 )
409425
410426 def ask_ml (
@@ -413,7 +429,9 @@ def ask_ml(
413429 image : Union [str , bytes , Image .Image , BytesIO , BufferedReader , np .ndarray ],
414430 wait : Optional [float ] = None ,
415431 ) -> ImageQuery :
416- """Evaluates an image with Groundlight, getting the first answer Groundlight can provide.
432+ """
433+ Evaluates an image with Groundlight, getting the first answer Groundlight can provide.
434+
417435 :param detector: the Detector object, or string id of a detector like `det_12345`
418436 :type detector: Detector or str
419437
@@ -443,12 +461,13 @@ def ask_ml(
443461 wait = self .DEFAULT_WAIT if wait is None else wait
444462 return self .wait_for_ml_result (iq , timeout_sec = wait )
445463
446- def ask_async (
464+ def ask_async ( # noqa: PLR0913 # pylint: disable=too-many-arguments
447465 self ,
448466 detector : Union [Detector , str ],
449467 image : Union [str , bytes , Image .Image , BytesIO , BufferedReader , np .ndarray ],
468+ patience_time : Optional [float ] = None ,
469+ confidence_threshold : Optional [float ] = None ,
450470 human_review : Optional [str ] = None ,
451- inspection_id : Optional [str ] = None ,
452471 ) -> ImageQuery :
453472 """
454473 Convenience method for submitting an `ImageQuery` asynchronously. This is equivalent to calling
@@ -469,6 +488,17 @@ def ask_async(
469488
470489 :type image: str or bytes or Image.Image or BytesIO or BufferedReader or np.ndarray
471490
491+ :param patience_time: How long to wait (in seconds) for a confident answer for this image query.
492+ The longer the patience_time, the more likely Groundlight will arrive at a confident answer.
493+ Within patience_time, Groundlight will update ML predictions based on stronger findings,
494+ and, additionally, Groundlight will prioritize human review of the image query if necessary.
495+ This is a soft server-side timeout. If not set, use the detector's patience_time.
496+ :type patience_time: float
497+
498+ :param confidence_threshold: The confidence threshold to wait for.
499+ If not set, use the detector's confidence threshold.
500+ :type confidence_threshold: float
501+
472502 :param human_review: If `None` or `DEFAULT`, send the image query for human review
473503 only if the ML prediction is not confident.
474504 If set to `ALWAYS`, always send the image query for human review.
@@ -500,26 +530,34 @@ def ask_async(
500530 assert image_query.id is not None
501531
502532 # Do not attempt to access the result of this query as the result for all async queries
503- # will be None. Your result is being computed asynchronously and will be available
504- # later
533+ # will be None. Your result is being computed asynchronously and will be available later
505534 assert image_query.result is None
506535
507- # retrieve the result later or on another machine by calling gl.get_image_query ()
508- # with the id of the image_query above
509- image_query = gl.get_image_query (image_query.id)
536+ # retrieve the result later or on another machine by calling gl.wait_for_confident_result ()
537+ # with the id of the image_query above. This will block until the result is available.
538+ image_query = gl.wait_for_confident_result (image_query.id)
510539
511540 # now the result will be available for your use
512541 assert image_query.result is not None
513542
543+ # alternatively, you can check if the result is available (without blocking) by calling
544+ # gl.get_image_query() with the id of the image_query above.
545+ image_query = gl.get_image_query(image_query.id)
514546 """
515547 return self .submit_image_query (
516- detector , image , wait = 0 , human_review = human_review , want_async = True , inspection_id = inspection_id
548+ detector ,
549+ image ,
550+ wait = 0 ,
551+ patience_time = patience_time ,
552+ confidence_threshold = confidence_threshold ,
553+ human_review = human_review ,
554+ want_async = True ,
517555 )
518556
519557 def wait_for_confident_result (
520558 self ,
521559 image_query : Union [ImageQuery , str ],
522- confidence_threshold : float ,
560+ confidence_threshold : Optional [ float ] = None ,
523561 timeout_sec : float = 30.0 ,
524562 ) -> ImageQuery :
525563 """
@@ -529,7 +567,8 @@ def wait_for_confident_result(
529567 :param image_query: An ImageQuery object to poll
530568 :type image_query: ImageQuery or str
531569
532- :param confidence_threshold: The minimum confidence level required to return before the timeout.
570+ :param confidence_threshold: The confidence threshold to wait for.
571+ If not set, use the detector's confidence threshold.
533572 :type confidence_threshold: float
534573
535574 :param timeout_sec: The maximum number of seconds to wait.
@@ -538,10 +577,12 @@ def wait_for_confident_result(
538577 :return: ImageQuery
539578 :rtype: ImageQuery
540579 """
580+ if confidence_threshold is None :
581+ if isinstance (image_query , str ):
582+ image_query = self .get_image_query (image_query )
583+ confidence_threshold = self .get_detector (image_query .detector_id ).confidence_threshold
541584
542- def confidence_above_thresh (iq ):
543- return iq_is_confident (iq , confidence_threshold = confidence_threshold )
544-
585+ confidence_above_thresh = partial (iq_is_confident , confidence_threshold = confidence_threshold )
545586 return self ._wait_for_result (image_query , condition = confidence_above_thresh , timeout_sec = timeout_sec )
546587
547588 def wait_for_ml_result (self , image_query : Union [ImageQuery , str ], timeout_sec : float = 30.0 ) -> ImageQuery :
@@ -551,9 +592,6 @@ def wait_for_ml_result(self, image_query: Union[ImageQuery, str], timeout_sec: f
551592 :param image_query: An ImageQuery object to poll
552593 :type image_query: ImageQuery or str
553594
554- :param confidence_threshold: The minimum confidence level required to return before the timeout.
555- :type confidence_threshold: float
556-
557595 :param timeout_sec: The maximum number of seconds to wait.
558596 :type timeout_sec: float
559597
@@ -623,6 +661,7 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str
623661 else :
624662 image_query_id = str (image_query )
625663 # Some old imagequery id's started with "chk_"
664+ # TODO: handle iqe_ for image_queries returned from edge endpoints
626665 if not image_query_id .startswith (("chk_" , "iq_" )):
627666 raise ValueError (f"Invalid image query id { image_query_id } " )
628667 api_label = convert_display_label_to_internal (image_query_id , label )
0 commit comments