pangea-sdk 5.5.1__py3-none-any.whl → 6.1.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.
pangea/request.py CHANGED
@@ -14,6 +14,7 @@ from pydantic_core import to_jsonable_python
14
14
  from requests.adapters import HTTPAdapter, Retry
15
15
  from requests_toolbelt import MultipartDecoder # type: ignore[import-untyped]
16
16
  from typing_extensions import TypeVar
17
+ from yarl import URL
17
18
 
18
19
  import pangea
19
20
  import pangea.exceptions as pe
@@ -107,16 +108,8 @@ class PangeaRequestBase:
107
108
  return f"request/{request_id}"
108
109
 
109
110
  def _url(self, path: str) -> str:
110
- if self.config.domain.startswith("http://") or self.config.domain.startswith("https://"):
111
- # it's URL
112
- url = f"{self.config.domain}/{path}"
113
- else:
114
- schema = "http://" if self.config.insecure else "https://"
115
- domain = (
116
- self.config.domain if self.config.environment == "local" else f"{self.service}.{self.config.domain}"
117
- )
118
- url = f"{schema}{domain}/{path}"
119
- return url
111
+ url = URL(self.config.base_url_template.format(SERVICE_NAME=self.service))
112
+ return str(url / path)
120
113
 
121
114
  def _headers(self) -> dict:
122
115
  headers = {
@@ -124,7 +117,7 @@ class PangeaRequestBase:
124
117
  "Authorization": f"Bearer {self.token}",
125
118
  }
126
119
 
127
- # We want to ignore previous headers if user tryed to set them, so we will overwrite them.
120
+ # We want to ignore previous headers if user tried to set them, so we will overwrite them.
128
121
  self._extra_headers.update(headers)
129
122
  return self._extra_headers
130
123
 
@@ -628,9 +621,7 @@ class PangeaRequest(PangeaRequestBase):
628
621
  adapter = HTTPAdapter(max_retries=retry_config)
629
622
  session = requests.Session()
630
623
 
631
- if self.config.insecure:
632
- session.mount("http://", adapter)
633
- else:
634
- session.mount("https://", adapter)
624
+ session.mount("http://", adapter)
625
+ session.mount("https://", adapter)
635
626
 
636
627
  return session
@@ -1,11 +1,155 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict, Generic, List, Optional, TypeVar, overload
3
+ from typing import Any, Dict, Generic, List, Literal, Optional, TypeVar, overload
4
4
 
5
5
  from pangea.config import PangeaConfig
6
6
  from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
7
7
  from pangea.services.base import ServiceBase
8
8
 
9
+ # This is named "prompt injection" in the API spec even though it is also used
10
+ # for many other detectors.
11
+ PromptInjectionAction = Literal["report", "block"]
12
+
13
+ MaliciousEntityAction = Literal["report", "defang", "disabled", "block"]
14
+
15
+ # This is named "PII entity" in the API spec even though it is also used for the
16
+ # secrets detector.
17
+ PiiEntityAction = Literal["disabled", "report", "block", "mask", "partial_masking", "replacement", "hash", "fpe"]
18
+
19
+
20
+ class CodeDetectionOverride(APIRequestModel):
21
+ disabled: Optional[bool] = None
22
+ action: Optional[Literal["report", "block"]] = None
23
+
24
+
25
+ class LanguageDetectionOverride(APIRequestModel):
26
+ disabled: Optional[bool] = None
27
+ allow: Optional[List[str]] = None
28
+ block: Optional[List[str]] = None
29
+ report: Optional[List[str]] = None
30
+
31
+
32
+ class TopicDetectionOverride(APIRequestModel):
33
+ disabled: Optional[bool] = None
34
+ block: Optional[List[str]] = None
35
+
36
+
37
+ class PromptInjectionOverride(APIRequestModel):
38
+ disabled: Optional[bool] = None
39
+ action: Optional[PromptInjectionAction] = None
40
+
41
+
42
+ class SelfHarmOverride(APIRequestModel):
43
+ disabled: Optional[bool] = None
44
+ action: Optional[PromptInjectionAction] = None
45
+ threshold: Optional[float] = None
46
+
47
+
48
+ class GibberishOverride(APIRequestModel):
49
+ disabled: Optional[bool] = None
50
+ action: Optional[PromptInjectionAction] = None
51
+
52
+
53
+ class RoleplayOverride(APIRequestModel):
54
+ disabled: Optional[bool] = None
55
+ action: Optional[PromptInjectionAction] = None
56
+
57
+
58
+ class SentimentOverride(APIRequestModel):
59
+ disabled: Optional[bool] = None
60
+ action: Optional[PromptInjectionAction] = None
61
+ threshold: Optional[float] = None
62
+
63
+
64
+ class MaliciousEntityOverride(APIRequestModel):
65
+ disabled: Optional[bool] = None
66
+ ip_address: Optional[MaliciousEntityAction] = None
67
+ url: Optional[MaliciousEntityAction] = None
68
+ domain: Optional[MaliciousEntityAction] = None
69
+
70
+
71
+ class CompetitorsOverride(APIRequestModel):
72
+ disabled: Optional[bool] = None
73
+ action: Optional[PromptInjectionAction] = None
74
+
75
+
76
+ class PiiEntityOverride(APIRequestModel):
77
+ disabled: Optional[bool] = None
78
+ email_address: Optional[PiiEntityAction] = None
79
+ nrp: Optional[PiiEntityAction] = None
80
+ location: Optional[PiiEntityAction] = None
81
+ person: Optional[PiiEntityAction] = None
82
+ phone_number: Optional[PiiEntityAction] = None
83
+ date_time: Optional[PiiEntityAction] = None
84
+ ip_address: Optional[PiiEntityAction] = None
85
+ url: Optional[PiiEntityAction] = None
86
+ money: Optional[PiiEntityAction] = None
87
+ credit_card: Optional[PiiEntityAction] = None
88
+ crypto: Optional[PiiEntityAction] = None
89
+ iban_code: Optional[PiiEntityAction] = None
90
+ us_bank_number: Optional[PiiEntityAction] = None
91
+ nif: Optional[PiiEntityAction] = None
92
+ au_abn: Optional[PiiEntityAction] = None
93
+ au_acn: Optional[PiiEntityAction] = None
94
+ au_tfn: Optional[PiiEntityAction] = None
95
+ medical_license: Optional[PiiEntityAction] = None
96
+ uk_nhs: Optional[PiiEntityAction] = None
97
+ au_medicare: Optional[PiiEntityAction] = None
98
+ us_drivers_license: Optional[PiiEntityAction] = None
99
+ us_itin: Optional[PiiEntityAction] = None
100
+ us_passport: Optional[PiiEntityAction] = None
101
+ us_ssn: Optional[PiiEntityAction] = None
102
+
103
+
104
+ class SecretsDetectionOverride(APIRequestModel):
105
+ disabled: Optional[bool] = None
106
+ slack_token: Optional[PiiEntityAction] = None
107
+ rsa_private_key: Optional[PiiEntityAction] = None
108
+ ssh_dsa_private_key: Optional[PiiEntityAction] = None
109
+ ssh_ec_private_key: Optional[PiiEntityAction] = None
110
+ pgp_private_key_block: Optional[PiiEntityAction] = None
111
+ amazon_aws_access_key_id: Optional[PiiEntityAction] = None
112
+ amazon_aws_secret_access_key: Optional[PiiEntityAction] = None
113
+ amazon_mws_auth_token: Optional[PiiEntityAction] = None
114
+ facebook_access_token: Optional[PiiEntityAction] = None
115
+ github_access_token: Optional[PiiEntityAction] = None
116
+ jwt_token: Optional[PiiEntityAction] = None
117
+ google_api_key: Optional[PiiEntityAction] = None
118
+ google_cloud_platform_api_key: Optional[PiiEntityAction] = None
119
+ google_drive_api_key: Optional[PiiEntityAction] = None
120
+ google_cloud_platform_service_account: Optional[PiiEntityAction] = None
121
+ google_gmail_api_key: Optional[PiiEntityAction] = None
122
+ youtube_api_key: Optional[PiiEntityAction] = None
123
+ mailchimp_api_key: Optional[PiiEntityAction] = None
124
+ mailgun_api_key: Optional[PiiEntityAction] = None
125
+ basic_auth: Optional[PiiEntityAction] = None
126
+ picatic_api_key: Optional[PiiEntityAction] = None
127
+ slack_webhook: Optional[PiiEntityAction] = None
128
+ stripe_api_key: Optional[PiiEntityAction] = None
129
+ stripe_restricted_api_key: Optional[PiiEntityAction] = None
130
+ square_access_token: Optional[PiiEntityAction] = None
131
+ square_oauth_secret: Optional[PiiEntityAction] = None
132
+ twilio_api_key: Optional[PiiEntityAction] = None
133
+ pangea_token: Optional[PiiEntityAction] = None
134
+
135
+
136
+ class Overrides(APIRequestModel):
137
+ ignore_recipe: Optional[bool] = None
138
+ """Bypass existing Recipe content and create an on-the-fly Recipe."""
139
+
140
+ code_detection: Optional[CodeDetectionOverride] = None
141
+ competitors: Optional[CompetitorsOverride] = None
142
+ gibberish: Optional[GibberishOverride] = None
143
+ language_detection: Optional[LanguageDetectionOverride] = None
144
+ malicious_entity: Optional[MaliciousEntityOverride] = None
145
+ pii_entity: Optional[PiiEntityOverride] = None
146
+ prompt_injection: Optional[PromptInjectionOverride] = None
147
+ roleplay: Optional[RoleplayOverride] = None
148
+ secrets_detection: Optional[SecretsDetectionOverride] = None
149
+ selfharm: Optional[SelfHarmOverride] = None
150
+ sentiment: Optional[SentimentOverride] = None
151
+ topic_detection: Optional[TopicDetectionOverride] = None
152
+
9
153
 
10
154
  class LogFields(APIRequestModel):
11
155
  """Additional fields to include in activity log"""
@@ -33,6 +177,8 @@ class AnalyzerResponse(APIResponseModel):
33
177
 
34
178
  class PromptInjectionResult(APIResponseModel):
35
179
  action: str
180
+ """The action taken by this Detector"""
181
+
36
182
  analyzer_responses: List[AnalyzerResponse]
37
183
  """Triggered prompt injection analyzers."""
38
184
 
@@ -41,11 +187,13 @@ class PiiEntity(APIResponseModel):
41
187
  type: str
42
188
  value: str
43
189
  action: str
190
+ """The action taken on this Entity"""
44
191
  start_pos: Optional[int] = None
45
192
 
46
193
 
47
194
  class PiiEntityResult(APIResponseModel):
48
195
  entities: List[PiiEntity]
196
+ """Detected redaction rules."""
49
197
 
50
198
 
51
199
  class MaliciousEntity(APIResponseModel):
@@ -58,35 +206,59 @@ class MaliciousEntity(APIResponseModel):
58
206
 
59
207
  class MaliciousEntityResult(APIResponseModel):
60
208
  entities: List[MaliciousEntity]
209
+ """Detected harmful items."""
210
+
211
+
212
+ class CustomEntity(APIResponseModel):
213
+ type: str
214
+ value: str
215
+ action: str
216
+ """The action taken on this Entity"""
217
+ start_pos: Optional[int] = None
218
+ raw: Optional[Dict[str, Any]] = None
219
+
220
+
221
+ class CustomEntityResult(APIResponseModel):
222
+ entities: List[CustomEntity]
223
+ """Detected redaction rules."""
61
224
 
62
225
 
63
226
  class SecretsEntity(APIResponseModel):
64
227
  type: str
65
228
  value: str
66
229
  action: str
230
+ """The action taken on this Entity"""
67
231
  start_pos: Optional[int] = None
68
232
  redacted_value: Optional[str] = None
69
233
 
70
234
 
71
235
  class SecretsEntityResult(APIResponseModel):
72
236
  entities: List[SecretsEntity]
237
+ """Detected redaction rules."""
73
238
 
74
239
 
75
240
  class LanguageDetectionResult(APIResponseModel):
76
241
  language: str
77
242
  action: str
243
+ """The action taken by this Detector"""
244
+
245
+
246
+ class TopicDetectionResult(APIResponseModel):
247
+ action: str
248
+ """The action taken by this Detector"""
78
249
 
79
250
 
80
251
  class CodeDetectionResult(APIResponseModel):
81
252
  language: str
82
253
  action: str
254
+ """The action taken by this Detector"""
83
255
 
84
256
 
85
257
  _T = TypeVar("_T")
86
258
 
87
259
 
88
260
  class TextGuardDetector(APIResponseModel, Generic[_T]):
89
- detected: bool
261
+ detected: Optional[bool] = None
90
262
  data: Optional[_T] = None
91
263
 
92
264
 
@@ -94,10 +266,11 @@ class TextGuardDetectors(APIResponseModel):
94
266
  prompt_injection: Optional[TextGuardDetector[PromptInjectionResult]] = None
95
267
  pii_entity: Optional[TextGuardDetector[PiiEntityResult]] = None
96
268
  malicious_entity: Optional[TextGuardDetector[MaliciousEntityResult]] = None
269
+ custom_entity: Optional[TextGuardDetector[Any]] = None
97
270
  secrets_detection: Optional[TextGuardDetector[SecretsEntityResult]] = None
98
271
  profanity_and_toxicity: Optional[TextGuardDetector[Any]] = None
99
- custom_entity: Optional[TextGuardDetector[Any]] = None
100
272
  language_detection: Optional[TextGuardDetector[LanguageDetectionResult]] = None
273
+ topic_detection: Optional[TextGuardDetector[TopicDetectionResult]] = None
101
274
  code_detection: Optional[TextGuardDetector[CodeDetectionResult]] = None
102
275
 
103
276
 
@@ -112,6 +285,16 @@ class TextGuardResult(PangeaResponseResult, Generic[_T]):
112
285
  """Updated structured prompt, if applicable."""
113
286
 
114
287
  blocked: bool
288
+ """Whether or not the prompt triggered a block detection."""
289
+
290
+ recipe: str
291
+ """The Recipe that was used."""
292
+
293
+ fpe_context: Optional[str] = None
294
+ """
295
+ If an FPE redaction method returned results, this will be the context passed
296
+ to unredact.
297
+ """
115
298
 
116
299
 
117
300
  class AIGuard(ServiceBase):
@@ -160,7 +343,7 @@ class AIGuard(ServiceBase):
160
343
  *,
161
344
  recipe: str | None = None,
162
345
  debug: bool | None = None,
163
- llm_info: str | None = None,
346
+ overrides: Overrides | None = None,
164
347
  log_fields: LogFields | None = None,
165
348
  ) -> PangeaResponse[TextGuardResult[None]]:
166
349
  """
@@ -180,7 +363,6 @@ class AIGuard(ServiceBase):
180
363
  are to be applied to the text, such as defang malicious URLs.
181
364
  debug: Setting this value to true will provide a detailed analysis
182
365
  of the text data
183
- llm_info: Short string hint for the LLM Provider information
184
366
  log_field: Additional fields to include in activity log
185
367
 
186
368
  Examples:
@@ -194,7 +376,7 @@ class AIGuard(ServiceBase):
194
376
  messages: _T,
195
377
  recipe: str | None = None,
196
378
  debug: bool | None = None,
197
- llm_info: str | None = None,
379
+ overrides: Overrides | None = None,
198
380
  log_fields: LogFields | None = None,
199
381
  ) -> PangeaResponse[TextGuardResult[_T]]:
200
382
  """
@@ -215,59 +397,20 @@ class AIGuard(ServiceBase):
215
397
  are to be applied to the text, such as defang malicious URLs.
216
398
  debug: Setting this value to true will provide a detailed analysis
217
399
  of the text data
218
- llm_info: Short string hint for the LLM Provider information
219
400
  log_field: Additional fields to include in activity log
220
401
 
221
402
  Examples:
222
403
  response = ai_guard.guard_text(messages=[{"role": "user", "content": "hello world"}])
223
404
  """
224
405
 
225
- @overload
226
- def guard_text(
227
- self,
228
- *,
229
- llm_input: _T,
230
- recipe: str | None = None,
231
- debug: bool | None = None,
232
- llm_info: str | None = None,
233
- log_fields: LogFields | None = None,
234
- ) -> PangeaResponse[TextGuardResult[_T]]:
235
- """
236
- Text Guard for scanning LLM inputs and outputs
237
-
238
- Analyze and redact text to avoid manipulation of the model, addition of
239
- malicious content, and other undesirable data transfers.
240
-
241
- OperationId: ai_guard_post_v1_text_guard
242
-
243
- Args:
244
- llm_input: Structured full llm payload data to be scanned by AI
245
- Guard for PII, sensitive data, malicious content, and other data
246
- types defined by the configuration. Supports processing up to
247
- 10KB of JSON text
248
- recipe: Recipe key of a configuration of data types and settings
249
- defined in the Pangea User Console. It specifies the rules that
250
- are to be applied to the text, such as defang malicious URLs.
251
- debug: Setting this value to true will provide a detailed analysis
252
- of the text data
253
- llm_info: Short string hint for the LLM Provider information
254
- log_field: Additional fields to include in activity log
255
-
256
- Examples:
257
- response = ai_guard.guard_text(
258
- llm_input={"model": "gpt-4o", "messages": [{"role": "user", "content": "hello world"}]}
259
- )
260
- """
261
-
262
406
  def guard_text( # type: ignore[misc]
263
407
  self,
264
408
  text: str | None = None,
265
409
  *,
266
410
  messages: _T | None = None,
267
- llm_input: _T | None = None,
268
411
  recipe: str | None = None,
269
412
  debug: bool | None = None,
270
- llm_info: str | None = None,
413
+ overrides: Overrides | None = None,
271
414
  log_fields: LogFields | None = None,
272
415
  ) -> PangeaResponse[TextGuardResult[None]]:
273
416
  """
@@ -286,27 +429,19 @@ class AIGuard(ServiceBase):
286
429
  PII, sensitive data, malicious content, and other data types
287
430
  defined by the configuration. Supports processing up to 10KB of
288
431
  JSON text
289
- llm_input: Structured full llm payload data to be scanned by AI
290
- Guard for PII, sensitive data, malicious content, and other data
291
- types defined by the configuration. Supports processing up to
292
- 10KB of JSON text
293
432
  recipe: Recipe key of a configuration of data types and settings
294
433
  defined in the Pangea User Console. It specifies the rules that
295
434
  are to be applied to the text, such as defang malicious URLs.
296
435
  debug: Setting this value to true will provide a detailed analysis
297
436
  of the text data
298
- llm_info: Short string hint for the LLM Provider information
299
437
  log_field: Additional fields to include in activity log
300
438
 
301
439
  Examples:
302
440
  response = ai_guard.guard_text("text")
303
441
  """
304
442
 
305
- if not any((text, messages, llm_input)):
306
- raise ValueError("At least one of `text`, `messages`, or `llm_input` must be given")
307
-
308
- if sum((text is not None, messages is not None, llm_input is not None)) > 1:
309
- raise ValueError("Only one of `text`, `messages`, or `llm_input` can be given at once")
443
+ if text is not None and messages is not None:
444
+ raise ValueError("Exactly one of `text` or `messages` must be given")
310
445
 
311
446
  return self.request.post(
312
447
  "v1/text/guard",
@@ -314,10 +449,9 @@ class AIGuard(ServiceBase):
314
449
  data={
315
450
  "text": text,
316
451
  "messages": messages,
317
- "llm_input": llm_input,
318
452
  "recipe": recipe,
319
453
  "debug": debug,
320
- "llm_info": llm_info,
454
+ "overrides": overrides,
321
455
  "log_fields": log_fields,
322
456
  },
323
457
  )
@@ -408,10 +408,6 @@ class Audit(ServiceBase, AuditBase):
408
408
  config = PangeaConfig(domain="pangea_domain")
409
409
  audit = Audit(token="pangea_token", config=config)
410
410
  """
411
- # FIXME: Temporary check to deprecate config_id from PangeaConfig.
412
- # Delete it when deprecate PangeaConfig.config_id
413
- if config_id and config is not None and config.config_id is not None:
414
- config_id = config.config_id
415
411
  ServiceBase.__init__(self, token, config=config, logger_name=logger_name, config_id=config_id)
416
412
  AuditBase.__init__(
417
413
  self, private_key_file=private_key_file, public_key_info=public_key_info, tenant_id=tenant_id