aiverify-moonshot 0.4.1__py3-none-any.whl → 0.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {aiverify_moonshot-0.4.1.dist-info → aiverify_moonshot-0.4.3.dist-info}/METADATA +2 -2
  2. {aiverify_moonshot-0.4.1.dist-info → aiverify_moonshot-0.4.3.dist-info}/RECORD +70 -56
  3. moonshot/__main__.py +77 -35
  4. moonshot/api.py +16 -0
  5. moonshot/integrations/cli/benchmark/benchmark.py +29 -13
  6. moonshot/integrations/cli/benchmark/cookbook.py +62 -24
  7. moonshot/integrations/cli/benchmark/datasets.py +79 -40
  8. moonshot/integrations/cli/benchmark/metrics.py +62 -23
  9. moonshot/integrations/cli/benchmark/recipe.py +89 -69
  10. moonshot/integrations/cli/benchmark/result.py +85 -47
  11. moonshot/integrations/cli/benchmark/run.py +99 -59
  12. moonshot/integrations/cli/common/common.py +20 -6
  13. moonshot/integrations/cli/common/connectors.py +154 -74
  14. moonshot/integrations/cli/common/dataset.py +66 -0
  15. moonshot/integrations/cli/common/prompt_template.py +57 -19
  16. moonshot/integrations/cli/redteam/attack_module.py +90 -24
  17. moonshot/integrations/cli/redteam/context_strategy.py +83 -23
  18. moonshot/integrations/cli/redteam/prompt_template.py +1 -1
  19. moonshot/integrations/cli/redteam/redteam.py +52 -6
  20. moonshot/integrations/cli/redteam/session.py +565 -44
  21. moonshot/integrations/cli/utils/process_data.py +52 -0
  22. moonshot/integrations/web_api/__main__.py +2 -0
  23. moonshot/integrations/web_api/app.py +6 -6
  24. moonshot/integrations/web_api/container.py +12 -2
  25. moonshot/integrations/web_api/routes/bookmark.py +173 -0
  26. moonshot/integrations/web_api/routes/dataset.py +46 -1
  27. moonshot/integrations/web_api/schemas/bookmark_create_dto.py +13 -0
  28. moonshot/integrations/web_api/schemas/dataset_create_dto.py +18 -0
  29. moonshot/integrations/web_api/schemas/recipe_create_dto.py +0 -2
  30. moonshot/integrations/web_api/services/bookmark_service.py +94 -0
  31. moonshot/integrations/web_api/services/dataset_service.py +25 -0
  32. moonshot/integrations/web_api/services/recipe_service.py +0 -1
  33. moonshot/integrations/web_api/services/utils/file_manager.py +52 -0
  34. moonshot/integrations/web_api/status_updater/moonshot_ui_webhook.py +0 -1
  35. moonshot/integrations/web_api/temp/.gitkeep +0 -0
  36. moonshot/src/api/api_bookmark.py +95 -0
  37. moonshot/src/api/api_connector_endpoint.py +1 -1
  38. moonshot/src/api/api_context_strategy.py +2 -2
  39. moonshot/src/api/api_dataset.py +35 -0
  40. moonshot/src/api/api_recipe.py +0 -3
  41. moonshot/src/api/api_session.py +1 -1
  42. moonshot/src/bookmark/bookmark.py +257 -0
  43. moonshot/src/bookmark/bookmark_arguments.py +38 -0
  44. moonshot/src/configs/env_variables.py +12 -2
  45. moonshot/src/connectors/connector.py +15 -7
  46. moonshot/src/connectors_endpoints/connector_endpoint.py +65 -49
  47. moonshot/src/cookbooks/cookbook.py +57 -37
  48. moonshot/src/datasets/dataset.py +125 -5
  49. moonshot/src/metrics/metric.py +8 -4
  50. moonshot/src/metrics/metric_interface.py +8 -2
  51. moonshot/src/prompt_templates/prompt_template.py +5 -1
  52. moonshot/src/recipes/recipe.py +38 -40
  53. moonshot/src/recipes/recipe_arguments.py +0 -4
  54. moonshot/src/redteaming/attack/attack_module.py +18 -8
  55. moonshot/src/redteaming/attack/context_strategy.py +6 -2
  56. moonshot/src/redteaming/session/session.py +15 -11
  57. moonshot/src/results/result.py +7 -3
  58. moonshot/src/runners/runner.py +65 -42
  59. moonshot/src/runs/run.py +15 -11
  60. moonshot/src/runs/run_progress.py +7 -3
  61. moonshot/src/storage/db_interface.py +14 -0
  62. moonshot/src/storage/storage.py +33 -2
  63. moonshot/src/utils/find_feature.py +45 -0
  64. moonshot/src/utils/log.py +72 -0
  65. moonshot/src/utils/pagination.py +25 -0
  66. moonshot/src/utils/timeit.py +8 -1
  67. {aiverify_moonshot-0.4.1.dist-info → aiverify_moonshot-0.4.3.dist-info}/WHEEL +0 -0
  68. {aiverify_moonshot-0.4.1.dist-info → aiverify_moonshot-0.4.3.dist-info}/licenses/AUTHORS.md +0 -0
  69. {aiverify_moonshot-0.4.1.dist-info → aiverify_moonshot-0.4.3.dist-info}/licenses/LICENSE.md +0 -0
  70. {aiverify_moonshot-0.4.1.dist-info → aiverify_moonshot-0.4.3.dist-info}/licenses/NOTICES.md +0 -0
@@ -18,6 +18,7 @@ from moonshot.api import (
18
18
  api_update_recipe,
19
19
  )
20
20
  from moonshot.integrations.cli.common.display_helper import display_view_list_format
21
+ from moonshot.integrations.cli.utils.process_data import filter_data
21
22
 
22
23
  console = Console()
23
24
 
@@ -36,8 +37,7 @@ def add_recipe(args) -> None:
36
37
 
37
38
  Args:
38
39
  args (argparse.Namespace): The arguments provided to the command line interface.
39
- Expected keys are name, description, tags, categories, dataset, prompt_templates, metrics, attack_modules,
40
- and grading_scale.
40
+ Expected keys are name, description, tags, categories, dataset, prompt_templates, metrics and grading_scale.
41
41
 
42
42
  Returns:
43
43
  None
@@ -53,9 +53,6 @@ def add_recipe(args) -> None:
53
53
  literal_eval(args.prompt_templates) if args.prompt_templates else []
54
54
  )
55
55
  metrics = literal_eval(args.metrics)
56
- attack_modules = (
57
- literal_eval(args.attack_modules) if args.attack_modules else []
58
- )
59
56
  grading_scale = literal_eval(args.grading_scale) if args.grading_scale else {}
60
57
 
61
58
  new_recipe_id = api_create_recipe(
@@ -66,7 +63,6 @@ def add_recipe(args) -> None:
66
63
  datasets,
67
64
  prompt_templates,
68
65
  metrics,
69
- attack_modules,
70
66
  grading_scale,
71
67
  )
72
68
  print(f"[add_recipe]: Recipe ({new_recipe_id}) created.")
@@ -74,20 +70,37 @@ def add_recipe(args) -> None:
74
70
  print(f"[add_recipe]: {str(e)}")
75
71
 
76
72
 
77
- def list_recipes() -> None:
73
+ def list_recipes(args) -> list | None:
78
74
  """
79
75
  List all available recipes.
80
76
 
81
77
  This function retrieves all available recipes by calling the api_get_all_recipe function from the
82
78
  moonshot.api module.
83
- It then displays the retrieved recipes using the display_recipes function.
79
+ It then displays the retrieved recipes using the _display_recipes function.
80
+
81
+ Args:
82
+ args: A namespace object from argparse. It should have an optional attribute:
83
+ find (str): Optional field to find recipe(s) with a keyword.
84
+ pagination (str): Optional field to paginate recipes.
84
85
 
85
86
  Returns:
86
- None
87
+ list | None: A list of Recipe or None if there is no result.
87
88
  """
89
+
88
90
  try:
89
91
  recipes_list = api_get_all_recipe()
90
- display_recipes(recipes_list)
92
+ keyword = args.find.lower() if args.find else ""
93
+ pagination = literal_eval(args.pagination) if args.pagination else ()
94
+
95
+ if recipes_list:
96
+ filtered_recipes_list = filter_data(recipes_list, keyword, pagination)
97
+ if filtered_recipes_list:
98
+ _display_recipes(filtered_recipes_list)
99
+ return filtered_recipes_list
100
+
101
+ console.print("[red]There are no recipes found.[/red]")
102
+ return None
103
+
91
104
  except Exception as e:
92
105
  print(f"[list_recipes]: {str(e)}")
93
106
 
@@ -109,7 +122,7 @@ def view_recipe(args) -> None:
109
122
  """
110
123
  try:
111
124
  recipe_info = api_read_recipe(args.recipe)
112
- display_recipes([recipe_info])
125
+ _display_recipes([recipe_info])
113
126
  except Exception as e:
114
127
  print(f"[view_recipe]: {str(e)}")
115
128
 
@@ -295,7 +308,7 @@ def display_view_statistics_format(title: str, stats: dict) -> str:
295
308
  return f"[blue]{title}[/blue]: nil"
296
309
 
297
310
 
298
- def display_recipes(recipes_list: list) -> None:
311
+ def _display_recipes(recipes_list: list) -> None:
299
312
  """
300
313
  Display the list of recipes in a tabular format.
301
314
 
@@ -307,54 +320,48 @@ def display_recipes(recipes_list: list) -> None:
307
320
  Args:
308
321
  recipes_list (list): A list of dictionaries, where each dictionary contains the details of a recipe.
309
322
  """
310
- if recipes_list:
311
- table = Table(
312
- title="List of Recipes", show_lines=True, expand=True, header_style="bold"
323
+ table = Table(
324
+ title="List of Recipes", show_lines=True, expand=True, header_style="bold"
325
+ )
326
+ table.add_column("No.", width=2)
327
+ table.add_column("Recipe", justify="left", width=78)
328
+ table.add_column("Contains", justify="left", width=20, overflow="fold")
329
+ for idx, recipe in enumerate(recipes_list, 1):
330
+ (
331
+ id,
332
+ name,
333
+ description,
334
+ tags,
335
+ categories,
336
+ datasets,
337
+ prompt_templates,
338
+ metrics,
339
+ grading_scale,
340
+ stats,
341
+ *other_args,
342
+ ) = recipe.values()
343
+ idx = recipe.get("idx", idx)
344
+ tags_info = display_view_list_format("Tags", tags)
345
+ categories_info = display_view_list_format("Categories", categories)
346
+ datasets_info = display_view_list_format("Datasets", datasets)
347
+ prompt_templates_info = display_view_list_format(
348
+ "Prompt Templates", prompt_templates
313
349
  )
314
- table.add_column("No.", width=2)
315
- table.add_column("Recipe", justify="left", width=78)
316
- table.add_column("Contains", justify="left", width=20, overflow="fold")
317
- for recipe_id, recipe in enumerate(recipes_list, 1):
318
- (
319
- id,
320
- name,
321
- description,
322
- tags,
323
- categories,
324
- datasets,
325
- prompt_templates,
326
- metrics,
327
- attack_strategies,
328
- grading_scale,
329
- stats,
330
- ) = recipe.values()
331
-
332
- tags_info = display_view_list_format("Tags", tags)
333
- categories_info = display_view_list_format("Categories", categories)
334
- datasets_info = display_view_list_format("Datasets", datasets)
335
- prompt_templates_info = display_view_list_format(
336
- "Prompt Templates", prompt_templates
337
- )
338
- metrics_info = display_view_list_format("Metrics", metrics)
339
- attack_strategies_info = display_view_list_format(
340
- "Attack Strategies", attack_strategies
341
- )
342
- grading_scale_info = display_view_grading_scale_format(
343
- "Grading Scale", grading_scale
344
- )
345
- stats_info = display_view_statistics_format("Statistics", stats)
350
+ metrics_info = display_view_list_format("Metrics", metrics)
351
+ grading_scale_info = display_view_grading_scale_format(
352
+ "Grading Scale", grading_scale
353
+ )
354
+ stats_info = display_view_statistics_format("Statistics", stats)
346
355
 
347
- recipe_info = (
348
- f"[red]id: {id}[/red]\n\n[blue]{name}[/blue]\n{description}\n\n"
349
- f"{tags_info}\n\n{categories_info}\n\n{grading_scale_info}\n\n{stats_info}"
350
- )
351
- contains_info = f"{datasets_info}\n\n{prompt_templates_info}\n\n{metrics_info}\n\n{attack_strategies_info}"
356
+ recipe_info = (
357
+ f"[red]id: {id}[/red]\n\n[blue]{name}[/blue]\n{description}\n\n"
358
+ f"{tags_info}\n\n{categories_info}\n\n{grading_scale_info}\n\n{stats_info}"
359
+ )
360
+ contains_info = f"{datasets_info}\n\n{prompt_templates_info}\n\n{metrics_info}"
352
361
 
353
- table.add_section()
354
- table.add_row(str(recipe_id), recipe_info, contains_info)
355
- console.print(table)
356
- else:
357
- console.print("[red]There are no recipes found.[/red]")
362
+ table.add_section()
363
+ table.add_row(str(idx), recipe_info, contains_info)
364
+ console.print(table)
358
365
 
359
366
 
360
367
  def show_recipe_results(recipes, endpoints, recipe_results, duration):
@@ -382,9 +389,9 @@ def show_recipe_results(recipes, endpoints, recipe_results, duration):
382
389
  console.print("[red]There are no results.[/red]")
383
390
 
384
391
  # Print run stats
385
- console.print(
386
- f"{'='*50}\n[blue]Time taken to run: {duration}s[/blue]\n*Overall rating will be the lowest grade that the recipes have in each cookbook\n{'='*50}"
387
- )
392
+ run_stats = f"""{'='*50}\n[blue]Time taken to run: {duration}s[/blue]\n*Overall rating will be the lowest grade
393
+ that the recipes have in each cookbook\n{'='*50}"""
394
+ console.print(run_stats)
388
395
 
389
396
 
390
397
  def generate_recipe_table(recipes: list, endpoints: list, results: dict) -> None:
@@ -483,7 +490,6 @@ add_recipe_args = cmd2.Cmd2ArgumentParser(
483
490
  "\"['bertscore','bleuscore']\" "
484
491
  "-p \"['analogical-similarity','mmlu']\" "
485
492
  "-t \"['tag1','tag2']\" "
486
- "-a \"['charswap_attack']\" "
487
493
  "-g \"{'A':[80,100],'B':[60,79],'C':[40,59],'D':[20,39],'E':[0,19]}\" ",
488
494
  )
489
495
  add_recipe_args.add_argument("name", type=str, help="Name of the new recipe")
@@ -511,13 +517,6 @@ add_recipe_args.add_argument(
511
517
  add_recipe_args.add_argument(
512
518
  "metrics", type=str, help="List of metrics to be included in the new recipe"
513
519
  )
514
- add_recipe_args.add_argument(
515
- "-a",
516
- "--attack_modules",
517
- type=str,
518
- help="List of attack modules to be included in the new recipe",
519
- nargs="?",
520
- )
521
520
  add_recipe_args.add_argument(
522
521
  "-g",
523
522
  "--grading_scale",
@@ -537,7 +536,6 @@ update_recipe_args = cmd2.Cmd2ArgumentParser(
537
536
  " datasets: A list of datasets used in the recipe. \n"
538
537
  " prompt_templates: A list of prompt templates for the recipe. \n"
539
538
  " metrics: A list of metrics to evaluate the recipe. \n"
540
- " attack_modules: A list of attack modules used in the recipe.\n"
541
539
  " grading_scale: A list of grading scale used in the recipe. \n\n"
542
540
  "Example command:\n"
543
541
  " update_recipe my-new-recipe \"[('name', 'My Updated Recipe'), ('tags', ['fairness', 'bbq'])]\" ",
@@ -596,3 +594,25 @@ run_recipe_args.add_argument(
596
594
  default="benchmarking-result",
597
595
  help="Result processing module to use",
598
596
  )
597
+
598
+ # List recipe arguments
599
+ list_recipes_args = cmd2.Cmd2ArgumentParser(
600
+ description="List all recipes.",
601
+ epilog='Example:\n list_recipes -f "mmlu"',
602
+ )
603
+
604
+ list_recipes_args.add_argument(
605
+ "-f",
606
+ "--find",
607
+ type=str,
608
+ help="Optional field to find recipe(s) with keyword",
609
+ nargs="?",
610
+ )
611
+
612
+ list_recipes_args.add_argument(
613
+ "-p",
614
+ "--pagination",
615
+ type=str,
616
+ help="Optional tuple to paginate recipes(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
617
+ nargs="?",
618
+ )
@@ -1,3 +1,5 @@
1
+ from ast import literal_eval
2
+
1
3
  import cmd2
2
4
  from rich.console import Console
3
5
  from rich.table import Table
@@ -9,6 +11,7 @@ from moonshot.integrations.cli.common.display_helper import (
9
11
  display_view_list_format,
10
12
  display_view_str_format,
11
13
  )
14
+ from moonshot.integrations.cli.utils.process_data import filter_data
12
15
 
13
16
  console = Console()
14
17
 
@@ -16,7 +19,7 @@ console = Console()
16
19
  # ------------------------------------------------------------------------------
17
20
  # CLI Functions
18
21
  # ------------------------------------------------------------------------------
19
- def list_results() -> None:
22
+ def list_results(args) -> list | None:
20
23
  """
21
24
  List all available results.
22
25
 
@@ -24,12 +27,29 @@ def list_results() -> None:
24
27
  moonshot.api module. It then creates a table with the result id and name. If there are no results, it prints a
25
28
  message indicating that no results were found.
26
29
 
30
+ Args:
31
+ args: A namespace object from argparse. It should have an optional attribute:
32
+ find (str): Optional field to find result(s) with a keyword.
33
+ pagination (str): Optional field to paginate results.
34
+
27
35
  Returns:
28
- None
36
+ list | None: A list of Result or None if there is no result.
29
37
  """
38
+
30
39
  try:
31
40
  results_list = api_get_all_result()
32
- display_results(results_list)
41
+ keyword = args.find.lower() if args.find else ""
42
+ pagination = literal_eval(args.pagination) if args.pagination else ()
43
+
44
+ if results_list:
45
+ filtered_results_list = filter_data(results_list, keyword, pagination)
46
+ if filtered_results_list:
47
+ _display_results(filtered_results_list)
48
+ return filtered_results_list
49
+
50
+ console.print("[red]There are no results found.[/red]")
51
+ return None
52
+
33
53
  except Exception as e:
34
54
  print(f"[list_results]: {str(e)}")
35
55
 
@@ -95,7 +115,7 @@ def delete_result(args) -> None:
95
115
  # ------------------------------------------------------------------------------
96
116
  # Helper functions: Display on cli
97
117
  # ------------------------------------------------------------------------------
98
- def display_results(results_list):
118
+ def _display_results(results_list):
99
119
  """
100
120
  Display a list of results.
101
121
 
@@ -108,50 +128,46 @@ def display_results(results_list):
108
128
  Returns:
109
129
  None
110
130
  """
111
- if results_list:
112
- table = Table(
113
- title="List of Results", show_lines=True, expand=True, header_style="bold"
131
+ table = Table(
132
+ title="List of Results", show_lines=True, expand=True, header_style="bold"
133
+ )
134
+ table.add_column("No.", width=2)
135
+ table.add_column("Result", justify="left", width=78)
136
+ table.add_column("Contains", justify="left", width=20, overflow="fold")
137
+ for idx, result in enumerate(results_list, 1):
138
+ metadata, results, *other_args = result.values()
139
+
140
+ id = metadata["id"]
141
+ start_time = metadata["start_time"]
142
+ end_time = metadata["end_time"]
143
+ duration = metadata["duration"]
144
+ status = metadata["status"]
145
+ recipes = metadata["recipes"]
146
+ cookbooks = metadata["cookbooks"]
147
+ endpoints = metadata["endpoints"]
148
+ num_of_prompts = metadata["num_of_prompts"]
149
+ random_seed = metadata["random_seed"]
150
+ system_prompt = metadata["system_prompt"]
151
+ idx = result.get("idx", idx)
152
+
153
+ duration_info = f"[blue]Period:[/blue] {start_time} - {end_time} ({duration}s)"
154
+ status_info = display_view_str_format("Status", status)
155
+ recipes_info = display_view_list_format("Recipes", recipes)
156
+ cookbooks_info = display_view_list_format("Cookbooks", cookbooks)
157
+ endpoints_info = display_view_list_format("Endpoints", endpoints)
158
+ prompts_info = display_view_str_format("Number of Prompts", num_of_prompts)
159
+ seed_info = display_view_str_format("Seed", random_seed)
160
+ system_prompt_info = display_view_str_format("System Prompt", system_prompt)
161
+
162
+ result_info = f"[red]id: {id}[/red]\n\n{duration_info}\n\n{status_info}"
163
+ contains_info = (
164
+ f"{recipes_info}\n\n{cookbooks_info}\n\n{endpoints_info}\n\n{prompts_info}"
165
+ f"\n\n{seed_info}\n\n{system_prompt_info}"
114
166
  )
115
- table.add_column("No.", width=2)
116
- table.add_column("Result", justify="left", width=78)
117
- table.add_column("Contains", justify="left", width=20, overflow="fold")
118
- for result_id, result in enumerate(results_list, 1):
119
- metadata, results = result.values()
120
-
121
- id = metadata["id"]
122
- start_time = metadata["start_time"]
123
- end_time = metadata["end_time"]
124
- duration = metadata["duration"]
125
- status = metadata["status"]
126
- recipes = metadata["recipes"]
127
- cookbooks = metadata["cookbooks"]
128
- endpoints = metadata["endpoints"]
129
- num_of_prompts = metadata["num_of_prompts"]
130
- random_seed = metadata["random_seed"]
131
- system_prompt = metadata["system_prompt"]
132
-
133
- duration_info = (
134
- f"[blue]Period:[/blue] {start_time} - {end_time} ({duration}s)"
135
- )
136
- status_info = display_view_str_format("Status", status)
137
- recipes_info = display_view_list_format("Recipes", recipes)
138
- cookbooks_info = display_view_list_format("Cookbooks", cookbooks)
139
- endpoints_info = display_view_list_format("Endpoints", endpoints)
140
- prompts_info = display_view_str_format("Number of Prompts", num_of_prompts)
141
- seed_info = display_view_str_format("Seed", random_seed)
142
- system_prompt_info = display_view_str_format("System Prompt", system_prompt)
143
-
144
- result_info = f"[red]id: {id}[/red]\n\n{duration_info}\n\n{status_info}"
145
- contains_info = (
146
- f"{recipes_info}\n\n{cookbooks_info}\n\n{endpoints_info}\n\n{prompts_info}"
147
- f"\n\n{seed_info}\n\n{system_prompt_info}"
148
- )
149
-
150
- table.add_section()
151
- table.add_row(str(result_id), result_info, contains_info)
152
- console.print(table)
153
- else:
154
- console.print("[red]There are no results found.[/red]")
167
+
168
+ table.add_section()
169
+ table.add_row(str(idx), result_info, contains_info)
170
+ console.print(table)
155
171
 
156
172
 
157
173
  def display_view_recipe_result(result_info):
@@ -214,3 +230,25 @@ delete_result_args = cmd2.Cmd2ArgumentParser(
214
230
  epilog="Example:\n delete_result my-new-cookbook-runner",
215
231
  )
216
232
  delete_result_args.add_argument("result", type=str, help="Name of the result")
233
+
234
+ # List result arguments
235
+ list_results_args = cmd2.Cmd2ArgumentParser(
236
+ description="List all results.",
237
+ epilog='Example:\n list_results -f "my-runner"',
238
+ )
239
+
240
+ list_results_args.add_argument(
241
+ "-f",
242
+ "--find",
243
+ type=str,
244
+ help="Optional field to find result(s) with keyword",
245
+ nargs="?",
246
+ )
247
+
248
+ list_results_args.add_argument(
249
+ "-p",
250
+ "--pagination",
251
+ type=str,
252
+ help="Optional tuple to paginate result(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
253
+ nargs="?",
254
+ )
@@ -1,3 +1,5 @@
1
+ from ast import literal_eval
2
+
1
3
  import cmd2
2
4
  from rich.console import Console
3
5
  from rich.table import Table
@@ -7,6 +9,7 @@ from moonshot.integrations.cli.common.display_helper import (
7
9
  display_view_list_format,
8
10
  display_view_str_format,
9
11
  )
12
+ from moonshot.integrations.cli.utils.process_data import filter_data
10
13
 
11
14
  console = Console()
12
15
 
@@ -14,21 +17,38 @@ console = Console()
14
17
  # ------------------------------------------------------------------------------
15
18
  # CLI Functions
16
19
  # ------------------------------------------------------------------------------
17
- def list_runs() -> None:
20
+ def list_runs(args) -> list | None:
18
21
  """
19
22
  List all runs.
20
23
 
21
24
  This function retrieves all available runs by calling the api_get_all_run function from the
22
- moonshot.api module. It then calls the display_runs function to present the retrieved run information
25
+ moonshot.api module. It then calls the _display_runs function to present the retrieved run information
23
26
  in a user-friendly format on the command line interface. If an exception occurs during the retrieval
24
27
  or display process, it prints an error message.
25
28
 
29
+ Args:
30
+ args: A namespace object from argparse. It should have an optional attribute:
31
+ find (str): Optional field to find run(s) with a keyword.
32
+ pagination (str): Optional field to paginate runs.
33
+
26
34
  Returns:
27
- None
35
+ list | None: A list of Run or None if there is no result.
28
36
  """
37
+
29
38
  try:
30
39
  runner_run_info = api_get_all_run()
31
- display_runs(runner_run_info)
40
+ keyword = args.find.lower() if args.find else ""
41
+ pagination = literal_eval(args.pagination) if args.pagination else ()
42
+
43
+ if runner_run_info:
44
+ filtered_runs_list = filter_data(runner_run_info, keyword, pagination)
45
+ if filtered_runs_list:
46
+ _display_runs(filtered_runs_list)
47
+ return filtered_runs_list
48
+
49
+ console.print("[red]There are no runs found.[/red]")
50
+ return None
51
+
32
52
  except Exception as e:
33
53
  print(f"[list_runs]: {str(e)}")
34
54
 
@@ -38,7 +58,7 @@ def view_run(args) -> None:
38
58
  View the details of a specific run.
39
59
 
40
60
  This function retrieves and displays information about a specific run associated with a runner. It uses the runner
41
- identifier provided in the arguments to fetch the data and then calls the display_runs function to present it in a
61
+ identifier provided in the arguments to fetch the data and then calls the _display_runs function to present it in a
42
62
  user-friendly format.
43
63
 
44
64
  Args:
@@ -50,7 +70,7 @@ def view_run(args) -> None:
50
70
  """
51
71
  try:
52
72
  runner_run_info = api_get_all_run(args.runner_id)
53
- display_runs(runner_run_info)
73
+ _display_runs(runner_run_info)
54
74
  except Exception as e:
55
75
  print(f"[view_run]: {str(e)}")
56
76
 
@@ -58,7 +78,7 @@ def view_run(args) -> None:
58
78
  # ------------------------------------------------------------------------------
59
79
  # Helper functions: Display on cli
60
80
  # ------------------------------------------------------------------------------
61
- def display_runs(runs_list: list):
81
+ def _display_runs(runs_list: list):
62
82
  """
63
83
  Display a list of runs in a table format.
64
84
 
@@ -74,59 +94,56 @@ def display_runs(runs_list: list):
74
94
  Returns:
75
95
  None
76
96
  """
77
- if runs_list:
78
- table = Table(
79
- title="List of Runs", show_lines=True, expand=True, header_style="bold"
97
+ table = Table(
98
+ title="List of Runs", show_lines=True, expand=True, header_style="bold"
99
+ )
100
+ table.add_column("No.", width=2)
101
+ table.add_column("Run", justify="left", width=78)
102
+ table.add_column("Contains", justify="left", width=20, overflow="fold")
103
+ for idx, run in enumerate(runs_list, 1):
104
+ (
105
+ run_id,
106
+ runner_id,
107
+ runner_type,
108
+ runner_args,
109
+ endpoints,
110
+ results_file,
111
+ start_time,
112
+ end_time,
113
+ duration,
114
+ error_messages,
115
+ raw_results,
116
+ results,
117
+ status,
118
+ *other_args,
119
+ ) = run.values()
120
+
121
+ duration_info = f"[blue]Period:[/blue] {start_time} - {end_time} ({duration}s)"
122
+ run_id = display_view_str_format("Run ID", run_id)
123
+ runner_id = display_view_str_format("Runner ID", runner_id)
124
+ runner_type = display_view_str_format("Runner Type", runner_type)
125
+ runner_args = display_view_str_format("Runner Args", runner_args)
126
+ status_info = display_view_str_format("Status", status)
127
+ results_info = display_view_str_format("Results File", results_file)
128
+ endpoints_info = display_view_list_format("Endpoints", endpoints)
129
+ error_messages_info = display_view_list_format("Error Messages", error_messages)
130
+
131
+ has_raw_results = bool(raw_results)
132
+ has_results = bool(results)
133
+ idx = run.get("idx", idx)
134
+
135
+ result_info = (
136
+ f"[red]{runner_id}[/red]\n\n{run_id}\n\n{duration_info}\n\n{status_info}"
137
+ )
138
+ contains_info = (
139
+ f"{results_info}\n\n{error_messages_info}\n\n{endpoints_info}\n\n"
140
+ f"[blue]Has Raw Results: {has_raw_results}[/blue]\n\n"
141
+ f"[blue]Has Results: {has_results}[/blue]"
80
142
  )
81
- table.add_column("No.", width=2)
82
- table.add_column("Run", justify="left", width=78)
83
- table.add_column("Contains", justify="left", width=20, overflow="fold")
84
- for run_number, run in enumerate(runs_list, 1):
85
- (
86
- run_id,
87
- runner_id,
88
- runner_type,
89
- runner_args,
90
- endpoints,
91
- results_file,
92
- start_time,
93
- end_time,
94
- duration,
95
- error_messages,
96
- raw_results,
97
- results,
98
- status,
99
- ) = run.values()
100
-
101
- duration_info = (
102
- f"[blue]Period:[/blue] {start_time} - {end_time} ({duration}s)"
103
- )
104
- run_id = display_view_str_format("Run ID", run_id)
105
- runner_id = display_view_str_format("Runner ID", runner_id)
106
- runner_type = display_view_str_format("Runner Type", runner_type)
107
- runner_args = display_view_str_format("Runner Args", runner_args)
108
- status_info = display_view_str_format("Status", status)
109
- results_info = display_view_str_format("Results File", results_file)
110
- endpoints_info = display_view_list_format("Endpoints", endpoints)
111
- error_messages_info = display_view_list_format(
112
- "Error Messages", error_messages
113
- )
114
-
115
- has_raw_results = bool(raw_results)
116
- has_results = bool(results)
117
-
118
- result_info = f"[red]{runner_id}[/red]\n\n{run_id}\n\n{duration_info}\n\n{status_info}"
119
- contains_info = (
120
- f"{results_info}\n\n{error_messages_info}\n\n{endpoints_info}\n\n"
121
- f"[blue]Has Raw Results: {has_raw_results}[/blue]\n\n"
122
- f"[blue]Has Results: {has_results}[/blue]"
123
- )
124
-
125
- table.add_section()
126
- table.add_row(str(run_number), result_info, contains_info)
127
- console.print(table)
128
- else:
129
- console.print("[red]There are no results found.[/red]")
143
+
144
+ table.add_section()
145
+ table.add_row(str(idx), result_info, contains_info)
146
+ console.print(table)
130
147
 
131
148
 
132
149
  # ------------------------------------------------------------------------------
@@ -138,3 +155,26 @@ view_run_args = cmd2.Cmd2ArgumentParser(
138
155
  epilog="Example:\n view_run my-new-cookbook-runner",
139
156
  )
140
157
  view_run_args.add_argument("runner_id", type=str, help="Name of the runner")
158
+
159
+
160
+ # List run arguments
161
+ list_runs_args = cmd2.Cmd2ArgumentParser(
162
+ description="List all runs.",
163
+ epilog='Example:\n list_runs -f "my-run"',
164
+ )
165
+
166
+ list_runs_args.add_argument(
167
+ "-f",
168
+ "--find",
169
+ type=str,
170
+ help="Optional field to find run(s) with keyword",
171
+ nargs="?",
172
+ )
173
+
174
+ list_runs_args.add_argument(
175
+ "-p",
176
+ "--pagination",
177
+ type=str,
178
+ help="Optional tuple to paginate run(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
179
+ nargs="?",
180
+ )