aiverify-moonshot 0.4.2__py3-none-any.whl → 0.4.4__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 (35) hide show
  1. {aiverify_moonshot-0.4.2.dist-info → aiverify_moonshot-0.4.4.dist-info}/METADATA +12 -10
  2. {aiverify_moonshot-0.4.2.dist-info → aiverify_moonshot-0.4.4.dist-info}/RECORD +35 -31
  3. moonshot/api.py +2 -0
  4. moonshot/integrations/cli/benchmark/cookbook.py +36 -28
  5. moonshot/integrations/cli/benchmark/datasets.py +56 -47
  6. moonshot/integrations/cli/benchmark/metrics.py +39 -30
  7. moonshot/integrations/cli/benchmark/recipe.py +63 -73
  8. moonshot/integrations/cli/benchmark/result.py +62 -54
  9. moonshot/integrations/cli/benchmark/run.py +75 -66
  10. moonshot/integrations/cli/common/common.py +8 -0
  11. moonshot/integrations/cli/common/connectors.py +101 -85
  12. moonshot/integrations/cli/common/dataset.py +66 -0
  13. moonshot/integrations/cli/common/prompt_template.py +30 -27
  14. moonshot/integrations/cli/redteam/attack_module.py +45 -30
  15. moonshot/integrations/cli/redteam/context_strategy.py +36 -30
  16. moonshot/integrations/cli/redteam/session.py +101 -76
  17. moonshot/integrations/cli/utils/process_data.py +52 -0
  18. moonshot/integrations/web_api/app.py +1 -1
  19. moonshot/integrations/web_api/routes/dataset.py +46 -1
  20. moonshot/integrations/web_api/schemas/dataset_create_dto.py +18 -0
  21. moonshot/integrations/web_api/schemas/recipe_create_dto.py +0 -2
  22. moonshot/integrations/web_api/services/bookmark_service.py +6 -2
  23. moonshot/integrations/web_api/services/dataset_service.py +25 -0
  24. moonshot/integrations/web_api/services/recipe_service.py +0 -1
  25. moonshot/src/api/api_dataset.py +35 -0
  26. moonshot/src/api/api_recipe.py +0 -3
  27. moonshot/src/datasets/dataset.py +116 -0
  28. moonshot/src/recipes/recipe.py +0 -15
  29. moonshot/src/recipes/recipe_arguments.py +0 -4
  30. moonshot/src/utils/log.py +12 -6
  31. moonshot/src/utils/pagination.py +25 -0
  32. {aiverify_moonshot-0.4.2.dist-info → aiverify_moonshot-0.4.4.dist-info}/WHEEL +0 -0
  33. {aiverify_moonshot-0.4.2.dist-info → aiverify_moonshot-0.4.4.dist-info}/licenses/AUTHORS.md +0 -0
  34. {aiverify_moonshot-0.4.2.dist-info → aiverify_moonshot-0.4.4.dist-info}/licenses/LICENSE.md +0 -0
  35. {aiverify_moonshot-0.4.2.dist-info → aiverify_moonshot-0.4.4.dist-info}/licenses/NOTICES.md +0 -0
@@ -13,7 +13,7 @@ from moonshot.api import (
13
13
  api_read_endpoint,
14
14
  api_update_endpoint,
15
15
  )
16
- from moonshot.src.utils.find_feature import find_keyword
16
+ from moonshot.integrations.cli.utils.process_data import filter_data
17
17
 
18
18
  console = Console()
19
19
 
@@ -63,11 +63,12 @@ def list_endpoints(args) -> list | None:
63
63
  List all endpoints.
64
64
 
65
65
  This function retrieves all endpoints by calling the api_get_all_endpoint function from the
66
- moonshot.api module. It then displays the endpoints using the display_endpoints function.
66
+ moonshot.api module. It then displays the endpoints using the _display_endpoints function.
67
67
 
68
68
  Args:
69
69
  args: A namespace object from argparse. It should have an optional attribute:
70
70
  find (str): Optional field to find endpoint(s) with a keyword.
71
+ pagination (str): Optional field to paginate endpoints.
71
72
 
72
73
  Returns:
73
74
  list | None: A list of ConnectorEndpoint or None if there is no result.
@@ -75,17 +76,17 @@ def list_endpoints(args) -> list | None:
75
76
  try:
76
77
  endpoints_list = api_get_all_endpoint()
77
78
  keyword = args.find.lower() if args.find else ""
78
- if keyword:
79
- filtered_endpoints_list = find_keyword(keyword, endpoints_list)
79
+ pagination = literal_eval(args.pagination) if args.pagination else ()
80
+
81
+ if endpoints_list:
82
+ filtered_endpoints_list = filter_data(endpoints_list, keyword, pagination)
80
83
  if filtered_endpoints_list:
81
- display_endpoints(filtered_endpoints_list)
84
+ _display_endpoints(filtered_endpoints_list)
82
85
  return filtered_endpoints_list
83
- else:
84
- print("No endpoints containing keyword found.")
85
- return None
86
- else:
87
- display_endpoints(endpoints_list)
88
- return endpoints_list
86
+
87
+ console.print("[red]There are no endpoints found.[/red]")
88
+ return None
89
+
89
90
  except Exception as e:
90
91
  print(f"[list_endpoints]: {str(e)}")
91
92
 
@@ -95,11 +96,12 @@ def list_connector_types(args) -> list | None:
95
96
  List all connector types.
96
97
 
97
98
  This function retrieves all connector types by calling the api_get_all_connector_type function from the
98
- moonshot.api module. It then displays the connector types using the display_connector_types function.
99
+ moonshot.api module. It then displays the connector types using the _display_connector_types function.
99
100
 
100
101
  Args:
101
102
  args: A namespace object from argparse. It should have an optional attribute:
102
103
  find (str): Optional field to find connector type(s) with a keyword.
104
+ pagination (str): Optional field to paginate connector types.
103
105
 
104
106
  Returns:
105
107
  list | None: A list of Connector or None if there is no result.
@@ -107,17 +109,19 @@ def list_connector_types(args) -> list | None:
107
109
  try:
108
110
  connector_type_list = api_get_all_connector_type()
109
111
  keyword = args.find.lower() if args.find else ""
110
- if keyword:
111
- filtered_connector_type_list = find_keyword(keyword, connector_type_list)
112
+ pagination = literal_eval(args.pagination) if args.pagination else ()
113
+
114
+ if connector_type_list:
115
+ filtered_connector_type_list = filter_data(
116
+ connector_type_list, keyword, pagination
117
+ )
112
118
  if filtered_connector_type_list:
113
- display_connector_types(filtered_connector_type_list)
119
+ _display_connector_types(filtered_connector_type_list)
114
120
  return filtered_connector_type_list
115
- else:
116
- print("No connectors containing keyword found.")
117
- return None
118
- else:
119
- display_connector_types(connector_type_list)
120
- return connector_type_list
121
+
122
+ console.print("[red]There are no connector types found.[/red]")
123
+ return None
124
+
121
125
  except Exception as e:
122
126
  print(f"[list_connector_types]: {str(e)}")
123
127
 
@@ -128,7 +132,7 @@ def view_endpoint(args) -> None:
128
132
 
129
133
  This function retrieves a specific endpoint by calling the api_read_endpoint function from the
130
134
  moonshot.api module using the endpoint name provided in the args. It then displays the endpoint
131
- information using the display_endpoints function.
135
+ information using the _display_endpoints function.
132
136
 
133
137
  Args:
134
138
  args: A namespace object from argparse. It should have the following attribute:
@@ -139,7 +143,7 @@ def view_endpoint(args) -> None:
139
143
  """
140
144
  try:
141
145
  endpoint_info = api_read_endpoint(args.endpoint)
142
- display_endpoints([endpoint_info])
146
+ _display_endpoints([endpoint_info])
143
147
  except Exception as e:
144
148
  print(f"[view_endpoint]: {str(e)}")
145
149
 
@@ -201,7 +205,7 @@ def delete_endpoint(args) -> None:
201
205
  # ------------------------------------------------------------------------------
202
206
  # Helper functions: Display on cli
203
207
  # ------------------------------------------------------------------------------
204
- def display_connector_types(connector_types: list) -> None:
208
+ def _display_connector_types(connector_types: list) -> None:
205
209
  """
206
210
  Display a list of connector types.
207
211
 
@@ -214,24 +218,22 @@ def display_connector_types(connector_types: list) -> None:
214
218
  Returns:
215
219
  None
216
220
  """
217
- if connector_types:
218
- table = Table(
219
- title="List of Connector Types",
220
- show_lines=True,
221
- expand=True,
222
- header_style="bold",
223
- )
224
- table.add_column("No.", width=2)
225
- table.add_column("Connector Type", justify="left", width=78)
226
- for connector_id, connector_type in enumerate(connector_types, 1):
227
- table.add_section()
228
- table.add_row(str(connector_id), connector_type)
229
- console.print(table)
230
- else:
231
- console.print("[red]There are no connector types found.[/red]")
221
+ table = Table(
222
+ title="List of Connector Types",
223
+ show_lines=True,
224
+ expand=True,
225
+ header_style="bold",
226
+ )
227
+ table.add_column("No.", width=2)
228
+ table.add_column("Connector Type", justify="left", width=78)
229
+
230
+ for idx, connector_type in enumerate(connector_types, 1):
231
+ table.add_section()
232
+ table.add_row(str(idx), connector_type)
233
+ console.print(table)
232
234
 
233
235
 
234
- def display_endpoints(endpoints_list):
236
+ def _display_endpoints(endpoints_list):
235
237
  """
236
238
  Display a list of endpoints.
237
239
 
@@ -245,52 +247,51 @@ def display_endpoints(endpoints_list):
245
247
  Returns:
246
248
  None
247
249
  """
248
- if endpoints_list:
249
- table = Table(
250
- title="List of Connector Endpoints",
251
- show_lines=True,
252
- expand=True,
253
- header_style="bold",
250
+ table = Table(
251
+ title="List of Connector Endpoints",
252
+ show_lines=True,
253
+ expand=True,
254
+ header_style="bold",
255
+ )
256
+ table.add_column("No.", justify="left", width=2)
257
+ table.add_column("Id", justify="left", width=10)
258
+ table.add_column("Name", justify="left", width=10)
259
+ table.add_column("Connector Type", justify="left", width=10)
260
+ table.add_column("Uri", justify="left", width=10)
261
+ table.add_column("Token", justify="left", width=10)
262
+ table.add_column("Max Calls Per Second", justify="left", width=5)
263
+ table.add_column("Max concurrency", justify="left", width=5)
264
+ table.add_column("Params", justify="left", width=30)
265
+ table.add_column("Created Date", justify="left", width=8)
266
+
267
+ for idx, endpoint in enumerate(endpoints_list, 1):
268
+ (
269
+ id,
270
+ name,
271
+ connector_type,
272
+ uri,
273
+ token,
274
+ max_calls_per_second,
275
+ max_concurrency,
276
+ params,
277
+ created_date,
278
+ *other_args,
279
+ ) = endpoint.values()
280
+ table.add_section()
281
+ idx = endpoint.get("idx", idx)
282
+ table.add_row(
283
+ str(idx),
284
+ id,
285
+ name,
286
+ connector_type,
287
+ uri,
288
+ token,
289
+ str(max_calls_per_second),
290
+ str(max_concurrency),
291
+ escape(str(params)),
292
+ created_date,
254
293
  )
255
- table.add_column("No.", justify="left", width=2)
256
- table.add_column("Id", justify="left", width=10)
257
- table.add_column("Name", justify="left", width=10)
258
- table.add_column("Connector Type", justify="left", width=10)
259
- table.add_column("Uri", justify="left", width=10)
260
- table.add_column("Token", justify="left", width=10)
261
- table.add_column("Max Calls Per Second", justify="left", width=5)
262
- table.add_column("Max concurrency", justify="left", width=5)
263
- table.add_column("Params", justify="left", width=30)
264
- table.add_column("Created Date", justify="left", width=8)
265
-
266
- for endpoint_id, endpoint in enumerate(endpoints_list, 1):
267
- (
268
- id,
269
- name,
270
- connector_type,
271
- uri,
272
- token,
273
- max_calls_per_second,
274
- max_concurrency,
275
- params,
276
- created_date,
277
- ) = endpoint.values()
278
- table.add_section()
279
- table.add_row(
280
- str(endpoint_id),
281
- id,
282
- name,
283
- connector_type,
284
- uri,
285
- token,
286
- str(max_calls_per_second),
287
- str(max_concurrency),
288
- escape(str(params)),
289
- created_date,
290
- )
291
- console.print(table)
292
- else:
293
- console.print("[red]There are no endpoints found.[/red]")
294
+ console.print(table)
294
295
 
295
296
 
296
297
  # ------------------------------------------------------------------------------
@@ -373,6 +374,13 @@ list_endpoints_args.add_argument(
373
374
  nargs="?",
374
375
  )
375
376
 
377
+ list_endpoints_args.add_argument(
378
+ "-p",
379
+ "--pagination",
380
+ type=str,
381
+ help="Optional tuple to paginate endpoint(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
382
+ nargs="?",
383
+ )
376
384
 
377
385
  # List connector types arguments
378
386
  list_connector_types_args = cmd2.Cmd2ArgumentParser(
@@ -387,3 +395,11 @@ list_connector_types_args.add_argument(
387
395
  help="Optional field to find connector type(s) with keyword",
388
396
  nargs="?",
389
397
  )
398
+
399
+ list_connector_types_args.add_argument(
400
+ "-p",
401
+ "--pagination",
402
+ type=str,
403
+ help="Optional tuple to paginate connector type(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
404
+ nargs="?",
405
+ )
@@ -0,0 +1,66 @@
1
+ from ast import literal_eval
2
+
3
+ import cmd2
4
+ from rich.console import Console
5
+
6
+ from moonshot.api import (
7
+ api_create_datasets,
8
+ )
9
+
10
+ console = Console()
11
+ def add_dataset(args) -> None:
12
+ """
13
+ Create a new dataset using the provided arguments and log the result.
14
+
15
+ This function attempts to create a new dataset by calling the `api_create_datasets`
16
+ function with the necessary parameters extracted from `args`. If successful, it logs
17
+ the creation of the dataset with its ID. If an exception occurs, it logs the error.
18
+
19
+ Args:
20
+ args: An argparse.Namespace object containing the following attributes:
21
+ - name (str): Name of the new dataset.
22
+ - description (str): Description of the new dataset.
23
+ - reference (str): Reference URL for the new dataset.
24
+ - license (str): License type for the new dataset.
25
+ - method (str): Method to convert the new dataset ('hf' or 'csv').
26
+ - params (dict): Additional parameters for dataset creation.
27
+ """
28
+ try:
29
+ new_dataset_id = api_create_datasets(
30
+ args.name,
31
+ args.description,
32
+ args.reference,
33
+ args.license,
34
+ args.method,
35
+ **args.params,
36
+ )
37
+ print(f"[add_dataset]: Dataset ({new_dataset_id}) created.")
38
+ except Exception as e:
39
+ print(f"[add_dataset]: {str(e)}")
40
+
41
+ # ------------------------------------------------------------------------------
42
+ # Cmd2 Arguments Parsers
43
+ # ------------------------------------------------------------------------------
44
+ # Add dataset arguments
45
+ add_dataset_args = cmd2.Cmd2ArgumentParser(
46
+ description="Add a new dataset. The 'name' argument will be slugified to create a unique identifier.",
47
+ epilog=(
48
+ "Examples:\n"
49
+ "1. add_dataset 'dataset-name' 'A brief description' 'http://reference.com' 'MIT' 'csv' \"{'csv_file_path': '/path/to/your/file.csv'}\"\n"
50
+ "2. add_dataset 'dataset-name' 'A brief description' 'http://reference.com' 'MIT' 'hf' \"{'dataset_name': 'cais/mmlu', 'dataset_config': 'college_biology', 'split': 'test', 'input_col': ['question','choices'], 'target_col': 'answer'}\""
51
+ ),
52
+ )
53
+ add_dataset_args.add_argument("name", type=str, help="Name of the new dataset")
54
+ add_dataset_args.add_argument("description", type=str, help="Description of the new dataset")
55
+ add_dataset_args.add_argument("reference", type=str, help="Reference of the new dataset")
56
+ add_dataset_args.add_argument("license", type=str, help="License of the new dataset")
57
+ add_dataset_args.add_argument("method", type=str, choices=['hf', 'csv'], help="Method to convert the new dataset. Choose either 'hf' or 'csv'.")
58
+ add_dataset_args.add_argument(
59
+ "params",
60
+ type=literal_eval,
61
+ help=(
62
+ "Params of the new dataset in dictionary format. For example: \n"
63
+ "1. For 'csv' method: \"{'csv_file_path': '/path/to/your/file.csv'}\"\n"
64
+ "2. For 'hf' method: \"{'dataset_name': 'cais_mmlu', 'dataset_config': 'college_biology', 'split': 'test', 'input_col': ['questions','choices'], 'target_col': 'answer'}\""
65
+ )
66
+ )
@@ -1,9 +1,11 @@
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
4
6
 
5
7
  from moonshot.api import api_delete_prompt_template, api_get_all_prompt_template_detail
6
- from moonshot.src.utils.find_feature import find_keyword
8
+ from moonshot.integrations.cli.utils.process_data import filter_data
7
9
 
8
10
  console = Console()
9
11
 
@@ -18,6 +20,7 @@ def list_prompt_templates(args) -> list | None:
18
20
  Args:
19
21
  args: A namespace object from argparse. It should have an optional attribute:
20
22
  find (str): Optional field to find prompt template(s) with a keyword.
23
+ pagination (str): Optional field to paginate prompt templates.
21
24
 
22
25
  Returns:
23
26
  list | None: A list of PromptTemplate or None if there is no result.
@@ -25,19 +28,19 @@ def list_prompt_templates(args) -> list | None:
25
28
  try:
26
29
  prompt_templates_list = api_get_all_prompt_template_detail()
27
30
  keyword = args.find.lower() if args.find else ""
28
- if keyword:
29
- filtered_prompt_templates_list = find_keyword(
30
- keyword, prompt_templates_list
31
+ pagination = literal_eval(args.pagination) if args.pagination else ()
32
+
33
+ if prompt_templates_list:
34
+ filtered_prompt_templates_list = filter_data(
35
+ prompt_templates_list, keyword, pagination
31
36
  )
32
37
  if filtered_prompt_templates_list:
33
- display_prompt_templates(filtered_prompt_templates_list)
38
+ _display_prompt_templates(filtered_prompt_templates_list)
34
39
  return filtered_prompt_templates_list
35
- else:
36
- print("No prompt templates containing keyword found.")
37
- return None
38
- else:
39
- display_prompt_templates(prompt_templates_list)
40
- return prompt_templates_list
40
+
41
+ console.print("[red]There are no prompt templates found.[/red]")
42
+ return None
43
+
41
44
  except Exception as e:
42
45
  print(f"[list_prompt_templates]: {str(e)}")
43
46
 
@@ -67,7 +70,7 @@ def delete_prompt_template(args) -> None:
67
70
  # ------------------------------------------------------------------------------
68
71
  # Helper functions: Display on cli
69
72
  # ------------------------------------------------------------------------------
70
- def display_prompt_templates(prompt_templates) -> None:
73
+ def _display_prompt_templates(prompt_templates) -> None:
71
74
  """
72
75
  Display the list of prompt templates in a formatted table.
73
76
 
@@ -87,21 +90,13 @@ def display_prompt_templates(prompt_templates) -> None:
87
90
  table.add_column("No.", width=2)
88
91
  table.add_column("Prompt Template", justify="left", width=50)
89
92
  table.add_column("Contains", justify="left", width=48, overflow="fold")
90
- if prompt_templates:
91
- for prompt_index, prompt_template in enumerate(prompt_templates, 1):
92
- (
93
- id,
94
- name,
95
- description,
96
- contents,
97
- ) = prompt_template.values()
98
-
99
- prompt_info = f"[red]id: {id}[/red]\n\n[blue]{name}[/blue]\n{description}"
100
- table.add_section()
101
- table.add_row(str(prompt_index), prompt_info, contents)
102
- console.print(table)
103
- else:
104
- console.print("[red]There are no prompt templates found.[/red]")
93
+ for idx, prompt_template in enumerate(prompt_templates, 1):
94
+ (id, name, description, contents, *other_args) = prompt_template.values()
95
+ idx = prompt_template.get("idx", idx)
96
+ prompt_info = f"[red]id: {id}[/red]\n\n[blue]{name}[/blue]\n{description}"
97
+ table.add_section()
98
+ table.add_row(str(idx), prompt_info, contents)
99
+ console.print(table)
105
100
 
106
101
 
107
102
  # Delete prompt template arguments
@@ -127,3 +122,11 @@ list_prompt_templates_args.add_argument(
127
122
  help="Optional field to find prompt template(s) with keyword",
128
123
  nargs="?",
129
124
  )
125
+
126
+ list_prompt_templates_args.add_argument(
127
+ "-p",
128
+ "--pagination",
129
+ type=str,
130
+ help="Optional tuple to paginate prompt template(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
131
+ nargs="?",
132
+ )
@@ -1,13 +1,18 @@
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
4
6
 
5
7
  from moonshot.api import api_delete_attack_module, api_get_all_attack_module_metadata
6
- from moonshot.src.utils.find_feature import find_keyword
8
+ from moonshot.integrations.cli.utils.process_data import filter_data
7
9
 
8
10
  console = Console()
9
11
 
10
12
 
13
+ # ------------------------------------------------------------------------------
14
+ # CLI Functions
15
+ # ------------------------------------------------------------------------------
11
16
  def list_attack_modules(args) -> list | None:
12
17
  """
13
18
  Retrieves and prints the metadata of all attack modules.
@@ -15,6 +20,7 @@ def list_attack_modules(args) -> list | None:
15
20
  Args:
16
21
  args: A namespace object from argparse. It should have an optional attribute:
17
22
  find (str): Optional field to find attack module(s) with a keyword.
23
+ pagination (str): Optional field to paginate attack modules.
18
24
 
19
25
  Returns:
20
26
  list | None: A list of AttackModule or None if there is no result.
@@ -23,19 +29,19 @@ def list_attack_modules(args) -> list | None:
23
29
  print("Listing attack modules may take a while...")
24
30
  attack_module_metadata_list = api_get_all_attack_module_metadata()
25
31
  keyword = args.find.lower() if args.find else ""
26
- if keyword:
27
- filtered_attack_modules_list = find_keyword(
28
- keyword, attack_module_metadata_list
32
+ pagination = literal_eval(args.pagination) if args.pagination else ()
33
+
34
+ if attack_module_metadata_list:
35
+ filtered_attack_modules_list = filter_data(
36
+ attack_module_metadata_list, keyword, pagination
29
37
  )
30
38
  if filtered_attack_modules_list:
31
- display_attack_modules(filtered_attack_modules_list)
39
+ _display_attack_modules(filtered_attack_modules_list)
32
40
  return filtered_attack_modules_list
33
- else:
34
- print("No attack modules containing keyword found.")
35
- return None
36
- else:
37
- display_attack_modules(attack_module_metadata_list)
38
- return attack_module_metadata_list
41
+
42
+ console.print("[red]There are no attack modules found.[/red]")
43
+ return None
44
+
39
45
  except Exception as e:
40
46
  print(f"[list_attack_modules]: {str(e)}")
41
47
 
@@ -62,7 +68,10 @@ def delete_attack_module(args) -> None:
62
68
  print(f"[delete_attack_module]: {str(e)}")
63
69
 
64
70
 
65
- def display_attack_modules(attack_modules: list) -> None:
71
+ # ------------------------------------------------------------------------------
72
+ # Helper functions: Display on cli
73
+ # ------------------------------------------------------------------------------
74
+ def _display_attack_modules(attack_modules: list) -> None:
66
75
  """
67
76
  Display a list of attack modules.
68
77
 
@@ -75,25 +84,23 @@ def display_attack_modules(attack_modules: list) -> None:
75
84
  Returns:
76
85
  None
77
86
  """
78
- if attack_modules:
79
- table = Table(
80
- title="Attack Module List",
81
- show_lines=True,
82
- expand=True,
83
- header_style="bold",
84
- )
85
- table.add_column("No.", width=2)
86
- table.add_column("Details", justify="left", width=98)
87
-
88
- for attack_module_index, attack_module_data in enumerate(attack_modules, 1):
89
- attack_module_data_str = ""
90
- for k, v in attack_module_data.items():
91
- attack_module_data_str += f"[blue]{k.capitalize()}:[/blue] {v}\n\n"
92
- table.add_row(str(attack_module_index), attack_module_data_str)
87
+ table = Table(
88
+ title="Attack Module List",
89
+ show_lines=True,
90
+ expand=True,
91
+ header_style="bold",
92
+ )
93
+ table.add_column("No.", width=2)
94
+ table.add_column("Details", justify="left", width=98)
93
95
 
94
- console.print(table)
95
- else:
96
- console.print("[red]There are no attack modules found.[/red]", style="bold")
96
+ for idx, attack_module_data in enumerate(attack_modules, 1):
97
+ attack_module_data_str = ""
98
+ for k, v in attack_module_data.items():
99
+ if k != "idx":
100
+ attack_module_data_str += f"[blue]{k.capitalize()}:[/blue] {v}\n\n"
101
+ idx = attack_module_data.get("idx", idx)
102
+ table.add_row(str(idx), attack_module_data_str)
103
+ console.print(table)
97
104
 
98
105
 
99
106
  # Delete attack module arguments
@@ -119,3 +126,11 @@ list_attack_modules_args.add_argument(
119
126
  help="Optional field to find attack module(s) with keyword",
120
127
  nargs="?",
121
128
  )
129
+
130
+ list_attack_modules_args.add_argument(
131
+ "-p",
132
+ "--pagination",
133
+ type=str,
134
+ help="Optional tuple to paginate attack module(s). E.g. (2,10) returns 2nd page with 10 items in each page.",
135
+ nargs="?",
136
+ )