enkryptai-sdk 1.0.24__py3-none-any.whl → 1.0.26__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.
enkryptai_sdk/red_team.py CHANGED
@@ -6,11 +6,14 @@ from .datasets import DatasetClient
6
6
  from .dto import (
7
7
  RedteamHealthResponse,
8
8
  RedTeamModelHealthConfig,
9
+ RedTeamModelHealthConfigV3,
9
10
  RedteamModelHealthResponse,
10
11
  RedTeamConfig,
11
12
  RedTeamConfigWithSavedModel,
12
13
  RedTeamCustomConfig,
13
14
  RedTeamCustomConfigWithSavedModel,
15
+ RedTeamCustomConfigV3,
16
+ RedTeamCustomConfigWithSavedModelV3,
14
17
  RedTeamResponse,
15
18
  RedTeamResultSummary,
16
19
  RedTeamResultDetails,
@@ -21,7 +24,8 @@ from .dto import (
21
24
  RedTeamRiskMitigationGuardrailsPolicyResponse,
22
25
  RedTeamRiskMitigationSystemPromptConfig,
23
26
  RedTeamRiskMitigationSystemPromptResponse,
24
- RedTeamFindingsResponse
27
+ RedTeamFindingsResponse,
28
+ RedTeamDownloadLinkResponse,
25
29
  )
26
30
 
27
31
 
@@ -70,14 +74,16 @@ class RedTeamClient(BaseClient):
70
74
  config = RedTeamModelHealthConfig.from_dict(config)
71
75
  # Print the config as json string
72
76
  # print(f"Config: {json.dumps(config.to_dict(), indent=4)}")
73
- response = self._request("POST", "/redteam/model-health", json=config.to_dict())
77
+ response = self._request(
78
+ "POST", "/redteam/model-health", json=config.to_dict()
79
+ )
74
80
  # if response.get("error"):
75
81
  if response.get("error") not in [None, ""]:
76
82
  raise RedTeamClientError(f"API Error: {str(response)}")
77
83
  return RedteamModelHealthResponse.from_dict(response)
78
84
  except Exception as e:
79
85
  raise RedTeamClientError(str(e))
80
-
86
+
81
87
  def check_saved_model_health(self, model_saved_name: str, model_version: str):
82
88
  """
83
89
  Get the health status of a saved model.
@@ -87,7 +93,9 @@ class RedTeamClient(BaseClient):
87
93
  "X-Enkrypt-Model": model_saved_name,
88
94
  "X-Enkrypt-Model-Version": model_version,
89
95
  }
90
- response = self._request("POST", "/redteam/model/model-health", headers=headers)
96
+ response = self._request(
97
+ "POST", "/redteam/model/model-health", headers=headers
98
+ )
91
99
  # if response.get("error"):
92
100
  if response.get("error") not in [None, ""]:
93
101
  raise RedTeamClientError(f"API Error: {str(response)}")
@@ -95,6 +103,38 @@ class RedTeamClient(BaseClient):
95
103
  except Exception as e:
96
104
  raise RedTeamClientError(str(e))
97
105
 
106
+ def check_model_health_v3(self, config: RedTeamModelHealthConfigV3):
107
+ """
108
+ Get the health status of a model using V3 format with endpoint_configuration.
109
+
110
+ This method accepts endpoint_configuration (similar to add_custom_task) and
111
+ converts it internally to target_model_configuration format for backend compatibility.
112
+
113
+ Args:
114
+ config (RedTeamModelHealthConfigV3): Configuration object containing endpoint_configuration
115
+
116
+ Returns:
117
+ RedteamModelHealthResponse: Response from the API containing health status
118
+
119
+ Raises:
120
+ RedTeamClientError: If there's an error from the API
121
+ """
122
+ try:
123
+ config = RedTeamModelHealthConfigV3.from_dict(config)
124
+
125
+ # Convert endpoint_configuration to target_model_configuration
126
+ target_config = config.to_target_model_configuration()
127
+
128
+ # Create the payload in the format expected by the backend
129
+ payload = {"target_model_configuration": target_config.to_dict()}
130
+
131
+ response = self._request("POST", "/redteam/model-health", json=payload)
132
+ if response.get("error") not in [None, ""]:
133
+ raise RedTeamClientError(f"API Error: {str(response)}")
134
+ return RedteamModelHealthResponse.from_dict(response)
135
+ except Exception as e:
136
+ raise RedTeamClientError(str(e))
137
+
98
138
  def add_task(
99
139
  self,
100
140
  config: RedTeamConfig,
@@ -128,9 +168,7 @@ class RedTeamClient(BaseClient):
128
168
  raise RedTeamClientError(f"API Error: {str(response)}")
129
169
  return RedTeamResponse.from_dict(response)
130
170
  else:
131
- raise RedTeamClientError(
132
- "Please provide a target model configuration"
133
- )
171
+ raise RedTeamClientError("Please provide a target model configuration")
134
172
 
135
173
  def add_task_with_saved_model(
136
174
  self,
@@ -143,7 +181,7 @@ class RedTeamClient(BaseClient):
143
181
  """
144
182
  if not model_saved_name:
145
183
  raise RedTeamClientError("Please provide a model_saved_name")
146
-
184
+
147
185
  if not model_version:
148
186
  raise RedTeamClientError("Please provide a model_version. Default is 'v1'")
149
187
 
@@ -200,17 +238,19 @@ class RedTeamClient(BaseClient):
200
238
  "redteam_test_configurations": test_configs,
201
239
  }
202
240
 
241
+ # Only add frameworks if provided and not empty
242
+ if config.frameworks:
243
+ payload["frameworks"] = config.frameworks
244
+
203
245
  if config.dataset_configuration:
204
246
  payload["dataset_configuration"] = DatasetClient.prepare_dataset_payload(
205
- config.dataset_configuration, True)
206
- else:
207
- raise RedTeamClientError(
208
- "Please provide a dataset configuration"
247
+ config.dataset_configuration, True
209
248
  )
210
249
 
211
250
  if config.endpoint_configuration:
212
251
  payload["endpoint_configuration"] = ModelClient.prepare_model_payload(
213
- config.endpoint_configuration, True)
252
+ config.endpoint_configuration, True
253
+ )
214
254
  # print(payload)
215
255
 
216
256
  response = self._request(
@@ -223,9 +263,7 @@ class RedTeamClient(BaseClient):
223
263
  raise RedTeamClientError(f"API Error: {str(response)}")
224
264
  return RedTeamResponse.from_dict(response)
225
265
  else:
226
- raise RedTeamClientError(
227
- "Please provide a endpoint configuration"
228
- )
266
+ raise RedTeamClientError("Please provide a endpoint configuration")
229
267
 
230
268
  def add_custom_task_with_saved_model(
231
269
  self,
@@ -239,7 +277,7 @@ class RedTeamClient(BaseClient):
239
277
  """
240
278
  if not model_saved_name:
241
279
  raise RedTeamClientError("Please provide a model_saved_name")
242
-
280
+
243
281
  if not model_version:
244
282
  raise RedTeamClientError("Please provide a model_version. Default is 'v1'")
245
283
 
@@ -254,12 +292,13 @@ class RedTeamClient(BaseClient):
254
292
  "redteam_test_configurations": test_configs,
255
293
  }
256
294
 
295
+ # Only add frameworks if provided and not empty
296
+ if config.frameworks:
297
+ payload["frameworks"] = config.frameworks
298
+
257
299
  if config.dataset_configuration:
258
300
  payload["dataset_configuration"] = DatasetClient.prepare_dataset_payload(
259
- config.dataset_configuration, True)
260
- else:
261
- raise RedTeamClientError(
262
- "Please provide a dataset configuration"
301
+ config.dataset_configuration, True
263
302
  )
264
303
 
265
304
  headers = {
@@ -281,6 +320,140 @@ class RedTeamClient(BaseClient):
281
320
  raise RedTeamClientError(f"API Error: {str(response)}")
282
321
  return RedTeamResponse.from_dict(response)
283
322
 
323
+ def add_custom_task_v3(
324
+ self,
325
+ config: RedTeamCustomConfigV3,
326
+ policy_name: str = None,
327
+ ):
328
+ """
329
+ Add a new custom red teaming task with v3 attack methods format.
330
+
331
+ V3 format supports nested attack methods:
332
+ {
333
+ "test_name": {
334
+ "sample_percentage": 50,
335
+ "attack_methods": {
336
+ "method_category": {
337
+ "method_name": {
338
+ "params": {}
339
+ }
340
+ }
341
+ }
342
+ }
343
+ }
344
+ """
345
+ headers = {
346
+ "Content-Type": "application/json",
347
+ }
348
+
349
+ if policy_name is not None:
350
+ headers["X-Enkrypt-Policy"] = policy_name
351
+
352
+ config = RedTeamCustomConfigV3.from_dict(config)
353
+ test_configs = config.redteam_test_configurations.to_dict()
354
+ # Remove None or empty test configurations
355
+ test_configs = {k: v for k, v in test_configs.items() if v is not None}
356
+
357
+ payload = {
358
+ "test_name": config.test_name,
359
+ "redteam_test_configurations": test_configs,
360
+ }
361
+
362
+ # Only add frameworks if provided and not empty
363
+ if config.frameworks:
364
+ payload["frameworks"] = config.frameworks
365
+
366
+ if config.dataset_configuration:
367
+ payload["dataset_configuration"] = DatasetClient.prepare_dataset_payload(
368
+ config.dataset_configuration, True
369
+ )
370
+
371
+ if config.endpoint_configuration:
372
+ payload["endpoint_configuration"] = ModelClient.prepare_model_payload(
373
+ config.endpoint_configuration, True
374
+ )
375
+
376
+ response = self._request(
377
+ "POST",
378
+ "/redteam/v3/add-custom-task",
379
+ headers=headers,
380
+ json=payload,
381
+ )
382
+ if response.get("error"):
383
+ raise RedTeamClientError(f"API Error: {str(response)}")
384
+ return RedTeamResponse.from_dict(response)
385
+ else:
386
+ raise RedTeamClientError("Please provide a endpoint configuration")
387
+
388
+ def add_custom_task_with_saved_model_v3(
389
+ self,
390
+ config: RedTeamCustomConfigWithSavedModelV3,
391
+ model_saved_name: str,
392
+ model_version: str,
393
+ policy_name: str = None,
394
+ ):
395
+ """
396
+ Add a new red teaming custom task using a saved model with v3 attack methods format.
397
+
398
+ V3 format supports nested attack methods:
399
+ {
400
+ "test_name": {
401
+ "sample_percentage": 50,
402
+ "attack_methods": {
403
+ "method_category": {
404
+ "method_name": {
405
+ "params": {}
406
+ }
407
+ }
408
+ }
409
+ }
410
+ }
411
+ """
412
+ if not model_saved_name:
413
+ raise RedTeamClientError("Please provide a model_saved_name")
414
+
415
+ if not model_version:
416
+ raise RedTeamClientError("Please provide a model_version. Default is 'v1'")
417
+
418
+ config = RedTeamCustomConfigWithSavedModelV3.from_dict(config)
419
+ test_configs = config.redteam_test_configurations.to_dict()
420
+ # Remove None or empty test configurations
421
+ test_configs = {k: v for k, v in test_configs.items() if v is not None}
422
+
423
+ payload = {
424
+ "test_name": config.test_name,
425
+ "redteam_test_configurations": test_configs,
426
+ }
427
+
428
+ # Only add frameworks if provided and not empty
429
+ if config.frameworks:
430
+ payload["frameworks"] = config.frameworks
431
+ print(config.__dict__)
432
+ if config.dataset_configuration:
433
+ payload["dataset_configuration"] = DatasetClient.prepare_dataset_payload(
434
+ config.dataset_configuration, True
435
+ )
436
+
437
+ headers = {
438
+ "X-Enkrypt-Model": model_saved_name,
439
+ "X-Enkrypt-Model-Version": model_version,
440
+ "Content-Type": "application/json",
441
+ }
442
+
443
+ if policy_name is not None:
444
+ headers["X-Enkrypt-Policy"] = policy_name
445
+
446
+ print("Request payload:", payload)
447
+ response = self._request(
448
+ "POST",
449
+ "/redteam/v3/model/add-custom-task",
450
+ headers=headers,
451
+ json=payload,
452
+ )
453
+ if response.get("error"):
454
+ raise RedTeamClientError(f"API Error: {str(response)}")
455
+ return RedTeamResponse.from_dict(response)
456
+
284
457
  def status(self, task_id: str = None, test_name: str = None):
285
458
  """
286
459
  Get the status of a specific red teaming task.
@@ -291,13 +464,13 @@ class RedTeamClient(BaseClient):
291
464
 
292
465
  Returns:
293
466
  dict: The task status information
294
-
467
+
295
468
  Raises:
296
469
  RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
297
470
  """
298
471
  if not task_id and not test_name:
299
472
  raise RedTeamClientError("Either task_id or test_name must be provided")
300
-
473
+
301
474
  headers = {}
302
475
  if task_id:
303
476
  headers["X-Enkrypt-Task-ID"] = task_id
@@ -352,13 +525,13 @@ class RedTeamClient(BaseClient):
352
525
 
353
526
  Returns:
354
527
  dict: The task details and status
355
-
528
+
356
529
  Raises:
357
530
  RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
358
531
  """
359
532
  if not task_id and not test_name:
360
533
  raise RedTeamClientError("Either task_id or test_name must be provided")
361
-
534
+
362
535
  headers = {}
363
536
  if task_id:
364
537
  headers["X-Enkrypt-Task-ID"] = task_id
@@ -381,26 +554,28 @@ class RedTeamClient(BaseClient):
381
554
 
382
555
  Returns:
383
556
  dict: The summary of the task results
384
-
557
+
385
558
  Raises:
386
559
  RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
387
560
  """
388
561
  if not task_id and not test_name:
389
562
  raise RedTeamClientError("Either task_id or test_name must be provided")
390
-
563
+
391
564
  headers = {}
392
565
  if task_id:
393
566
  headers["X-Enkrypt-Task-ID"] = task_id
394
567
  if test_name:
395
568
  headers["X-Enkrypt-Test-Name"] = test_name
396
569
 
397
- response = self._request("GET", "/redteam/results/summary", headers=headers)
570
+ response = self._request("GET", "/redteam/v3/results/summary", headers=headers)
398
571
  if response.get("error"):
399
572
  raise RedTeamClientError(f"API Error: {str(response)}")
400
573
  # print(f"Response: {response}")
401
574
  return RedTeamResultSummary.from_dict(response)
402
-
403
- def get_result_summary_test_type(self, task_id: str = None, test_name: str = None, test_type: str = None):
575
+
576
+ def get_result_summary_test_type(
577
+ self, task_id: str = None, test_name: str = None, test_type: str = None
578
+ ):
404
579
  """
405
580
  Get the summary of results for a specific red teaming task for a specific test type.
406
581
 
@@ -411,23 +586,23 @@ class RedTeamClient(BaseClient):
411
586
 
412
587
  Returns:
413
588
  dict: The summary of the task results for the specified test type
414
-
589
+
415
590
  Raises:
416
591
  RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
417
592
  """
418
593
  if not task_id and not test_name:
419
594
  raise RedTeamClientError("Either task_id or test_name must be provided")
420
-
595
+
421
596
  if not test_type:
422
597
  raise RedTeamClientError("test_type must be provided")
423
-
598
+
424
599
  headers = {}
425
600
  if task_id:
426
601
  headers["X-Enkrypt-Task-ID"] = task_id
427
602
  if test_name:
428
603
  headers["X-Enkrypt-Test-Name"] = test_name
429
604
 
430
- url = f"/redteam/v2/results/summary/{test_type}"
605
+ url = f"/redteam/v3/results/summary/{test_type}"
431
606
  response = self._request("GET", url, headers=headers)
432
607
  if response.get("error"):
433
608
  raise RedTeamClientError(f"API Error: {str(response)}")
@@ -444,25 +619,27 @@ class RedTeamClient(BaseClient):
444
619
 
445
620
  Returns:
446
621
  dict: The detailed task results
447
-
622
+
448
623
  Raises:
449
624
  RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
450
625
  """
451
626
  if not task_id and not test_name:
452
627
  raise RedTeamClientError("Either task_id or test_name must be provided")
453
-
628
+
454
629
  headers = {}
455
630
  if task_id:
456
631
  headers["X-Enkrypt-Task-ID"] = task_id
457
632
  if test_name:
458
633
  headers["X-Enkrypt-Test-Name"] = test_name
459
-
460
- response = self._request("GET", "/redteam/v2/results/details", headers=headers)
634
+
635
+ response = self._request("GET", "/redteam/v3/results/details", headers=headers)
461
636
  if response.get("error"):
462
637
  raise RedTeamClientError(f"API Error: {str(response)}")
463
638
  return RedTeamResultDetails.from_dict(response)
464
-
465
- def get_result_details_test_type(self, task_id: str = None, test_name: str = None, test_type: str = None):
639
+
640
+ def get_result_details_test_type(
641
+ self, task_id: str = None, test_name: str = None, test_type: str = None
642
+ ):
466
643
  """
467
644
  Get the detailed results for a specific red teaming task for a specific test type.
468
645
 
@@ -473,24 +650,23 @@ class RedTeamClient(BaseClient):
473
650
 
474
651
  Returns:
475
652
  dict: The detailed task results
476
-
653
+
477
654
  Raises:
478
655
  RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
479
656
  """
480
657
  if not task_id and not test_name:
481
658
  raise RedTeamClientError("Either task_id or test_name must be provided")
482
-
659
+
483
660
  if not test_type:
484
661
  raise RedTeamClientError("test_type must be provided")
485
-
662
+
486
663
  headers = {}
487
664
  if task_id:
488
665
  headers["X-Enkrypt-Task-ID"] = task_id
489
666
  if test_name:
490
667
  headers["X-Enkrypt-Test-Name"] = test_name
491
-
492
668
 
493
- url = f"/redteam/v2/results/details/{test_type}"
669
+ url = f"/redteam/v3/results/details/{test_type}"
494
670
  response = self._request("GET", url, headers=headers)
495
671
  if response.get("error"):
496
672
  raise RedTeamClientError(f"API Error: {str(response)}")
@@ -515,26 +691,34 @@ class RedTeamClient(BaseClient):
515
691
  raise RedTeamClientError(f"API Error: {str(response)}")
516
692
  return RedTeamTaskList.from_dict(response)
517
693
 
518
- def risk_mitigation_guardrails_policy(self, config: RedTeamRiskMitigationGuardrailsPolicyConfig):
694
+ def risk_mitigation_guardrails_policy(
695
+ self, config: RedTeamRiskMitigationGuardrailsPolicyConfig
696
+ ):
519
697
  """
520
698
  Get the guardrails policy generated for risk mitigation.
521
699
  """
522
700
  config = RedTeamRiskMitigationGuardrailsPolicyConfig.from_dict(config)
523
701
  payload = config.to_dict()
524
-
525
- response = self._request("POST", "/redteam/risk-mitigation/guardrails-policy", json=payload)
702
+
703
+ response = self._request(
704
+ "POST", "/redteam/risk-mitigation/guardrails-policy", json=payload
705
+ )
526
706
  if isinstance(response, dict) and response.get("error"):
527
707
  raise RedTeamClientError(f"API Error: {str(response)}")
528
708
  return RedTeamRiskMitigationGuardrailsPolicyResponse.from_dict(response)
529
709
 
530
- def risk_mitigation_system_prompt(self, config: RedTeamRiskMitigationSystemPromptConfig):
710
+ def risk_mitigation_system_prompt(
711
+ self, config: RedTeamRiskMitigationSystemPromptConfig
712
+ ):
531
713
  """
532
714
  Get the system prompt generated for risk mitigation.
533
715
  """
534
716
  config = RedTeamRiskMitigationSystemPromptConfig.from_dict(config)
535
717
  payload = config.to_dict()
536
-
537
- response = self._request("POST", "/redteam/risk-mitigation/system-prompt", json=payload)
718
+
719
+ response = self._request(
720
+ "POST", "/redteam/risk-mitigation/system-prompt", json=payload
721
+ )
538
722
  if isinstance(response, dict) and response.get("error"):
539
723
  raise RedTeamClientError(f"API Error: {str(response)}")
540
724
  return RedTeamRiskMitigationSystemPromptResponse.from_dict(response)
@@ -542,20 +726,18 @@ class RedTeamClient(BaseClient):
542
726
  def get_findings(self, redteam_summary):
543
727
  """
544
728
  Get findings and insights based on red team summary data.
545
-
729
+
546
730
  Parameters:
547
731
  - redteam_summary (dict or ResultSummary): Red team test summary data
548
-
732
+
549
733
  Returns:
550
734
  - RedTeamFindingsResponse: Response from the API containing findings
551
735
  """
552
736
  # Allow passing in either a dict or a ResultSummary instance
553
737
  if hasattr(redteam_summary, "to_dict"):
554
738
  redteam_summary = redteam_summary.to_dict()
555
-
556
- payload = {
557
- "redteam_summary": redteam_summary
558
- }
739
+
740
+ payload = {"redteam_summary": redteam_summary}
559
741
 
560
742
  try:
561
743
  response = self._request("POST", "/redteam/findings", json=payload)
@@ -564,4 +746,34 @@ class RedTeamClient(BaseClient):
564
746
  return RedTeamFindingsResponse.from_dict(response)
565
747
  except Exception as e:
566
748
  raise RedTeamClientError(str(e))
567
-
749
+
750
+ def get_download_link(self, task_id: str = None, test_name: str = None):
751
+ """
752
+ Get a download link for red team test results.
753
+
754
+ Args:
755
+ task_id (str, optional): The ID of the task to get download link for
756
+ test_name (str, optional): The name of the test to get download link for
757
+
758
+ Returns:
759
+ RedTeamDownloadLinkResponse: Response containing download link and expiry information
760
+
761
+ Raises:
762
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
763
+ """
764
+ if not task_id and not test_name:
765
+ raise RedTeamClientError("Either task_id or test_name must be provided")
766
+
767
+ headers = {}
768
+ if task_id:
769
+ headers["X-Enkrypt-Task-ID"] = task_id
770
+ if test_name:
771
+ headers["X-Enkrypt-Test-Name"] = test_name
772
+
773
+ try:
774
+ response = self._request("GET", "/redteam/download-link", headers=headers)
775
+ if response.get("error"):
776
+ raise RedTeamClientError(f"API Error: {str(response)}")
777
+ return RedTeamDownloadLinkResponse.from_dict(response)
778
+ except Exception as e:
779
+ raise RedTeamClientError(str(e))