enkryptai-sdk 0.1.6__py3-none-any.whl → 1.0.0__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/models.py CHANGED
@@ -1,6 +1,6 @@
1
- import urllib3
2
- from .dto import ModelConfig, ModelResponse
1
+ from .base import BaseClient
3
2
  from urllib.parse import urlparse, urlsplit
3
+ from .dto import ModelConfig, ModelResponse, ModelCollection
4
4
 
5
5
 
6
6
  class ModelClientError(Exception):
@@ -11,41 +11,11 @@ class ModelClientError(Exception):
11
11
  pass
12
12
 
13
13
 
14
- class ModelClient:
14
+ class ModelClient(BaseClient):
15
15
  def __init__(self, api_key: str, base_url: str = "https://api.enkryptai.com:443"):
16
- self.api_key = api_key
17
- self.base_url = base_url
18
- self.http = urllib3.PoolManager()
19
- self.headers = {"apikey": self.api_key}
20
-
21
- def _request(self, method, endpoint, payload=None, headers=None, **kwargs):
22
- url = self.base_url + endpoint
23
- request_headers = {
24
- "Accept-Encoding": "gzip", # Add required gzip encoding
25
- **self.headers,
26
- }
27
- if headers:
28
- request_headers.update(headers)
29
-
30
- try:
31
- response = self.http.request(method, url, headers=request_headers, **kwargs)
32
-
33
- if response.status >= 400:
34
- error_data = (
35
- response.json()
36
- if response.data
37
- else {"message": f"HTTP {response.status}"}
38
- )
39
- error_message = error_data.get("message", str(error_data))
40
- raise urllib3.exceptions.HTTPError(error_message)
41
- return response.json()
42
- except urllib3.exceptions.HTTPError as e:
43
- return {"error": str(e)}
44
-
45
- def health(self):
46
- return self._request("GET", "/models/health")
16
+ super().__init__(api_key, base_url)
47
17
 
48
- def add_model(self, config: ModelConfig):
18
+ def add_model(self, config: ModelConfig) -> ModelResponse:
49
19
  """
50
20
  Add a new model configuration to the system.
51
21
 
@@ -69,11 +39,15 @@ class ModelClient:
69
39
  base_path = ""
70
40
  remaining_path = ""
71
41
 
72
- # Determine paths based on the endpoint
73
- paths = {
74
- "completions": f"/{remaining_path}" if remaining_path else "",
75
- "chat": "",
76
- }
42
+ if config.model_config.paths:
43
+ paths = config.model_config.paths.to_dict()
44
+ else:
45
+ paths = {
46
+ "completions": (
47
+ f"/{remaining_path.split('/')[-1]}" if remaining_path else ""
48
+ ),
49
+ "chat": f"/{remaining_path}" if remaining_path else "",
50
+ }
77
51
 
78
52
  payload = {
79
53
  "model_saved_name": config.model_saved_name,
@@ -82,11 +56,10 @@ class ModelClient:
82
56
  "model_type": config.model_type,
83
57
  "certifications": config.certifications,
84
58
  "model_config": {
85
- "is_compatible_with": config.model_config.is_compatible_with,
59
+ "model_provider": config.model_config.model_provider,
86
60
  "model_version": config.model_config.model_version,
87
61
  "hosting_type": config.model_config.hosting_type,
88
62
  "model_source": config.model_config.model_source,
89
- "model_provider": config.model_config.model_provider,
90
63
  "system_prompt": config.model_config.system_prompt,
91
64
  "conversation_template": config.model_config.conversation_template,
92
65
  "endpoint": {
@@ -94,7 +67,7 @@ class ModelClient:
94
67
  "host": parsed_url.hostname,
95
68
  "port": parsed_url.port
96
69
  or (443 if parsed_url.scheme == "https" else 80),
97
- "base_path": f"/{base_path}/{paths['completions']}", # Just v1
70
+ "base_path": f"/{base_path}", # Just v1
98
71
  },
99
72
  "paths": paths,
100
73
  "auth_data": {
@@ -118,18 +91,19 @@ class ModelClient:
118
91
  except Exception as e:
119
92
  raise ModelClientError(str(e))
120
93
 
121
- def get_model(self, model_id: str) -> ModelConfig:
94
+ def get_model(self, model_saved_name: str) -> ModelConfig:
122
95
  """
123
- Get model configuration by model ID.
96
+ Get model configuration by model saved name.
124
97
 
125
98
  Args:
126
- model_id (str): ID of the model to retrieve
99
+ model_saved_name (str): Saved name of the model to retrieve
127
100
 
128
101
  Returns:
129
102
  ModelConfig: Configuration object containing model details
130
103
  """
131
- headers = {"X-Enkrypt-Model": model_id}
104
+ headers = {"X-Enkrypt-Model": model_saved_name}
132
105
  response = self._request("GET", "/models/get-model", headers=headers)
106
+ print(response)
133
107
  if response.get("error"):
134
108
  raise ModelClientError(response["error"])
135
109
  return ModelConfig.from_dict(response)
@@ -142,19 +116,111 @@ class ModelClient:
142
116
  dict: Response from the API containing the list of models
143
117
  """
144
118
  try:
145
- return self._request("GET", "/models/list-models")
119
+ response = self._request("GET", "/models/list-models")
120
+ if response.get("error"):
121
+ raise ModelClientError(response["error"])
122
+ return ModelCollection.from_dict(response)
146
123
  except Exception as e:
147
124
  return {"error": str(e)}
148
125
 
149
- def delete_model(self, model_id: str):
126
+ def modify_model(self, config: ModelConfig, old_model_saved_name=None) -> ModelResponse:
127
+ """
128
+ Modify an existing model in the system.
129
+
130
+ Args:
131
+ model_saved_name (str): The saved name of the model to modify
132
+ config (ModelConfig): Configuration object containing model details
133
+
134
+ Returns:
135
+ dict: Response from the API containing the modified model details
136
+ """
137
+ if old_model_saved_name is None:
138
+ old_model_saved_name = config["model_saved_name"]
139
+
140
+ headers = {"Content-Type": "application/json", "X-Enkrypt-Model": old_model_saved_name}
141
+ # print(config)
142
+ config = ModelConfig.from_dict(config)
143
+ # Parse endpoint_url into components
144
+ parsed_url = urlparse(config.model_config.endpoint_url)
145
+ path_parts = parsed_url.path.strip("/").split("/")
146
+
147
+ # Extract base_path and endpoint path
148
+ if len(path_parts) >= 1:
149
+ base_path = path_parts[0] # Usually 'v1'
150
+ remaining_path = "/".join(path_parts[1:]) # The rest of the path
151
+ else:
152
+ base_path = ""
153
+ remaining_path = ""
154
+
155
+ if config.model_config.paths:
156
+ paths = config.model_config.paths.to_dict()
157
+ else:
158
+ # Determine paths based on the endpoint
159
+ paths = {
160
+ "completions": (
161
+ f"/{remaining_path.split('/')[-1]}" if remaining_path else ""
162
+ ),
163
+ "chat": f"/{remaining_path}" if remaining_path else "",
164
+ }
165
+
166
+ payload = {
167
+ "model_saved_name": config.model_saved_name,
168
+ "testing_for": config.testing_for,
169
+ "model_name": config.model_name,
170
+ "model_type": config.model_type,
171
+ "certifications": config.certifications,
172
+ "model_config": {
173
+ "model_provider": config.model_config.model_provider,
174
+ "model_version": config.model_config.model_version,
175
+ "hosting_type": config.model_config.hosting_type,
176
+ "model_source": config.model_config.model_source,
177
+ "system_prompt": config.model_config.system_prompt,
178
+ "conversation_template": config.model_config.conversation_template,
179
+ "endpoint": {
180
+ "scheme": parsed_url.scheme,
181
+ "host": parsed_url.hostname,
182
+ "port": parsed_url.port
183
+ or (443 if parsed_url.scheme == "https" else 80),
184
+ "base_path": f"/{base_path}", # Just v1
185
+ },
186
+ "paths": paths,
187
+ "auth_data": {
188
+ "header_name": config.model_config.auth_data.header_name,
189
+ "header_prefix": config.model_config.auth_data.header_prefix,
190
+ "space_after_prefix": config.model_config.auth_data.space_after_prefix,
191
+ },
192
+ "apikeys": (
193
+ [config.model_config.apikey] if config.model_config.apikey else []
194
+ ),
195
+ "default_request_options": config.model_config.default_request_options,
196
+ },
197
+ }
198
+ try:
199
+ response = self._request(
200
+ "PATCH", "/models/modify-model", headers=headers, json=payload
201
+ )
202
+ if response.get("error"):
203
+ raise ModelClientError(response["error"])
204
+ return ModelResponse.from_dict(response)
205
+ except Exception as e:
206
+ raise ModelClientError(str(e))
207
+
208
+ def delete_model(self, model_saved_name: str) -> ModelResponse:
150
209
  """
151
210
  Delete a specific model from the system.
152
211
 
153
212
  Args:
154
- model_id (str): The identifier or name of the model to delete
213
+ model_saved_name (str): The saved name of the model to delete
155
214
 
156
215
  Returns:
157
216
  dict: Response from the API containing the deletion status
158
217
  """
159
- headers = {"X-Enkrypt-Model": model_id}
160
- return self._request("DELETE", "/models/delete-model", headers=headers)
218
+ headers = {"X-Enkrypt-Model": model_saved_name}
219
+
220
+ try:
221
+ response = self._request("DELETE", "/models/delete-model", headers=headers)
222
+ if response.get("error"):
223
+ raise ModelClientError(response["error"])
224
+ return ModelResponse.from_dict(response)
225
+ except Exception as e:
226
+ raise ModelClientError(str(e))
enkryptai_sdk/red_team.py CHANGED
@@ -1,11 +1,17 @@
1
1
  import urllib3
2
+ from .base import BaseClient
2
3
  from .dto import (
4
+ RedteamHealthResponse,
5
+ RedTeamModelHealthConfig,
6
+ RedteamModelHealthResponse,
3
7
  RedTeamConfig,
8
+ RedTeamConfigWithSavedModel,
4
9
  RedTeamResponse,
5
10
  RedTeamResultSummary,
6
11
  RedTeamResultDetails,
7
12
  RedTeamTaskStatus,
8
13
  RedTeamTaskDetails,
14
+ RedTeamTaskList,
9
15
  )
10
16
 
11
17
 
@@ -17,47 +23,64 @@ class RedTeamClientError(Exception):
17
23
  pass
18
24
 
19
25
 
20
- class RedTeamClient:
26
+ class RedTeamClient(BaseClient):
21
27
  """
22
28
  A client for interacting with the Red Team API.
23
29
  """
24
30
 
25
31
  def __init__(self, api_key: str, base_url: str = "https://api.enkryptai.com"):
26
- self.api_key = api_key
27
- self.base_url = base_url
28
- self.http = urllib3.PoolManager()
29
- self.headers = {"apikey": self.api_key}
30
-
31
- def _request(self, method, endpoint, headers=None, **kwargs):
32
- url = self.base_url + endpoint
33
- request_headers = {
34
- "Accept-Encoding": "gzip", # Add required gzip encoding
35
- **self.headers,
36
- }
37
- if headers:
38
- request_headers.update(headers)
32
+ super().__init__(api_key, base_url)
33
+
34
+ # def get_model(self, model):
35
+ # models = self._request("GET", "/models/list-models")
36
+ # models = models["models"]
37
+ # for _model_data in models:
38
+ # if _model_data["model_saved_name"] == model:
39
+ # return _model_data["model_saved_name"]
40
+ # else:
41
+ # return None
39
42
 
43
+ def get_health(self):
44
+ """
45
+ Get the health status of the service.
46
+ """
40
47
  try:
41
- response = self.http.request(method, url, headers=request_headers, **kwargs)
42
-
43
- if response.status >= 400:
44
- error_data = (
45
- response.json()
46
- if response.data
47
- else {"message": f"HTTP {response.status}"}
48
- )
49
- error_message = error_data.get("message", str(error_data))
50
- raise urllib3.exceptions.HTTPError(error_message)
51
- return response.json()
52
- except urllib3.exceptions.HTTPError as e:
53
- return {"error": str(e)}
54
-
55
- def get_model(self, model):
56
- models = self._request("GET", "/models/list-models")
57
- if model in models:
58
- return model
59
- else:
60
- return None
48
+ response = self._request("GET", "/redteam/health")
49
+ if response.get("error"):
50
+ raise RedTeamClientError(response["error"])
51
+ return RedteamHealthResponse.from_dict(response)
52
+ except Exception as e:
53
+ raise RedTeamClientError(str(e))
54
+
55
+ def check_model_health(self, config: RedTeamModelHealthConfig):
56
+ """
57
+ Get the health status of a model.
58
+ """
59
+ try:
60
+ config = RedTeamModelHealthConfig.from_dict(config)
61
+ response = self._request("POST", "/redteam/model-health", json=config.to_dict())
62
+ # if response.get("error"):
63
+ if response.get("error") not in [None, ""]:
64
+ raise RedTeamClientError(response["error"])
65
+ return RedteamModelHealthResponse.from_dict(response)
66
+ except Exception as e:
67
+ raise RedTeamClientError(str(e))
68
+
69
+ def check_saved_model_health(self, model_saved_name: str):
70
+ """
71
+ Get the health status of a saved model.
72
+ """
73
+ try:
74
+ headers = {
75
+ "X-Enkrypt-Model": model_saved_name,
76
+ }
77
+ response = self._request("POST", "/redteam/model/model-health", headers=headers)
78
+ # if response.get("error"):
79
+ if response.get("error") not in [None, ""]:
80
+ raise RedTeamClientError(response["error"])
81
+ return RedteamModelHealthResponse.from_dict(response)
82
+ except Exception as e:
83
+ raise RedTeamClientError(str(e))
61
84
 
62
85
  def add_task(
63
86
  self,
@@ -75,26 +98,12 @@ class RedTeamClient:
75
98
  # "async": config.async_enabled,
76
99
  "dataset_name": config.dataset_name,
77
100
  "test_name": config.test_name,
78
- "redteam_test_configurations": test_configs,
101
+ "redteam_test_configurations": {
102
+ k: v.to_dict() for k, v in test_configs.items()
103
+ },
79
104
  }
80
105
 
81
- model = config.model_name
82
- saved_model = self.get_model(model)
83
-
84
- if saved_model:
85
- print("saved model found")
86
- headers = {
87
- "X-Enkrypt-Model": saved_model,
88
- "Content-Type": "application/json",
89
- }
90
- payload["location"] = {"storage": "supabase", "container_name": "supabase"}
91
- return self._request(
92
- "POST",
93
- "/redteam/v2/model/add-task",
94
- headers=headers,
95
- json=payload,
96
- )
97
- elif config.target_model_configuration:
106
+ if config.target_model_configuration:
98
107
  payload["target_model_configuration"] = (
99
108
  config.target_model_configuration.to_dict()
100
109
  )
@@ -109,87 +118,277 @@ class RedTeamClient:
109
118
  return RedTeamResponse.from_dict(response)
110
119
  else:
111
120
  raise RedTeamClientError(
112
- "Please use a saved model or provide a target model configuration"
121
+ "Please provide a target model configuration"
113
122
  )
114
123
 
115
- def status(self, task_id: str):
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
+
163
+ def status(self, task_id: str = None, test_name: str = None):
116
164
  """
117
165
  Get the status of a specific red teaming task.
118
166
 
119
167
  Args:
120
- task_id (str): The ID of the task to check status
168
+ task_id (str, optional): The ID of the task to check status
169
+ test_name (str, optional): The name of the test to check status
121
170
 
122
171
  Returns:
123
172
  dict: The task status information
173
+
174
+ Raises:
175
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
124
176
  """
125
- headers = {"X-Enkrypt-Task-ID": task_id}
177
+ if not task_id and not test_name:
178
+ raise RedTeamClientError("Either task_id or test_name must be provided")
179
+
180
+ headers = {}
181
+ if task_id:
182
+ headers["X-Enkrypt-Task-ID"] = task_id
183
+ if test_name:
184
+ headers["X-Enkrypt-Test-Name"] = test_name
126
185
 
127
186
  response = self._request("GET", "/redteam/task-status", headers=headers)
128
187
  if response.get("error"):
129
188
  raise RedTeamClientError(response["error"])
130
189
  return RedTeamTaskStatus.from_dict(response)
131
190
 
132
- def cancel_task(self, task_id: str):
191
+ def cancel_task(self, task_id: str = None, test_name: str = None):
133
192
  """
134
193
  Cancel a specific red teaming task.
135
194
 
136
195
  Args:
137
- task_id (str): The ID of the task to cancel
196
+ task_id (str, optional): The ID of the task to cancel
197
+ test_name (str, optional): The name of the test to cancel
198
+
199
+ Returns:
200
+ dict: The cancellation response
201
+
202
+ Raises:
203
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
138
204
  """
139
205
  raise RedTeamClientError(
140
206
  "This feature is currently under development. Please check our documentation "
141
207
  "at https://docs.enkrypt.ai for updates or contact support@enkrypt.ai for assistance."
142
208
  )
143
209
 
144
- def get_task(self, task_id: str):
210
+ if not task_id and not test_name:
211
+ raise RedTeamClientError("Either task_id or test_name must be provided")
212
+
213
+ headers = {}
214
+ if task_id:
215
+ headers["X-Enkrypt-Task-ID"] = task_id
216
+ if test_name:
217
+ headers["X-Enkrypt-Test-Name"] = test_name
218
+
219
+ response = self._request("POST", "/redteam/cancel-task", headers=headers)
220
+ if response.get("error"):
221
+ raise RedTeamClientError(response["error"])
222
+ return response
223
+
224
+ def get_task(self, task_id: str = None, test_name: str = None):
145
225
  """
146
226
  Get the status and details of a specific red teaming task.
147
227
 
148
228
  Args:
149
- task_id (str): The ID of the task to retrieve
229
+ task_id (str, optional): The ID of the task to retrieve
230
+ test_name (str, optional): The name of the test to retrieve
150
231
 
151
232
  Returns:
152
233
  dict: The task details and status
234
+
235
+ Raises:
236
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
153
237
  """
154
- headers = {"X-Enkrypt-Task-ID": task_id}
238
+ if not task_id and not test_name:
239
+ raise RedTeamClientError("Either task_id or test_name must be provided")
240
+
241
+ headers = {}
242
+ if task_id:
243
+ headers["X-Enkrypt-Task-ID"] = task_id
244
+ if test_name:
245
+ headers["X-Enkrypt-Test-Name"] = test_name
155
246
 
156
247
  response = self._request("GET", "/redteam/get-task", headers=headers)
157
248
  if response.get("error"):
158
249
  raise RedTeamClientError(response["error"])
159
- if response.get("data").get("job_id "):
160
- response["data"]["task_id"] = response["data"].pop("job_id")
161
250
  return RedTeamTaskDetails.from_dict(response["data"])
162
251
 
163
- def get_result_summary(self, task_id: str):
252
+ def get_result_summary(self, task_id: str = None, test_name: str = None):
164
253
  """
165
254
  Get the summary of results for a specific red teaming task.
166
255
 
167
256
  Args:
168
- task_id (str): The ID of the task to get results for
257
+ task_id (str, optional): The ID of the task to get results for
258
+ test_name (str, optional): The name of the test to get results for
169
259
 
170
260
  Returns:
171
261
  dict: The summary of the task results
262
+
263
+ Raises:
264
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
172
265
  """
173
- headers = {"X-Enkrypt-Task-ID": task_id}
266
+ if not task_id and not test_name:
267
+ raise RedTeamClientError("Either task_id or test_name must be provided")
268
+
269
+ headers = {}
270
+ if task_id:
271
+ headers["X-Enkrypt-Task-ID"] = task_id
272
+ if test_name:
273
+ headers["X-Enkrypt-Test-Name"] = test_name
174
274
 
175
275
  response = self._request("GET", "/redteam/results/summary", headers=headers)
176
276
  if response.get("error"):
177
277
  raise RedTeamClientError(response["error"])
178
- return RedTeamResultSummary.from_dict(response["summary"])
278
+ print(f"Response: {response}")
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)
179
314
 
180
- def get_result_details(self, task_id: str):
315
+ def get_result_details(self, task_id: str = None, test_name: str = None):
181
316
  """
182
317
  Get the detailed results for a specific red teaming task.
183
318
 
184
319
  Args:
185
- task_id (str): The ID of the task to get detailed results for
320
+ task_id (str, optional): The ID of the task to get detailed results for
321
+ test_name (str, optional): The name of the test to get detailed results for
186
322
 
187
323
  Returns:
188
324
  dict: The detailed task results
325
+
326
+ Raises:
327
+ RedTeamClientError: If neither task_id nor test_name is provided, or if there's an error from the API
189
328
  """
190
- # TODO: Update the response to be updated
191
- headers = {"X-Enkrypt-Task-ID": task_id}
192
- response = self._request("GET", "/redteam/results/details", headers=headers)
329
+ if not task_id and not test_name:
330
+ raise RedTeamClientError("Either task_id or test_name must be provided")
331
+
332
+ headers = {}
333
+ if task_id:
334
+ headers["X-Enkrypt-Task-ID"] = task_id
335
+ if test_name:
336
+ headers["X-Enkrypt-Test-Name"] = test_name
337
+
338
+ response = self._request("GET", "/redteam/v2/results/details", headers=headers)
339
+ if response.get("error"):
340
+ raise RedTeamClientError(response["error"])
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}"
390
+
391
+ response = self._request("GET", url)
193
392
  if response.get("error"):
194
393
  raise RedTeamClientError(response["error"])
195
- return RedTeamResultDetails.from_dict(response["details"])
394
+ return RedTeamTaskList.from_dict(response)