pangea-sdk 6.5.0b1__py3-none-any.whl → 6.6.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/__init__.py +3 -11
- pangea/_constants.py +4 -0
- pangea/_typing.py +30 -0
- pangea/asyncio/__init__.py +2 -1
- pangea/asyncio/file_uploader.py +3 -2
- pangea/asyncio/request.py +66 -162
- pangea/asyncio/services/__init__.py +19 -3
- pangea/asyncio/services/ai_guard.py +23 -169
- pangea/asyncio/services/audit.py +1 -301
- pangea/asyncio/services/authn.py +25 -8
- pangea/asyncio/services/base.py +21 -6
- pangea/asyncio/services/file_scan.py +1 -1
- pangea/asyncio/services/intel.py +160 -95
- pangea/asyncio/services/prompt_guard.py +5 -112
- pangea/asyncio/services/redact.py +4 -265
- pangea/config.py +4 -2
- pangea/file_uploader.py +4 -1
- pangea/request.py +91 -166
- pangea/response.py +5 -1
- pangea/services/__init__.py +19 -3
- pangea/services/ai_guard.py +84 -694
- pangea/services/audit/audit.py +3 -301
- pangea/services/audit/models.py +1 -273
- pangea/services/audit/util.py +2 -0
- pangea/services/authn/authn.py +4 -5
- pangea/services/base.py +3 -0
- pangea/services/file_scan.py +3 -2
- pangea/services/intel.py +187 -252
- pangea/services/prompt_guard.py +5 -193
- pangea/services/redact.py +7 -473
- pangea/services/vault/vault.py +3 -0
- {pangea_sdk-6.5.0b1.dist-info → pangea_sdk-6.6.0.dist-info}/METADATA +17 -18
- pangea_sdk-6.6.0.dist-info/RECORD +62 -0
- pangea_sdk-6.6.0.dist-info/WHEEL +4 -0
- pangea/asyncio/services/management.py +0 -576
- pangea/services/management.py +0 -720
- pangea_sdk-6.5.0b1.dist-info/RECORD +0 -62
- pangea_sdk-6.5.0b1.dist-info/WHEEL +0 -4
pangea/__init__.py
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
__version__ = "6.
|
1
|
+
__version__ = "6.6.0"
|
2
2
|
|
3
|
-
from pangea.asyncio.request import PangeaRequestAsync
|
4
3
|
from pangea.config import PangeaConfig
|
5
4
|
from pangea.file_uploader import FileUploader
|
6
|
-
from pangea.
|
7
|
-
from pangea.response import PangeaResponse
|
5
|
+
from pangea.response import PangeaResponse, PangeaResponseResult, TransferMethod
|
8
6
|
|
9
|
-
__all__ = (
|
10
|
-
"FileUploader",
|
11
|
-
"PangeaConfig",
|
12
|
-
"PangeaRequest",
|
13
|
-
"PangeaRequestAsync",
|
14
|
-
"PangeaResponse",
|
15
|
-
)
|
7
|
+
__all__ = ("FileUploader", "PangeaConfig", "PangeaResponse", "PangeaResponseResult", "TransferMethod")
|
pangea/_constants.py
ADDED
pangea/_typing.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Iterator, Sequence
|
4
|
+
|
5
|
+
from typing_extensions import Any, Protocol, SupportsIndex, TypeVar, overload
|
6
|
+
|
7
|
+
T = TypeVar("T")
|
8
|
+
T_co = TypeVar("T_co", covariant=True)
|
9
|
+
|
10
|
+
|
11
|
+
class SequenceNotStr(Protocol[T_co]):
|
12
|
+
"""Sequence-like object that isn't str or bytes."""
|
13
|
+
|
14
|
+
@overload
|
15
|
+
def __getitem__(self, index: SupportsIndex, /) -> T_co: ...
|
16
|
+
|
17
|
+
@overload
|
18
|
+
def __getitem__(self, index: slice, /) -> Sequence[T_co]: ...
|
19
|
+
|
20
|
+
def __contains__(self, value: object, /) -> bool: ...
|
21
|
+
|
22
|
+
def __len__(self) -> int: ...
|
23
|
+
|
24
|
+
def __iter__(self) -> Iterator[T_co]: ...
|
25
|
+
|
26
|
+
def index(self, value: Any, start: int = ..., stop: int = ..., /) -> int: ...
|
27
|
+
|
28
|
+
def count(self, value: Any, /) -> int: ...
|
29
|
+
|
30
|
+
def __reversed__(self) -> Iterator[T_co]: ...
|
pangea/asyncio/__init__.py
CHANGED
pangea/asyncio/file_uploader.py
CHANGED
@@ -6,9 +6,10 @@ from __future__ import annotations
|
|
6
6
|
import io
|
7
7
|
import logging
|
8
8
|
|
9
|
+
from pangea import PangeaConfig, TransferMethod
|
9
10
|
from pangea.asyncio.request import PangeaRequestAsync
|
10
|
-
|
11
|
-
|
11
|
+
|
12
|
+
__all__ = ("FileUploaderAsync",)
|
12
13
|
|
13
14
|
|
14
15
|
class FileUploaderAsync:
|
pangea/asyncio/request.py
CHANGED
@@ -10,19 +10,23 @@ import asyncio
|
|
10
10
|
import json
|
11
11
|
import time
|
12
12
|
from collections.abc import Iterable, Mapping
|
13
|
-
from
|
13
|
+
from random import random
|
14
|
+
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
|
14
15
|
|
15
16
|
import aiohttp
|
16
17
|
from aiohttp import FormData
|
17
|
-
from pydantic import BaseModel
|
18
|
+
from pydantic import BaseModel
|
18
19
|
from pydantic_core import to_jsonable_python
|
19
|
-
from typing_extensions import Any,
|
20
|
+
from typing_extensions import Any, TypeAlias, TypeVar, override
|
20
21
|
|
21
22
|
import pangea.exceptions as pe
|
23
|
+
from pangea._constants import MAX_RETRY_DELAY, RETRYABLE_HTTP_CODES
|
22
24
|
from pangea.request import MultipartResponse, PangeaRequestBase
|
23
25
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
24
26
|
from pangea.utils import default_encoder
|
25
27
|
|
28
|
+
__all__ = ("PangeaRequestAsync",)
|
29
|
+
|
26
30
|
_FileName: TypeAlias = Union[str, None]
|
27
31
|
_FileContent: TypeAlias = Union[str, bytes]
|
28
32
|
_FileContentType: TypeAlias = str
|
@@ -50,24 +54,6 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
50
54
|
be set in PangeaConfig.
|
51
55
|
"""
|
52
56
|
|
53
|
-
async def delete(self, endpoint: str) -> None:
|
54
|
-
"""
|
55
|
-
Makes a DELETE call to a Pangea endpoint.
|
56
|
-
|
57
|
-
Args:
|
58
|
-
endpoint: The Pangea API endpoint.
|
59
|
-
"""
|
60
|
-
|
61
|
-
url = self._url(endpoint)
|
62
|
-
|
63
|
-
self.logger.debug(
|
64
|
-
json.dumps({"service": self.service, "action": "delete", "url": url}, default=default_encoder)
|
65
|
-
)
|
66
|
-
|
67
|
-
requests_response = await self._http_delete(url, headers=self._headers())
|
68
|
-
await self._check_http_errors(requests_response)
|
69
|
-
|
70
|
-
@overload
|
71
57
|
async def post(
|
72
58
|
self,
|
73
59
|
endpoint: str,
|
@@ -76,60 +62,18 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
76
62
|
files: Optional[List[Tuple]] = None,
|
77
63
|
poll_result: bool = True,
|
78
64
|
url: Optional[str] = None,
|
79
|
-
*,
|
80
|
-
pangea_response: Literal[True] = True,
|
81
65
|
) -> PangeaResponse[TResult]:
|
82
|
-
"""
|
83
|
-
Makes a POST call to a Pangea Service endpoint.
|
66
|
+
"""Makes the POST call to a Pangea Service endpoint.
|
84
67
|
|
85
68
|
Args:
|
86
|
-
endpoint: The Pangea Service API endpoint.
|
87
|
-
data: The POST body payload object
|
69
|
+
endpoint(str): The Pangea Service API endpoint.
|
70
|
+
data(dict): The POST body payload object
|
88
71
|
|
89
72
|
Returns:
|
90
73
|
PangeaResponse which contains the response in its entirety and
|
91
74
|
various properties to retrieve individual fields
|
92
75
|
"""
|
93
76
|
|
94
|
-
@overload
|
95
|
-
async def post(
|
96
|
-
self,
|
97
|
-
endpoint: str,
|
98
|
-
result_class: Type[TResult],
|
99
|
-
data: str | BaseModel | Mapping[str, Any] | None = None,
|
100
|
-
files: Optional[List[Tuple]] = None,
|
101
|
-
poll_result: bool = True,
|
102
|
-
url: Optional[str] = None,
|
103
|
-
*,
|
104
|
-
pangea_response: Literal[False],
|
105
|
-
) -> TResult:
|
106
|
-
"""
|
107
|
-
Makes a POST call to a Pangea Service endpoint.
|
108
|
-
|
109
|
-
Args:
|
110
|
-
endpoint: The Pangea Service API endpoint.
|
111
|
-
data: The POST body payload object
|
112
|
-
"""
|
113
|
-
|
114
|
-
async def post(
|
115
|
-
self,
|
116
|
-
endpoint: str,
|
117
|
-
result_class: Type[TResult],
|
118
|
-
data: str | BaseModel | Mapping[str, Any] | None = None,
|
119
|
-
files: Optional[List[Tuple]] = None,
|
120
|
-
poll_result: bool = True,
|
121
|
-
url: Optional[str] = None,
|
122
|
-
*,
|
123
|
-
pangea_response: bool = True,
|
124
|
-
) -> PangeaResponse[TResult] | TResult:
|
125
|
-
"""
|
126
|
-
Makes a POST call to a Pangea Service endpoint.
|
127
|
-
|
128
|
-
Args:
|
129
|
-
endpoint: The Pangea Service API endpoint.
|
130
|
-
data: The POST body payload object
|
131
|
-
"""
|
132
|
-
|
133
77
|
if isinstance(data, BaseModel):
|
134
78
|
data = data.model_dump(exclude_none=True)
|
135
79
|
|
@@ -169,13 +113,9 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
169
113
|
|
170
114
|
await self._check_http_errors(requests_response)
|
171
115
|
|
172
|
-
if not pangea_response:
|
173
|
-
type_adapter = TypeAdapter(result_class)
|
174
|
-
return type_adapter.validate_python(await requests_response.json())
|
175
|
-
|
176
116
|
if "multipart/form-data" in requests_response.headers.get("content-type", ""):
|
177
117
|
multipart_response = await self._process_multipart_response(requests_response)
|
178
|
-
|
118
|
+
pangea_response: PangeaResponse = PangeaResponse(
|
179
119
|
requests_response,
|
180
120
|
result_class=result_class,
|
181
121
|
json=multipart_response.pangea_json,
|
@@ -188,110 +128,49 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
188
128
|
json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
|
189
129
|
)
|
190
130
|
|
191
|
-
|
131
|
+
pangea_response = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
192
132
|
except aiohttp.ContentTypeError as e:
|
193
133
|
raise pe.PangeaException(
|
194
134
|
f"Failed to decode json response. {e}. Body: {await requests_response.text()}"
|
195
135
|
) from e
|
196
136
|
|
197
137
|
if poll_result:
|
198
|
-
|
138
|
+
pangea_response = await self._handle_queued_result(pangea_response)
|
199
139
|
|
200
|
-
return self._check_response(
|
140
|
+
return self._check_response(pangea_response)
|
201
141
|
|
202
|
-
|
203
|
-
|
204
|
-
self,
|
205
|
-
path: str,
|
206
|
-
result_class: Type[TResult],
|
207
|
-
check_response: bool = True,
|
208
|
-
*,
|
209
|
-
params: (
|
210
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
211
|
-
| None
|
212
|
-
) = None,
|
213
|
-
pangea_response: Literal[True] = True,
|
214
|
-
) -> PangeaResponse[TResult]:
|
215
|
-
"""
|
216
|
-
Makes the GET call to a Pangea Service endpoint.
|
142
|
+
async def get(self, path: str, result_class: Type[TResult], check_response: bool = True) -> PangeaResponse[TResult]:
|
143
|
+
"""Makes the GET call to a Pangea Service endpoint.
|
217
144
|
|
218
145
|
Args:
|
219
|
-
|
220
|
-
|
146
|
+
endpoint(str): The Pangea Service API endpoint.
|
147
|
+
path(str): Additional URL path
|
221
148
|
|
222
149
|
Returns:
|
223
150
|
PangeaResponse which contains the response in its entirety and
|
224
151
|
various properties to retrieve individual fields
|
225
152
|
"""
|
226
153
|
|
227
|
-
@overload
|
228
|
-
async def get(
|
229
|
-
self,
|
230
|
-
path: str,
|
231
|
-
result_class: Type[TResult],
|
232
|
-
check_response: bool = True,
|
233
|
-
*,
|
234
|
-
params: (
|
235
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
236
|
-
| None
|
237
|
-
) = None,
|
238
|
-
pangea_response: Literal[False] = False,
|
239
|
-
) -> TResult:
|
240
|
-
"""
|
241
|
-
Makes the GET call to a Pangea Service endpoint.
|
242
|
-
|
243
|
-
Args:
|
244
|
-
path: Additional URL path
|
245
|
-
params: Dictionary of querystring data to attach to the request
|
246
|
-
"""
|
247
|
-
|
248
|
-
async def get(
|
249
|
-
self,
|
250
|
-
path: str,
|
251
|
-
result_class: Type[TResult],
|
252
|
-
check_response: bool = True,
|
253
|
-
*,
|
254
|
-
params: (
|
255
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
256
|
-
| None
|
257
|
-
) = None,
|
258
|
-
pangea_response: bool = True,
|
259
|
-
) -> PangeaResponse[TResult] | TResult:
|
260
|
-
"""
|
261
|
-
Makes the GET call to a Pangea Service endpoint.
|
262
|
-
|
263
|
-
Args:
|
264
|
-
path: Additional URL path
|
265
|
-
params: Dictionary of querystring data to attach to the request
|
266
|
-
pangea_response: Whether or not the response body follows Pangea's
|
267
|
-
standard response schema
|
268
|
-
"""
|
269
|
-
|
270
154
|
url = self._url(path)
|
271
155
|
self.logger.debug(json.dumps({"service": self.service, "action": "get", "url": url}))
|
272
156
|
|
273
|
-
async with self.session.get(url,
|
157
|
+
async with self.session.get(url, headers=self._headers()) as requests_response:
|
274
158
|
await self._check_http_errors(requests_response)
|
275
|
-
|
276
|
-
if not pangea_response:
|
277
|
-
type_adapter = TypeAdapter(result_class)
|
278
|
-
return type_adapter.validate_python(await requests_response.json())
|
279
|
-
|
280
|
-
pangea_response_obj = PangeaResponse(
|
159
|
+
pangea_response = PangeaResponse(
|
281
160
|
requests_response, result_class=result_class, json=await requests_response.json()
|
282
161
|
)
|
283
162
|
|
284
163
|
self.logger.debug(
|
285
164
|
json.dumps(
|
286
|
-
{"service": self.service, "action": "get", "url": url, "response":
|
165
|
+
{"service": self.service, "action": "get", "url": url, "response": pangea_response.json},
|
287
166
|
default=default_encoder,
|
288
167
|
)
|
289
168
|
)
|
290
169
|
|
291
170
|
if check_response is False:
|
292
|
-
return
|
171
|
+
return pangea_response
|
293
172
|
|
294
|
-
return self._check_response(
|
173
|
+
return self._check_response(pangea_response)
|
295
174
|
|
296
175
|
async def _check_http_errors(self, resp: aiohttp.ClientResponse):
|
297
176
|
if resp.status == 503:
|
@@ -425,14 +304,6 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
425
304
|
attached_files = await self._get_attached_files(multipart_reader)
|
426
305
|
return MultipartResponse(pangea_json, attached_files) # type: ignore[arg-type]
|
427
306
|
|
428
|
-
async def _http_delete(
|
429
|
-
self,
|
430
|
-
url: str,
|
431
|
-
*,
|
432
|
-
headers: Mapping[str, str | bytes | None] = {},
|
433
|
-
) -> aiohttp.ClientResponse:
|
434
|
-
return await self.session.delete(url, headers=headers)
|
435
|
-
|
436
307
|
async def _http_post(
|
437
308
|
self,
|
438
309
|
url: str,
|
@@ -595,13 +466,46 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
595
466
|
|
596
467
|
@override
|
597
468
|
def _init_session(self) -> aiohttp.ClientSession:
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
469
|
+
return aiohttp.ClientSession(middlewares=[self._retry_middleware])
|
470
|
+
|
471
|
+
def _calculate_retry_timeout(self, remaining_retries: int) -> float:
|
472
|
+
max_retries = self.config.request_retries
|
473
|
+
nb_retries = min(max_retries - remaining_retries, 1000)
|
474
|
+
sleep_seconds = min(self.config.request_backoff * pow(2.0, nb_retries), MAX_RETRY_DELAY)
|
475
|
+
jitter = 1 - 0.25 * random()
|
476
|
+
timeout = sleep_seconds * jitter
|
477
|
+
return max(timeout, 0)
|
478
|
+
|
479
|
+
async def _retry_middleware(
|
480
|
+
self, request: aiohttp.ClientRequest, handler: aiohttp.ClientHandlerType
|
481
|
+
) -> aiohttp.ClientResponse:
|
482
|
+
max_retries = self.config.request_retries
|
483
|
+
request_ids = set[str]()
|
484
|
+
retries_taken = 0
|
485
|
+
for retries_taken in range(max_retries + 1):
|
486
|
+
remaining_retries = max_retries - retries_taken
|
487
|
+
|
488
|
+
if len(request_ids) > 0:
|
489
|
+
request.headers["X-Pangea-Retried-Request-Ids"] = ",".join(request_ids)
|
490
|
+
|
491
|
+
response = await handler(request)
|
492
|
+
|
493
|
+
request_id = response.headers.get("x-request-id")
|
494
|
+
if request_id:
|
495
|
+
request_ids.add(request_id)
|
496
|
+
|
497
|
+
if not response.ok and remaining_retries > 0 and self._should_retry(response):
|
498
|
+
await self._sleep_for_retry(retries_taken=retries_taken, max_retries=max_retries)
|
499
|
+
continue
|
500
|
+
|
501
|
+
break
|
502
|
+
|
503
|
+
return response
|
504
|
+
|
505
|
+
def _should_retry(self, response: aiohttp.ClientResponse) -> bool:
|
506
|
+
return response.status in RETRYABLE_HTTP_CODES
|
507
|
+
|
508
|
+
async def _sleep_for_retry(self, *, retries_taken: int, max_retries: int) -> None:
|
509
|
+
remaining_retries = max_retries - retries_taken
|
510
|
+
timeout = self._calculate_retry_timeout(remaining_retries)
|
511
|
+
await asyncio.sleep(timeout)
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# ruff: noqa: F401
|
2
|
-
|
3
1
|
from .ai_guard import AIGuardAsync
|
4
2
|
from .audit import AuditAsync
|
5
3
|
from .authn import AuthNAsync
|
@@ -7,9 +5,27 @@ from .authz import AuthZAsync
|
|
7
5
|
from .embargo import EmbargoAsync
|
8
6
|
from .file_scan import FileScanAsync
|
9
7
|
from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
|
10
|
-
from .management import ManagementAsync
|
11
8
|
from .prompt_guard import PromptGuardAsync
|
12
9
|
from .redact import RedactAsync
|
13
10
|
from .sanitize import SanitizeAsync
|
14
11
|
from .share import ShareAsync
|
15
12
|
from .vault import VaultAsync
|
13
|
+
|
14
|
+
__all__ = (
|
15
|
+
"AIGuardAsync",
|
16
|
+
"AuditAsync",
|
17
|
+
"AuthNAsync",
|
18
|
+
"AuthZAsync",
|
19
|
+
"DomainIntelAsync",
|
20
|
+
"EmbargoAsync",
|
21
|
+
"FileIntelAsync",
|
22
|
+
"FileScanAsync",
|
23
|
+
"IpIntelAsync",
|
24
|
+
"PromptGuardAsync",
|
25
|
+
"RedactAsync",
|
26
|
+
"SanitizeAsync",
|
27
|
+
"ShareAsync",
|
28
|
+
"UrlIntelAsync",
|
29
|
+
"UserIntelAsync",
|
30
|
+
"VaultAsync",
|
31
|
+
)
|
@@ -1,30 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from collections.abc import
|
4
|
-
from typing import
|
5
|
-
|
6
|
-
from typing_extensions import Literal, TypeVar
|
3
|
+
from collections.abc import Sequence
|
4
|
+
from typing import overload
|
7
5
|
|
8
6
|
from pangea.asyncio.services.base import ServiceBaseAsync
|
9
7
|
from pangea.config import PangeaConfig
|
10
8
|
from pangea.response import PangeaResponse
|
11
9
|
from pangea.services.ai_guard import (
|
12
|
-
AuditDataActivityConfig,
|
13
|
-
ConnectionsConfig,
|
14
|
-
ExtraInfo,
|
15
|
-
GuardResult,
|
16
10
|
LogFields,
|
11
|
+
McpToolsMessage,
|
17
12
|
Message,
|
18
13
|
Overrides,
|
19
|
-
RecipeConfig,
|
20
|
-
ServiceConfig,
|
21
|
-
ServiceConfigFilter,
|
22
|
-
ServiceConfigsPage,
|
23
14
|
TextGuardResult,
|
15
|
+
get_relevant_content,
|
16
|
+
patch_messages,
|
24
17
|
)
|
25
18
|
|
26
|
-
_T = TypeVar("_T")
|
27
|
-
|
28
19
|
|
29
20
|
class AIGuardAsync(ServiceBaseAsync):
|
30
21
|
"""AI Guard service client.
|
@@ -100,11 +91,12 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
100
91
|
async def guard_text(
|
101
92
|
self,
|
102
93
|
*,
|
103
|
-
messages: Sequence[Message],
|
94
|
+
messages: Sequence[Message | McpToolsMessage],
|
104
95
|
recipe: str | None = None,
|
105
96
|
debug: bool | None = None,
|
106
97
|
overrides: Overrides | None = None,
|
107
98
|
log_fields: LogFields | None = None,
|
99
|
+
only_relevant_content: bool = False,
|
108
100
|
) -> PangeaResponse[TextGuardResult]:
|
109
101
|
"""
|
110
102
|
Guard LLM input and output text
|
@@ -127,6 +119,8 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
127
119
|
recipe: Recipe key of a configuration of data types and settings
|
128
120
|
defined in the Pangea User Console. It specifies the rules that
|
129
121
|
are to be applied to the text, such as defang malicious URLs.
|
122
|
+
only_relevant_content: Whether or not to only send relevant content
|
123
|
+
to AI Guard.
|
130
124
|
|
131
125
|
Examples:
|
132
126
|
response = await ai_guard.guard_text(messages=[Message(role="user", content="hello world")])
|
@@ -136,11 +130,12 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
136
130
|
self,
|
137
131
|
text: str | None = None,
|
138
132
|
*,
|
139
|
-
messages: Sequence[Message] | None = None,
|
133
|
+
messages: Sequence[Message | McpToolsMessage] | None = None,
|
140
134
|
recipe: str | None = None,
|
141
135
|
debug: bool | None = None,
|
142
136
|
overrides: Overrides | None = None,
|
143
137
|
log_fields: LogFields | None = None,
|
138
|
+
only_relevant_content: bool = False,
|
144
139
|
) -> PangeaResponse[TextGuardResult]:
|
145
140
|
"""
|
146
141
|
Guard LLM input and output text
|
@@ -166,6 +161,8 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
166
161
|
recipe: Recipe key of a configuration of data types and settings
|
167
162
|
defined in the Pangea User Console. It specifies the rules that
|
168
163
|
are to be applied to the text, such as defang malicious URLs.
|
164
|
+
only_relevant_content: Whether or not to only send relevant content
|
165
|
+
to AI Guard.
|
169
166
|
|
170
167
|
Examples:
|
171
168
|
response = await ai_guard.guard_text("text")
|
@@ -174,7 +171,11 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
174
171
|
if text is not None and messages is not None:
|
175
172
|
raise ValueError("Exactly one of `text` or `messages` must be given")
|
176
173
|
|
177
|
-
|
174
|
+
if only_relevant_content and messages is not None:
|
175
|
+
original_messages = messages
|
176
|
+
messages, original_indices = get_relevant_content(messages)
|
177
|
+
|
178
|
+
response = await self.request.post(
|
178
179
|
"v1/text/guard",
|
179
180
|
TextGuardResult,
|
180
181
|
data={
|
@@ -187,156 +188,9 @@ class AIGuardAsync(ServiceBaseAsync):
|
|
187
188
|
},
|
188
189
|
)
|
189
190
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
recipe: str | None = None,
|
195
|
-
debug: bool | None = None,
|
196
|
-
overrides: Overrides | None = None,
|
197
|
-
app_id: str | None = None,
|
198
|
-
actor_id: str | None = None,
|
199
|
-
llm_provider: str | None = None,
|
200
|
-
model: str | None = None,
|
201
|
-
model_version: str | None = None,
|
202
|
-
request_token_count: int | None = None,
|
203
|
-
response_token_count: int | None = None,
|
204
|
-
source_ip: str | None = None,
|
205
|
-
source_location: str | None = None,
|
206
|
-
tenant_id: str | None = None,
|
207
|
-
event_type: Literal["input", "output"] | None = None,
|
208
|
-
sensor_instance_id: str | None = None,
|
209
|
-
extra_info: ExtraInfo | None = None,
|
210
|
-
count_tokens: bool | None = None,
|
211
|
-
) -> PangeaResponse[GuardResult]:
|
212
|
-
"""
|
213
|
-
Guard LLM input and output
|
214
|
-
|
215
|
-
Analyze and redact content to avoid manipulation of the model, addition
|
216
|
-
of malicious content, and other undesirable data transfers.
|
217
|
-
|
218
|
-
OperationId: ai_guard_post_v1beta_guard
|
219
|
-
|
220
|
-
Args:
|
221
|
-
input: 'messages' (required) contains Prompt content and role array
|
222
|
-
in JSON format. The `content` is the multimodal text or image
|
223
|
-
input that will be analyzed. Additional properties such as
|
224
|
-
'tools' may be provided for analysis.
|
225
|
-
recipe: Recipe key of a configuration of data types and settings defined in the Pangea User Console. It specifies the rules that are to be applied to the text, such as defang malicious URLs.
|
226
|
-
debug: Setting this value to true will provide a detailed analysis of the text data
|
227
|
-
app_name: Name of source application.
|
228
|
-
llm_provider: Underlying LLM. Example: 'OpenAI'.
|
229
|
-
model: Model used to perform the event. Example: 'gpt'.
|
230
|
-
model_version: Model version used to perform the event. Example: '3.5'.
|
231
|
-
request_token_count: Number of tokens in the request.
|
232
|
-
response_token_count: Number of tokens in the response.
|
233
|
-
source_ip: IP address of user or app or agent.
|
234
|
-
source_location: Location of user or app or agent.
|
235
|
-
tenant_id: For gateway-like integrations with multi-tenant support.
|
236
|
-
event_type: (AIDR) Event Type.
|
237
|
-
sensor_instance_id: (AIDR) sensor instance id.
|
238
|
-
extra_info: (AIDR) Logging schema.
|
239
|
-
count_tokens: Provide input and output token count.
|
240
|
-
"""
|
241
|
-
return await self.request.post(
|
242
|
-
"v1beta/guard",
|
243
|
-
GuardResult,
|
244
|
-
data={
|
245
|
-
"input": input,
|
246
|
-
"recipe": recipe,
|
247
|
-
"debug": debug,
|
248
|
-
"overrides": overrides,
|
249
|
-
"app_id": app_id,
|
250
|
-
"actor_id": actor_id,
|
251
|
-
"llm_provider": llm_provider,
|
252
|
-
"model": model,
|
253
|
-
"model_version": model_version,
|
254
|
-
"request_token_count": request_token_count,
|
255
|
-
"response_token_count": response_token_count,
|
256
|
-
"source_ip": source_ip,
|
257
|
-
"source_location": source_location,
|
258
|
-
"tenant_id": tenant_id,
|
259
|
-
"event_type": event_type,
|
260
|
-
"sensor_instance_id": sensor_instance_id,
|
261
|
-
"extra_info": extra_info,
|
262
|
-
"count_tokens": count_tokens,
|
263
|
-
},
|
264
|
-
)
|
265
|
-
|
266
|
-
async def get_service_config(self, id: str) -> PangeaResponse[ServiceConfig]:
|
267
|
-
"""
|
268
|
-
OperationId: ai_guard_post_v1beta_config
|
269
|
-
"""
|
270
|
-
return await self.request.post("v1beta/config", data={"id": id}, result_class=ServiceConfig)
|
191
|
+
if only_relevant_content and response.result and response.result.prompt_messages:
|
192
|
+
response.result.prompt_messages = patch_messages(
|
193
|
+
original_messages, original_indices, response.result.prompt_messages
|
194
|
+
) # type: ignore[assignment]
|
271
195
|
|
272
|
-
|
273
|
-
self,
|
274
|
-
name: str,
|
275
|
-
*,
|
276
|
-
id: str | None = None,
|
277
|
-
audit_data_activity: AuditDataActivityConfig | None = None,
|
278
|
-
connections: ConnectionsConfig | None = None,
|
279
|
-
recipes: Mapping[str, RecipeConfig] | None = None,
|
280
|
-
) -> PangeaResponse[ServiceConfig]:
|
281
|
-
"""
|
282
|
-
OperationId: ai_guard_post_v1beta_config_create
|
283
|
-
"""
|
284
|
-
return await self.request.post(
|
285
|
-
"v1beta/config/create",
|
286
|
-
data={
|
287
|
-
"name": name,
|
288
|
-
"id": id,
|
289
|
-
"audit_data_activity": audit_data_activity,
|
290
|
-
"connections": connections,
|
291
|
-
"recipes": recipes,
|
292
|
-
},
|
293
|
-
result_class=ServiceConfig,
|
294
|
-
)
|
295
|
-
|
296
|
-
async def update_service_config(
|
297
|
-
self,
|
298
|
-
id: str,
|
299
|
-
name: str,
|
300
|
-
*,
|
301
|
-
audit_data_activity: AuditDataActivityConfig | None = None,
|
302
|
-
connections: ConnectionsConfig | None = None,
|
303
|
-
recipes: Mapping[str, RecipeConfig] | None = None,
|
304
|
-
) -> PangeaResponse[ServiceConfig]:
|
305
|
-
"""
|
306
|
-
OperationId: ai_guard_post_v1beta_config_update
|
307
|
-
"""
|
308
|
-
return await self.request.post(
|
309
|
-
"v1beta/config/update",
|
310
|
-
data={
|
311
|
-
"id": id,
|
312
|
-
"name": name,
|
313
|
-
"audit_data_activity": audit_data_activity,
|
314
|
-
"connections": connections,
|
315
|
-
"recipes": recipes,
|
316
|
-
},
|
317
|
-
result_class=ServiceConfig,
|
318
|
-
)
|
319
|
-
|
320
|
-
async def delete_service_config(self, id: str) -> PangeaResponse[ServiceConfig]:
|
321
|
-
"""
|
322
|
-
OperationId: ai_guard_post_v1beta_config_delete
|
323
|
-
"""
|
324
|
-
return await self.request.post("v1beta/config/delete", data={"id": id}, result_class=ServiceConfig)
|
325
|
-
|
326
|
-
async def list_service_configs(
|
327
|
-
self,
|
328
|
-
*,
|
329
|
-
filter: ServiceConfigFilter | None = None,
|
330
|
-
last: str | None = None,
|
331
|
-
order: Literal["asc", "desc"] | None = None,
|
332
|
-
order_by: Literal["id", "created_at", "updated_at"] | None = None,
|
333
|
-
size: int | None = None,
|
334
|
-
) -> PangeaResponse[ServiceConfigsPage]:
|
335
|
-
"""
|
336
|
-
OperationId: ai_guard_post_v1beta_config_list
|
337
|
-
"""
|
338
|
-
return await self.request.post(
|
339
|
-
"v1beta/config/list",
|
340
|
-
data={"filter": filter, "last": last, "order": order, "order_by": order_by, "size": size},
|
341
|
-
result_class=ServiceConfigsPage,
|
342
|
-
)
|
196
|
+
return response
|