2727 help = "Experimental commands — may change or be removed without notice." ,
2828 context_settings = {"help_option_names" : ["-h" , "--help" ], "max_content_width" : 800 },
2929)
30- cli_app .add_typer (experimental_app , name = "experimental" )
31- cli_app .add_typer (experimental_app , name = "exp" )
32-
33-
34- _CLI_PRIMITIVE_TYPES = (str , int , float , bool )
30+ cli_app .add_typer (experimental_app , name = "exp" , rich_help_panel = "Subcommands" )
31+ cli_app .add_typer (experimental_app , name = "experimental" , hidden = True )
3532
3633
3734def is_cli_supported_type (annotation ):
@@ -48,7 +45,7 @@ def is_cli_representable(annotation) -> bool:
4845 Primitive scalar types, Enum subclasses, and Union types (handled separately) are considered
4946 representable. Complex types like dict, list, bytes, and custom model classes are not.
5047 """
51- if annotation in _CLI_PRIMITIVE_TYPES :
48+ if annotation in ( str , int , float , bool ) :
5249 return True
5350 if isinstance (annotation , type ) and issubclass (annotation , Enum ):
5451 return True
@@ -146,22 +143,115 @@ def wrapper(*args, **kwargs):
146143 return wrapper
147144
148145
146+ # Methods to exclude from the CLI entirely. These may be too complex to express
147+ # as CLI commands, deprecated, or otherwise not useful from a shell context.
148+ _CLI_EXCLUDED_METHODS = {
149+ "make_action" ,
150+ "create_rule" ,
151+ "get_rule" ,
152+ "delete_rule" ,
153+ "list_rules" ,
154+ "delete_all_rules" ,
155+ "start_inspection" ,
156+ "update_inspection_metadata" ,
157+ "stop_inspection" ,
158+ }
159+
160+ # Desired display order of command groups in the CLI help output.
161+ # Groups not listed here appear after the listed ones.
162+ _GROUP_ORDER = [
163+ "Account" ,
164+ "Detectors" ,
165+ "Image Queries" ,
166+ "ML Pipelines & Priming" ,
167+ "Notes" ,
168+ "Utilities" ,
169+ ]
170+
171+ # Maps method names to their rich_help_panel group label for the CLI help output.
172+ # Applies to both stable and experimental commands. Methods not listed here fall
173+ # into the default "Commands" panel.
174+ _COMMAND_GROUPS : dict = {
175+ # Account
176+ "whoami" : "Account" ,
177+ "get_month_to_date_usage" : "Account" ,
178+ # Detectors
179+ "get_detector" : "Detectors" ,
180+ "get_detector_by_name" : "Detectors" ,
181+ "list_detectors" : "Detectors" ,
182+ "create_detector" : "Detectors" ,
183+ "get_or_create_detector" : "Detectors" ,
184+ "delete_detector" : "Detectors" ,
185+ "create_binary_detector" : "Detectors" ,
186+ "create_counting_detector" : "Detectors" ,
187+ "create_multiclass_detector" : "Detectors" ,
188+ "create_bounding_box_detector" : "Detectors" ,
189+ "create_detector_group" : "Detectors" ,
190+ "list_detector_groups" : "Detectors" ,
191+ "create_roi" : "Detectors" ,
192+ "update_detector_confidence_threshold" : "Detectors" ,
193+ "update_detector_status" : "Detectors" ,
194+ "update_detector_escalation_type" : "Detectors" ,
195+ "reset_detector" : "Detectors" ,
196+ "update_detector_name" : "Detectors" ,
197+ "create_text_recognition_detector" : "Detectors" ,
198+ "get_detector_evaluation" : "Detectors" ,
199+ "get_detector_metrics" : "Detectors" ,
200+ "download_mlbinary" : "Detectors" ,
201+ # Image Queries
202+ "get_image_query" : "Image Queries" ,
203+ "list_image_queries" : "Image Queries" ,
204+ "submit_image_query" : "Image Queries" ,
205+ "ask_confident" : "Image Queries" ,
206+ "ask_ml" : "Image Queries" ,
207+ "ask_async" : "Image Queries" ,
208+ "wait_for_confident_result" : "Image Queries" ,
209+ "wait_for_ml_result" : "Image Queries" ,
210+ "get_image" : "Image Queries" ,
211+ "add_label" : "Image Queries" ,
212+ # Notes
213+ "get_notes" : "Notes" ,
214+ "create_note" : "Notes" ,
215+ # ML Pipelines & Priming
216+ "list_detector_pipelines" : "ML Pipelines & Priming" ,
217+ "list_priming_groups" : "ML Pipelines & Priming" ,
218+ "create_priming_group" : "ML Pipelines & Priming" ,
219+ "get_priming_group" : "ML Pipelines & Priming" ,
220+ "delete_priming_group" : "ML Pipelines & Priming" ,
221+ # Utilities
222+ "edge_base_url" : "Utilities" ,
223+ "get_raw_headers" : "Utilities" ,
224+ }
225+
226+
227+ def _cli_sort_key (item : tuple ) -> tuple :
228+ """Sort key for CLI command registration that controls group and within-group ordering.
229+
230+ Commands are ordered first by their group's position in _GROUP_ORDER (ungrouped last),
231+ then alphabetically by method name within each group.
232+ """
233+ name , _ = item
234+ group = _COMMAND_GROUPS .get (name )
235+ group_rank = _GROUP_ORDER .index (group ) if group in _GROUP_ORDER else len (_GROUP_ORDER )
236+ return (group_rank , name )
237+
238+
149239def groundlight ():
150240 """Entry point for the groundlight CLI."""
151241 try :
152242 stable_names = {n for n , m in vars (Groundlight ).items () if callable (m ) and not n .startswith ("_" )}
153243
154- for name , method in vars (Groundlight ).items ():
155- if callable (method ) and not name .startswith ("_" ):
244+ for name , method in sorted ( vars (Groundlight ).items (), key = _cli_sort_key ):
245+ if callable (method ) and not name .startswith ("_" ) and name not in _CLI_EXCLUDED_METHODS :
156246 cli_func = class_func_to_cli (method )
157- cli_app .command ()(cli_func )
247+ cli_app .command (rich_help_panel = _COMMAND_GROUPS . get ( name ) )(cli_func )
158248
159- for name , method in vars (ExperimentalApi ).items ():
160- if not callable (method ) or name .startswith ("_" ) or name in stable_names :
249+ for name , method in sorted ( vars (ExperimentalApi ).items (), key = _cli_sort_key ):
250+ if not callable (method ) or name .startswith ("_" ) or name in stable_names or name in _CLI_EXCLUDED_METHODS :
161251 continue
162252 try :
163253 cli_func = class_func_to_cli (method , is_experimental = True )
164- experimental_app .command ()(cli_func )
254+ experimental_app .command (rich_help_panel = _COMMAND_GROUPS . get ( name ) )(cli_func )
165255 except Exception as e : # pylint: disable=broad-except
166256 logger .debug ("Skipping experimental CLI command '%s': %s" , name , e )
167257
0 commit comments