enkryptai-sdk 0.1.7__py3-none-any.whl → 1.0.1__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/datasets.py CHANGED
@@ -128,15 +128,21 @@ class DatasetClient(BaseClient):
128
128
  response["dataset_name"] = dataset_name
129
129
  return DatasetSummary.from_dict(response)
130
130
 
131
- def list_datasets(self):
131
+ def list_datasets(self, status: str = None):
132
132
  """
133
133
  Get a list of all available dataset tasks.
134
134
 
135
+ Args:
136
+ status (str): Filter the list of dataset tasks by status
137
+
135
138
  Returns:
136
139
  dict: Response from the API containing the list of dataset tasks
137
140
  """
138
141
 
139
- response = self._request("GET", "/datasets/list-tasks")
142
+ url = "/datasets/list-tasks"
143
+ if status:
144
+ url += f"?status={status}"
145
+ response = self._request("GET", url)
140
146
  if response.get("error"):
141
147
  raise DatasetClientError(response["error"])
142
148
  return DatasetCollection.from_dict(response)
@@ -21,6 +21,7 @@ __all__ = [
21
21
  "Location",
22
22
  "Metadata",
23
23
  "DEFAULT_REDTEAM_CONFIG",
24
+ "DEFAULT_REDTEAM_CONFIG_WITH_SAVED_MODEL",
24
25
  "ADVANCED_REDTEAM_TESTS",
25
26
  "DETAIL_MODEL_CONFIG",
26
27
  "DatasetConfig",
@@ -73,6 +73,28 @@ class StatisticItem(BaseDTO):
73
73
  return d
74
74
 
75
75
 
76
+ @dataclass
77
+ class StatisticItemWithTestType(BaseDTO):
78
+ success_percentage: float
79
+ total: int
80
+ test_type: Optional[str] = None
81
+ _extra_fields: Dict[str, Any] = field(default_factory=dict)
82
+
83
+ @classmethod
84
+ def from_dict(cls, data: Dict) -> "StatisticItemWithTestType":
85
+ return cls(
86
+ success_percentage=data.get("success(%)", 0.0),
87
+ total=data.get("total", 0),
88
+ test_type=data.get("test_type", None),
89
+ )
90
+
91
+ def to_dict(self) -> Dict:
92
+ d = super().to_dict()
93
+ # Special handling for success percentage key
94
+ d["success(%)"] = d.pop("success_percentage")
95
+ return d
96
+
97
+
76
98
  @dataclass
77
99
  class ResultSummary(BaseDTO):
78
100
  test_date: str
@@ -86,7 +108,7 @@ class ResultSummary(BaseDTO):
86
108
  test_type: Dict[str, StatisticItem]
87
109
  nist_category: Dict[str, StatisticItem]
88
110
  scenario: Dict[str, StatisticItem]
89
- category: Dict[str, StatisticItem]
111
+ category: Dict[str, StatisticItemWithTestType]
90
112
  attack_method: Dict[str, StatisticItem]
91
113
  _extra_fields: Dict[str, Any] = field(default_factory=dict)
92
114
 
@@ -98,6 +120,13 @@ class ResultSummary(BaseDTO):
98
120
  for key, value in item.items():
99
121
  result[key] = StatisticItem.from_dict(value)
100
122
  return result
123
+
124
+ def convert_stat_test_type_list(stat_list: List[Dict]) -> Dict[str, StatisticItemWithTestType]:
125
+ result = {}
126
+ for item in stat_list:
127
+ for key, value in item.items():
128
+ result[key] = StatisticItemWithTestType.from_dict(value)
129
+ return result
101
130
 
102
131
  return cls(
103
132
  test_date=data.get("test_date", ""),
@@ -111,20 +140,23 @@ class ResultSummary(BaseDTO):
111
140
  test_type=convert_stat_list(data.get("test_type", [])),
112
141
  nist_category=convert_stat_list(data.get("nist_category", [])),
113
142
  scenario=convert_stat_list(data.get("scenario", [])),
114
- category=convert_stat_list(data.get("category", [])),
143
+ category=convert_stat_test_type_list(data.get("category", [])),
115
144
  attack_method=convert_stat_list(data.get("attack_method", [])),
116
145
  )
117
146
 
118
147
  def to_dict(self) -> Dict:
119
148
  def convert_stat_dict(stat_dict: Dict[str, StatisticItem]) -> List[Dict]:
120
149
  return [{key: value.to_dict()} for key, value in stat_dict.items()]
150
+
151
+ def convert_stat_test_type_dict(stat_dict: Dict[str, StatisticItemWithTestType]) -> List[Dict]:
152
+ return [{key: value.to_dict()} for key, value in stat_dict.items()]
121
153
 
122
154
  d = super().to_dict()
123
155
  # Convert stat dictionaries to lists of dictionaries
124
156
  d["test_type"] = convert_stat_dict(self.test_type)
125
157
  d["nist_category"] = convert_stat_dict(self.nist_category)
126
158
  d["scenario"] = convert_stat_dict(self.scenario)
127
- d["category"] = convert_stat_dict(self.category)
159
+ d["category"] = convert_stat_test_type_dict(self.category)
128
160
  d["attack_method"] = convert_stat_dict(self.attack_method)
129
161
  return d
130
162
 
@@ -132,12 +164,17 @@ class ResultSummary(BaseDTO):
132
164
  @dataclass
133
165
  class RedTeamResultSummary(BaseDTO):
134
166
  summary: ResultSummary
167
+ task_status: Optional[str] = None
135
168
  _extra_fields: Dict[str, Any] = field(default_factory=dict)
136
169
 
137
170
  @classmethod
138
171
  def from_dict(cls, data: Dict) -> "RedTeamResultSummary":
139
172
  if not data or "summary" not in data:
140
173
  return cls(summary=ResultSummary.from_dict({}))
174
+
175
+ if "task_status" in data:
176
+ return cls(summary=ResultSummary.from_dict({}), task_status=data["task_status"])
177
+
141
178
  return cls(summary=ResultSummary.from_dict(data["summary"]))
142
179
 
143
180
  def to_dict(self) -> Dict:
@@ -174,42 +211,47 @@ class TestResult(BaseDTO):
174
211
  @dataclass
175
212
  class RedTeamResultDetails(BaseDTO):
176
213
  details: List[TestResult]
214
+ task_status: Optional[str] = None
177
215
  _extra_fields: Dict[str, Any] = field(default_factory=dict)
178
216
 
179
217
  @classmethod
180
218
  def from_dict(cls, data: Dict) -> "RedTeamResultDetails":
181
219
  if not data or "details" not in data:
182
220
  return cls(details=[])
221
+
222
+ if "task_status" in data:
223
+ return cls(details=[], task_status=data["task_status"])
183
224
 
184
- details = []
185
- for result in data["details"]:
225
+ # details = []
226
+ # for result in data["details"]:
186
227
  # Convert eval_tokens dict to TestEvalTokens object
187
- eval_tokens = TestEvalTokens(**result["eval_tokens"])
228
+ # eval_tokens = TestEvalTokens(**result["eval_tokens"])
188
229
 
189
230
  # Create a copy of the result dict and replace eval_tokens
190
- result_copy = dict(result)
191
- result_copy["eval_tokens"] = eval_tokens
231
+ # result_copy = dict(result["details"])
232
+ # result_copy["eval_tokens"] = eval_tokens
192
233
 
193
234
  # Create TestResult object
194
- test_result = TestResult(**result_copy)
195
- details.append(test_result)
235
+ # test_result = TestResult(**result_copy)
236
+ # details.append(test_result)
196
237
 
197
- return cls(details=details)
238
+ return cls(details=data["details"])
198
239
 
199
240
  def to_dict(self) -> Dict:
200
241
  return {
201
- "details": [
202
- {**result.to_dict(), "eval_tokens": result.eval_tokens.to_dict()}
203
- for result in self.details
204
- ]
242
+ # "details": [
243
+ # {**result.to_dict(), "eval_tokens": result.eval_tokens.to_dict()}
244
+ # for result in self.details
245
+ # ]
246
+ "details": self.details
205
247
  }
206
248
 
207
- def to_dataframe(self) -> pd.DataFrame:
208
- data = [
209
- {**result.to_dict(), "eval_tokens": result.eval_tokens.to_dict()}
210
- for result in self.details
211
- ]
212
- return pd.DataFrame(data)
249
+ # def to_dataframe(self) -> pd.DataFrame:
250
+ # data = [
251
+ # {**result.to_dict(), "eval_tokens": result.eval_tokens.to_dict()}
252
+ # for result in self.details
253
+ # ]
254
+ # return pd.DataFrame(data)
213
255
 
214
256
 
215
257
  @dataclass
@@ -330,7 +372,6 @@ class RedteamModelHealthResponse(BaseDTO):
330
372
  class RedTeamConfig(BaseDTO):
331
373
  test_name: str = "Test Name"
332
374
  dataset_name: str = "standard"
333
- model_saved_name: str = "gpt-4o-mini"
334
375
 
335
376
  redteam_test_configurations: RedTeamTestConfigurations = field(
336
377
  default_factory=RedTeamTestConfigurations
@@ -363,41 +404,33 @@ class RedTeamConfig(BaseDTO):
363
404
  )
364
405
 
365
406
 
366
- # @dataclass
367
- # class RedTeamConfigWithSavedModel(BaseDTO):
368
- # test_name: str = "Test Name"
369
- # dataset_name: str = "standard"
370
- # model_saved_name: str = "gpt-4o-mini"
371
-
372
- # redteam_test_configurations: RedTeamTestConfigurations = field(
373
- # default_factory=RedTeamTestConfigurations
374
- # )
375
- # target_model_configuration: TargetModelConfiguration = field(
376
- # default_factory=TargetModelConfiguration
377
- # )
378
-
379
- # _extra_fields: Dict[str, Any] = field(default_factory=dict)
380
-
381
- # def to_dict(self) -> dict:
382
- # d = asdict(self)
383
- # d["redteam_test_configurations"] = self.redteam_test_configurations.to_dict()
384
- # d["target_model_configuration"] = self.target_model_configuration.to_dict()
385
- # return d
386
-
387
- # @classmethod
388
- # def from_dict(cls, data: dict):
389
- # data = data.copy()
390
- # test_configs = RedTeamTestConfigurations.from_dict(
391
- # data.pop("redteam_test_configurations", {})
392
- # )
393
- # target_config = TargetModelConfiguration.from_dict(
394
- # data.pop("target_model_configuration", {})
395
- # )
396
- # return cls(
397
- # **data,
398
- # redteam_test_configurations=test_configs,
399
- # target_model_configuration=target_config,
400
- # )
407
+ @dataclass
408
+ class RedTeamConfigWithSavedModel(BaseDTO):
409
+ test_name: str = "Test Name"
410
+ dataset_name: str = "standard"
411
+ model_saved_name: str = "gpt-4o-mini"
412
+
413
+ redteam_test_configurations: RedTeamTestConfigurations = field(
414
+ default_factory=RedTeamTestConfigurations
415
+ )
416
+
417
+ _extra_fields: Dict[str, Any] = field(default_factory=dict)
418
+
419
+ def to_dict(self) -> dict:
420
+ d = asdict(self)
421
+ d["redteam_test_configurations"] = self.redteam_test_configurations.to_dict()
422
+ return d
423
+
424
+ @classmethod
425
+ def from_dict(cls, data: dict):
426
+ data = data.copy()
427
+ test_configs = RedTeamTestConfigurations.from_dict(
428
+ data.pop("redteam_test_configurations", {})
429
+ )
430
+ return cls(
431
+ **data,
432
+ redteam_test_configurations=test_configs,
433
+ )
401
434
 
402
435
 
403
436
  @dataclass
@@ -411,3 +444,4 @@ class RedTeamTaskList(BaseDTO):
411
444
 
412
445
  # Default configurations
413
446
  DEFAULT_REDTEAM_CONFIG = RedTeamConfig()
447
+ DEFAULT_REDTEAM_CONFIG_WITH_SAVED_MODEL = RedTeamConfigWithSavedModel()
enkryptai_sdk/red_team.py CHANGED
@@ -5,7 +5,7 @@ from .dto import (
5
5
  RedTeamModelHealthConfig,
6
6
  RedteamModelHealthResponse,
7
7
  RedTeamConfig,
8
- # RedTeamConfigWithSavedModel,
8
+ RedTeamConfigWithSavedModel,
9
9
  RedTeamResponse,
10
10
  RedTeamResultSummary,
11
11
  RedTeamResultDetails,
@@ -103,22 +103,7 @@ class RedTeamClient(BaseClient):
103
103
  },
104
104
  }
105
105
 
106
- saved_model = config.model_saved_name
107
- if saved_model:
108
- headers = {
109
- "X-Enkrypt-Model": saved_model,
110
- "Content-Type": "application/json",
111
- }
112
- response = self._request(
113
- "POST",
114
- "/redteam/v2/model/add-task",
115
- headers=headers,
116
- json=payload,
117
- )
118
- if response.get("error"):
119
- raise RedTeamClientError(response["error"])
120
- return RedTeamResponse.from_dict(response)
121
- elif config.target_model_configuration:
106
+ if config.target_model_configuration:
122
107
  payload["target_model_configuration"] = (
123
108
  config.target_model_configuration.to_dict()
124
109
  )
@@ -133,9 +118,48 @@ class RedTeamClient(BaseClient):
133
118
  return RedTeamResponse.from_dict(response)
134
119
  else:
135
120
  raise RedTeamClientError(
136
- "Please use a saved model or provide a target model configuration"
121
+ "Please provide a target model configuration"
137
122
  )
138
123
 
124
+ def add_task_with_saved_model(
125
+ self,
126
+ config: RedTeamConfigWithSavedModel,
127
+ model_saved_name: str,
128
+ ):
129
+ """
130
+ Add a new red teaming task using a saved model.
131
+ """
132
+ if not model_saved_name:
133
+ raise RedTeamClientError("Please provide a model_saved_name")
134
+
135
+ config = RedTeamConfigWithSavedModel.from_dict(config)
136
+ test_configs = config.redteam_test_configurations.to_dict()
137
+ # Remove None or empty test configurations
138
+ test_configs = {k: v for k, v in test_configs.items() if v is not None}
139
+
140
+ payload = {
141
+ # "async": config.async_enabled,
142
+ "dataset_name": config.dataset_name,
143
+ "test_name": config.test_name,
144
+ "redteam_test_configurations": {
145
+ k: v.to_dict() for k, v in test_configs.items()
146
+ },
147
+ }
148
+
149
+ headers = {
150
+ "X-Enkrypt-Model": model_saved_name,
151
+ "Content-Type": "application/json",
152
+ }
153
+ response = self._request(
154
+ "POST",
155
+ "/redteam/v2/model/add-task",
156
+ headers=headers,
157
+ json=payload,
158
+ )
159
+ if response.get("error"):
160
+ raise RedTeamClientError(response["error"])
161
+ return RedTeamResponse.from_dict(response)
162
+
139
163
  def status(self, task_id: str = None, test_name: str = None):
140
164
  """
141
165
  Get the status of a specific red teaming task.
@@ -253,6 +277,40 @@ class RedTeamClient(BaseClient):
253
277
  raise RedTeamClientError(response["error"])
254
278
  print(f"Response: {response}")
255
279
  return RedTeamResultSummary.from_dict(response)
280
+
281
+ def get_result_summary_test_type(self, task_id: str = None, test_name: str = None, test_type: str = None):
282
+ """
283
+ Get the summary of results for a specific red teaming task for a specific test type.
284
+
285
+ Args:
286
+ task_id (str, optional): The ID of the task to get results for
287
+ test_name (str, optional): The name of the test to get results for
288
+ test_type (str, optional): The type of test to get results for
289
+
290
+ Returns:
291
+ dict: The summary of the task results for the specified test type
292
+
293
+ Raises:
294
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
295
+ """
296
+ if not task_id and not test_name:
297
+ raise RedTeamClientError("Either task_id or test_name must be provided")
298
+
299
+ if not test_type:
300
+ raise RedTeamClientError("test_type must be provided")
301
+
302
+ headers = {}
303
+ if task_id:
304
+ headers["X-Enkrypt-Task-ID"] = task_id
305
+ if test_name:
306
+ headers["X-Enkrypt-Test-Name"] = test_name
307
+
308
+ url = f"/redteam/v2/results/summary/{test_type}"
309
+ response = self._request("GET", url, headers=headers)
310
+ if response.get("error"):
311
+ raise RedTeamClientError(response["error"])
312
+ print(f"Response: {response}")
313
+ return RedTeamResultSummary.from_dict(response)
256
314
 
257
315
  def get_result_details(self, task_id: str = None, test_name: str = None):
258
316
  """
@@ -277,13 +335,60 @@ class RedTeamClient(BaseClient):
277
335
  if test_name:
278
336
  headers["X-Enkrypt-Test-Name"] = test_name
279
337
 
280
- response = self._request("GET", "/redteam/results/details", headers=headers)
338
+ response = self._request("GET", "/redteam/v2/results/details", headers=headers)
281
339
  if response.get("error"):
282
340
  raise RedTeamClientError(response["error"])
283
341
  return RedTeamResultDetails.from_dict(response)
342
+
343
+ def get_result_details_test_type(self, task_id: str = None, test_name: str = None, test_type: str = None):
344
+ """
345
+ Get the detailed results for a specific red teaming task for a specific test type.
346
+
347
+ Args:
348
+ task_id (str, optional): The ID of the task to get detailed results for
349
+ test_name (str, optional): The name of the test to get detailed results for
350
+ test_type (str, optional): The type of test to get detailed results for
351
+
352
+ Returns:
353
+ dict: The detailed task results
354
+
355
+ Raises:
356
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
357
+ """
358
+ if not task_id and not test_name:
359
+ raise RedTeamClientError("Either task_id or test_name must be provided")
360
+
361
+ if not test_type:
362
+ raise RedTeamClientError("test_type must be provided")
363
+
364
+ headers = {}
365
+ if task_id:
366
+ headers["X-Enkrypt-Task-ID"] = task_id
367
+ if test_name:
368
+ headers["X-Enkrypt-Test-Name"] = test_name
369
+
370
+
371
+ url = f"/redteam/v2/results/details/{test_type}"
372
+ response = self._request("GET", url, headers=headers)
373
+ if response.get("error"):
374
+ raise RedTeamClientError(response["error"])
375
+ return RedTeamResultDetails.from_dict(response)
376
+
377
+ def get_task_list(self, status: str = None):
378
+ """
379
+ Get a list of red teaming tasks.
380
+
381
+ Args:
382
+ status (str, optional): The status of the tasks to retrieve
383
+
384
+ Returns:
385
+ dict: The list of tasks
386
+ """
387
+ url = "/redteam/list-tasks"
388
+ if status:
389
+ url += f"?status={status}"
284
390
 
285
- def get_task_list(self):
286
- response = self._request("GET", "/redteam/list-tasks")
391
+ response = self._request("GET", url)
287
392
  if response.get("error"):
288
393
  raise RedTeamClientError(response["error"])
289
394
  return RedTeamTaskList.from_dict(response)