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/services/ai_guard.py
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from collections.abc import
|
4
|
-
from typing import
|
5
|
-
|
6
|
-
from pydantic import BaseModel, ConfigDict, Field, RootModel
|
7
|
-
from typing_extensions import TypeAlias, TypedDict, TypeVar
|
3
|
+
from collections.abc import Sequence
|
4
|
+
from typing import Any, Generic, Literal, Optional, overload
|
8
5
|
|
6
|
+
from pangea._typing import T
|
9
7
|
from pangea.config import PangeaConfig
|
10
|
-
from pangea.response import APIRequestModel, APIResponseModel,
|
8
|
+
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
|
11
9
|
from pangea.services.base import ServiceBase
|
12
10
|
|
13
11
|
# This is named "prompt injection" in the API spec even though it is also used
|
@@ -22,10 +20,15 @@ PiiEntityAction = Literal["disabled", "report", "block", "mask", "partial_maskin
|
|
22
20
|
|
23
21
|
|
24
22
|
class Message(APIRequestModel):
|
25
|
-
role: str
|
23
|
+
role: Optional[str] = None
|
26
24
|
content: str
|
27
25
|
|
28
26
|
|
27
|
+
class McpToolsMessage(APIRequestModel):
|
28
|
+
role: Literal["tools"]
|
29
|
+
content: list[dict[str, Any]]
|
30
|
+
|
31
|
+
|
29
32
|
class CodeDetectionOverride(APIRequestModel):
|
30
33
|
disabled: Optional[bool] = None
|
31
34
|
action: Optional[Literal["report", "block"]] = None
|
@@ -277,12 +280,9 @@ class CodeDetectionResult(APIResponseModel):
|
|
277
280
|
"""The action taken by this Detector"""
|
278
281
|
|
279
282
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
class TextGuardDetector(APIResponseModel, Generic[_T]):
|
283
|
+
class TextGuardDetector(APIResponseModel, Generic[T]):
|
284
284
|
detected: Optional[bool] = None
|
285
|
-
data: Optional[
|
285
|
+
data: Optional[T] = None
|
286
286
|
|
287
287
|
|
288
288
|
class TextGuardDetectors(APIResponseModel):
|
@@ -302,6 +302,11 @@ class TextGuardDetectors(APIResponseModel):
|
|
302
302
|
topic: Optional[TextGuardDetector[TopicDetectionResult]] = None
|
303
303
|
|
304
304
|
|
305
|
+
class PromptMessage(APIResponseModel):
|
306
|
+
role: str
|
307
|
+
content: str
|
308
|
+
|
309
|
+
|
305
310
|
class TextGuardResult(PangeaResponseResult):
|
306
311
|
detectors: TextGuardDetectors
|
307
312
|
"""Result of the recipe analyzing and input prompt."""
|
@@ -318,7 +323,7 @@ class TextGuardResult(PangeaResponseResult):
|
|
318
323
|
unredact.
|
319
324
|
"""
|
320
325
|
|
321
|
-
prompt_messages: Optional[
|
326
|
+
prompt_messages: Optional[list[PromptMessage]] = None
|
322
327
|
"""Updated structured prompt, if applicable."""
|
323
328
|
|
324
329
|
prompt_text: Optional[str] = None
|
@@ -331,539 +336,6 @@ class TextGuardResult(PangeaResponseResult):
|
|
331
336
|
"""Whether or not the original input was transformed."""
|
332
337
|
|
333
338
|
|
334
|
-
class GuardDetectors(APIResponseModel):
|
335
|
-
code: Optional[object] = None
|
336
|
-
competitors: Optional[object] = None
|
337
|
-
confidential_and_pii_entity: Optional[object] = None
|
338
|
-
custom_entity: Optional[object] = None
|
339
|
-
language: Optional[object] = None
|
340
|
-
malicious_entity: Optional[object] = None
|
341
|
-
malicious_prompt: Optional[object] = None
|
342
|
-
prompt_hardening: Optional[object] = None
|
343
|
-
secret_and_key_entity: Optional[object] = None
|
344
|
-
topic: Optional[object] = None
|
345
|
-
|
346
|
-
|
347
|
-
class GuardResult(PangeaResponseResult):
|
348
|
-
detectors: GuardDetectors
|
349
|
-
"""Result of the recipe analyzing and input prompt."""
|
350
|
-
|
351
|
-
access_rules: Optional[object] = None
|
352
|
-
"""Result of the recipe evaluating configured rules"""
|
353
|
-
|
354
|
-
blocked: Optional[bool] = None
|
355
|
-
"""Whether or not the prompt triggered a block detection."""
|
356
|
-
|
357
|
-
fpe_context: Optional[str] = None
|
358
|
-
"""
|
359
|
-
If an FPE redaction method returned results, this will be the context passed
|
360
|
-
to unredact.
|
361
|
-
"""
|
362
|
-
|
363
|
-
input_token_count: Optional[float] = None
|
364
|
-
"""Number of tokens counted in the input"""
|
365
|
-
|
366
|
-
output: Optional[object] = None
|
367
|
-
"""Updated structured prompt."""
|
368
|
-
|
369
|
-
output_token_count: Optional[float] = None
|
370
|
-
"""Number of tokens counted in the output"""
|
371
|
-
|
372
|
-
recipe: Optional[str] = None
|
373
|
-
"""The Recipe that was used."""
|
374
|
-
|
375
|
-
transformed: Optional[bool] = None
|
376
|
-
"""Whether or not the original input was transformed."""
|
377
|
-
|
378
|
-
|
379
|
-
class Areas(BaseModel):
|
380
|
-
model_config = ConfigDict(extra="forbid")
|
381
|
-
|
382
|
-
text_guard: bool
|
383
|
-
|
384
|
-
|
385
|
-
class AuditDataActivityConfig(BaseModel):
|
386
|
-
model_config = ConfigDict(extra="forbid")
|
387
|
-
|
388
|
-
enabled: bool
|
389
|
-
audit_service_config_id: str
|
390
|
-
areas: Areas
|
391
|
-
|
392
|
-
|
393
|
-
class PromptGuard(BaseModel):
|
394
|
-
model_config = ConfigDict(extra="forbid")
|
395
|
-
|
396
|
-
enabled: Optional[bool] = None
|
397
|
-
config_id: Optional[str] = None
|
398
|
-
confidence_threshold: Optional[float] = None
|
399
|
-
|
400
|
-
|
401
|
-
class IpIntel(BaseModel):
|
402
|
-
model_config = ConfigDict(extra="forbid")
|
403
|
-
|
404
|
-
enabled: Optional[bool] = None
|
405
|
-
config_id: Optional[str] = None
|
406
|
-
reputation_provider: Optional[str] = None
|
407
|
-
risk_threshold: Optional[float] = None
|
408
|
-
|
409
|
-
|
410
|
-
class UserIntel(BaseModel):
|
411
|
-
model_config = ConfigDict(extra="forbid")
|
412
|
-
|
413
|
-
enabled: Optional[bool] = None
|
414
|
-
config_id: Optional[str] = None
|
415
|
-
breach_provider: Optional[str] = None
|
416
|
-
|
417
|
-
|
418
|
-
class UrlIntel(BaseModel):
|
419
|
-
model_config = ConfigDict(extra="forbid")
|
420
|
-
|
421
|
-
enabled: Optional[bool] = None
|
422
|
-
config_id: Optional[str] = None
|
423
|
-
reputation_provider: Optional[str] = None
|
424
|
-
risk_threshold: Optional[float] = None
|
425
|
-
|
426
|
-
|
427
|
-
class DomainIntel(BaseModel):
|
428
|
-
model_config = ConfigDict(extra="forbid")
|
429
|
-
|
430
|
-
enabled: Optional[bool] = None
|
431
|
-
config_id: Optional[str] = None
|
432
|
-
reputation_provider: Optional[str] = None
|
433
|
-
risk_threshold: Optional[float] = None
|
434
|
-
|
435
|
-
|
436
|
-
class FileScan(BaseModel):
|
437
|
-
model_config = ConfigDict(extra="forbid")
|
438
|
-
|
439
|
-
enabled: Optional[bool] = None
|
440
|
-
config_id: Optional[str] = None
|
441
|
-
scan_provider: Optional[str] = None
|
442
|
-
risk_threshold: Optional[float] = None
|
443
|
-
|
444
|
-
|
445
|
-
class Redact(BaseModel):
|
446
|
-
model_config = ConfigDict(extra="forbid")
|
447
|
-
|
448
|
-
enabled: Optional[bool] = None
|
449
|
-
config_id: Optional[str] = None
|
450
|
-
|
451
|
-
|
452
|
-
class Vault(BaseModel):
|
453
|
-
model_config = ConfigDict(extra="forbid")
|
454
|
-
|
455
|
-
config_id: Optional[str] = None
|
456
|
-
|
457
|
-
|
458
|
-
class Lingua(BaseModel):
|
459
|
-
model_config = ConfigDict(extra="forbid")
|
460
|
-
|
461
|
-
enabled: Optional[bool] = None
|
462
|
-
|
463
|
-
|
464
|
-
class Code(BaseModel):
|
465
|
-
model_config = ConfigDict(extra="forbid")
|
466
|
-
|
467
|
-
enabled: Optional[bool] = None
|
468
|
-
|
469
|
-
|
470
|
-
class ConnectionsConfig(BaseModel):
|
471
|
-
model_config = ConfigDict(extra="forbid")
|
472
|
-
|
473
|
-
prompt_guard: Optional[PromptGuard] = None
|
474
|
-
ip_intel: Optional[IpIntel] = None
|
475
|
-
user_intel: Optional[UserIntel] = None
|
476
|
-
url_intel: Optional[UrlIntel] = None
|
477
|
-
domain_intel: Optional[DomainIntel] = None
|
478
|
-
file_scan: Optional[FileScan] = None
|
479
|
-
redact: Optional[Redact] = None
|
480
|
-
vault: Optional[Vault] = None
|
481
|
-
lingua: Optional[Lingua] = None
|
482
|
-
code: Optional[Code] = None
|
483
|
-
|
484
|
-
|
485
|
-
class PartialMasking(BaseModel):
|
486
|
-
masking_type: Optional[Literal["unmask", "mask"]] = "unmask"
|
487
|
-
unmasked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
488
|
-
unmasked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
489
|
-
masked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
490
|
-
masked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
491
|
-
chars_to_ignore: Optional[list[CharsToIgnoreItem]] = None
|
492
|
-
masking_char: Annotated[Optional[str], Field(max_length=1, min_length=1)] = "*"
|
493
|
-
|
494
|
-
|
495
|
-
class RuleRedactionConfig1(APIResponseModel):
|
496
|
-
redaction_type: Literal[
|
497
|
-
"mask",
|
498
|
-
"partial_masking",
|
499
|
-
"replacement",
|
500
|
-
"hash",
|
501
|
-
"detect_only",
|
502
|
-
"fpe",
|
503
|
-
"mask",
|
504
|
-
"detect_only",
|
505
|
-
]
|
506
|
-
"""Redaction method to apply for this rule"""
|
507
|
-
redaction_value: Optional[str] = None
|
508
|
-
partial_masking: Optional[PartialMasking] = None
|
509
|
-
hash: Optional[Hash] = None
|
510
|
-
fpe_alphabet: Optional[
|
511
|
-
Literal[
|
512
|
-
"numeric",
|
513
|
-
"alphalower",
|
514
|
-
"alphaupper",
|
515
|
-
"alpha",
|
516
|
-
"alphanumericlower",
|
517
|
-
"alphanumericupper",
|
518
|
-
"alphanumeric",
|
519
|
-
]
|
520
|
-
] = None
|
521
|
-
|
522
|
-
|
523
|
-
class PartialMasking1(BaseModel):
|
524
|
-
masking_type: Optional[Literal["unmask", "mask"]] = "unmask"
|
525
|
-
unmasked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
526
|
-
unmasked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
527
|
-
masked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
528
|
-
masked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
529
|
-
chars_to_ignore: Optional[list[CharsToIgnoreItem]] = None
|
530
|
-
masking_char: Annotated[Optional[str], Field(max_length=1, min_length=1)] = "*"
|
531
|
-
|
532
|
-
|
533
|
-
class RuleRedactionConfig2(BaseModel):
|
534
|
-
model_config = ConfigDict(extra="forbid")
|
535
|
-
|
536
|
-
redaction_type: Literal["replacement"]
|
537
|
-
redaction_value: str
|
538
|
-
partial_masking: Optional[PartialMasking1] = None
|
539
|
-
hash: Optional[Hash] = None
|
540
|
-
fpe_alphabet: Optional[
|
541
|
-
Literal[
|
542
|
-
"numeric",
|
543
|
-
"alphalower",
|
544
|
-
"alphaupper",
|
545
|
-
"alpha",
|
546
|
-
"alphanumericlower",
|
547
|
-
"alphanumericupper",
|
548
|
-
"alphanumeric",
|
549
|
-
]
|
550
|
-
] = None
|
551
|
-
|
552
|
-
|
553
|
-
class PartialMasking2(BaseModel):
|
554
|
-
masking_type: Optional[Literal["unmask", "mask"]] = "unmask"
|
555
|
-
unmasked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
556
|
-
unmasked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
557
|
-
masked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
558
|
-
masked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
559
|
-
chars_to_ignore: Optional[list[CharsToIgnoreItem]] = None
|
560
|
-
masking_char: Annotated[Optional[str], Field(max_length=1, min_length=1)] = "*"
|
561
|
-
|
562
|
-
|
563
|
-
class RuleRedactionConfig3(BaseModel):
|
564
|
-
model_config = ConfigDict(extra="forbid")
|
565
|
-
|
566
|
-
redaction_type: Literal["partial_masking"]
|
567
|
-
redaction_value: str
|
568
|
-
partial_masking: PartialMasking2
|
569
|
-
hash: Optional[Hash] = None
|
570
|
-
fpe_alphabet: Optional[
|
571
|
-
Literal[
|
572
|
-
"numeric",
|
573
|
-
"alphalower",
|
574
|
-
"alphaupper",
|
575
|
-
"alpha",
|
576
|
-
"alphanumericlower",
|
577
|
-
"alphanumericupper",
|
578
|
-
"alphanumeric",
|
579
|
-
]
|
580
|
-
] = None
|
581
|
-
|
582
|
-
|
583
|
-
class PartialMasking3(BaseModel):
|
584
|
-
masking_type: Optional[Literal["unmask", "mask"]] = "unmask"
|
585
|
-
unmasked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
586
|
-
unmasked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
587
|
-
masked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
588
|
-
masked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
589
|
-
chars_to_ignore: Optional[list[CharsToIgnoreItem]] = None
|
590
|
-
masking_char: Annotated[Optional[str], Field(max_length=1, min_length=1)] = "*"
|
591
|
-
|
592
|
-
|
593
|
-
class RuleRedactionConfig4(BaseModel):
|
594
|
-
model_config = ConfigDict(extra="forbid")
|
595
|
-
|
596
|
-
redaction_type: Literal["hash"]
|
597
|
-
redaction_value: str
|
598
|
-
partial_masking: PartialMasking3
|
599
|
-
hash: Optional[Hash] = None
|
600
|
-
fpe_alphabet: Optional[
|
601
|
-
Literal[
|
602
|
-
"numeric",
|
603
|
-
"alphalower",
|
604
|
-
"alphaupper",
|
605
|
-
"alpha",
|
606
|
-
"alphanumericlower",
|
607
|
-
"alphanumericupper",
|
608
|
-
"alphanumeric",
|
609
|
-
]
|
610
|
-
] = None
|
611
|
-
|
612
|
-
|
613
|
-
class CharsToIgnoreItem(RootModel[str]):
|
614
|
-
root: Annotated[str, Field(max_length=1, min_length=1)]
|
615
|
-
|
616
|
-
|
617
|
-
class PartialMasking4(BaseModel):
|
618
|
-
masking_type: Optional[Literal["unmask", "mask"]] = "unmask"
|
619
|
-
unmasked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
620
|
-
unmasked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
621
|
-
masked_from_left: Annotated[Optional[int], Field(ge=0)] = None
|
622
|
-
masked_from_right: Annotated[Optional[int], Field(ge=0)] = None
|
623
|
-
chars_to_ignore: Optional[list[CharsToIgnoreItem]] = None
|
624
|
-
masking_char: Annotated[Optional[str], Field(max_length=1, min_length=1)] = "*"
|
625
|
-
|
626
|
-
|
627
|
-
class Hash(BaseModel):
|
628
|
-
hash_type: Literal["md5", "sha256"]
|
629
|
-
"""The type of hashing algorithm"""
|
630
|
-
|
631
|
-
|
632
|
-
class RuleRedactionConfig5(BaseModel):
|
633
|
-
model_config = ConfigDict(extra="forbid")
|
634
|
-
|
635
|
-
redaction_type: Literal["fpe"]
|
636
|
-
redaction_value: str
|
637
|
-
partial_masking: PartialMasking4
|
638
|
-
hash: Optional[Hash] = None
|
639
|
-
fpe_alphabet: Optional[
|
640
|
-
Literal[
|
641
|
-
"numeric",
|
642
|
-
"alphalower",
|
643
|
-
"alphaupper",
|
644
|
-
"alpha",
|
645
|
-
"alphanumericlower",
|
646
|
-
"alphanumericupper",
|
647
|
-
"alphanumeric",
|
648
|
-
]
|
649
|
-
] = None
|
650
|
-
|
651
|
-
|
652
|
-
class Rule(APIResponseModel):
|
653
|
-
redact_rule_id: str
|
654
|
-
"""
|
655
|
-
Identifier of the redaction rule to apply. This should match a rule defined
|
656
|
-
in the [Redact service](https://pangea.cloud/docs/redact/using-redact/using-redact).
|
657
|
-
"""
|
658
|
-
redaction: Union[
|
659
|
-
RuleRedactionConfig1,
|
660
|
-
RuleRedactionConfig2,
|
661
|
-
RuleRedactionConfig3,
|
662
|
-
RuleRedactionConfig4,
|
663
|
-
RuleRedactionConfig5,
|
664
|
-
]
|
665
|
-
"""
|
666
|
-
Configuration for the redaction method applied to detected values.
|
667
|
-
|
668
|
-
Each rule supports one redaction type, such as masking, replacement,
|
669
|
-
hashing, Format-Preserving Encryption (FPE), or detection-only mode.
|
670
|
-
Additional parameters may be required depending on the selected redaction
|
671
|
-
type.
|
672
|
-
|
673
|
-
For more details, see the [AI Guard Recipe Actions](https://pangea.cloud/docs/ai-guard/recipes#actions)
|
674
|
-
documentation.
|
675
|
-
"""
|
676
|
-
block: Optional[bool] = None
|
677
|
-
"""
|
678
|
-
If `true`, indicates that further processing should be stopped when this
|
679
|
-
rule is triggered
|
680
|
-
"""
|
681
|
-
disabled: Optional[bool] = None
|
682
|
-
"""
|
683
|
-
If `true`, disables this specific rule even if the detector is enabled
|
684
|
-
"""
|
685
|
-
reputation_check: Optional[bool] = None
|
686
|
-
"""
|
687
|
-
If `true`, performs a reputation check using the configured intel provider.
|
688
|
-
Applies to the Malicious Entity detector when using IP, URL, or Domain Intel
|
689
|
-
services.
|
690
|
-
"""
|
691
|
-
transform_if_malicious: Optional[bool] = None
|
692
|
-
"""
|
693
|
-
If `true`, applies redaction or transformation when the detected value is
|
694
|
-
determined to be malicious by intel analysis
|
695
|
-
"""
|
696
|
-
|
697
|
-
|
698
|
-
class Settings(BaseModel):
|
699
|
-
rules: Optional[list[Rule]] = None
|
700
|
-
|
701
|
-
|
702
|
-
class DetectorSetting(BaseModel):
|
703
|
-
model_config = ConfigDict(extra="forbid")
|
704
|
-
|
705
|
-
detector_name: str
|
706
|
-
state: Literal["disabled", "enabled"]
|
707
|
-
settings: Settings
|
708
|
-
|
709
|
-
|
710
|
-
class RedactConnectorSettings(BaseModel):
|
711
|
-
fpe_tweak_vault_secret_id: Optional[str] = None
|
712
|
-
|
713
|
-
|
714
|
-
class ConnectorSettings(BaseModel):
|
715
|
-
model_config = ConfigDict(extra="forbid")
|
716
|
-
|
717
|
-
redact: Optional[RedactConnectorSettings] = None
|
718
|
-
|
719
|
-
|
720
|
-
class AccessRuleSettings(APIResponseModel):
|
721
|
-
"""
|
722
|
-
Configuration for an individual access rule used in an AI Guard recipe. Each
|
723
|
-
rule defines its matching logic and the action to apply when the logic
|
724
|
-
evaluates to true.
|
725
|
-
"""
|
726
|
-
|
727
|
-
rule_key: Annotated[str, Field(pattern="^([a-zA-Z0-9_][a-zA-Z0-9/|_]*)$")]
|
728
|
-
"""
|
729
|
-
Unique identifier for this rule. Should be user-readable and consistent
|
730
|
-
across recipe updates.
|
731
|
-
"""
|
732
|
-
name: str
|
733
|
-
"""Display label for the rule shown in user interfaces."""
|
734
|
-
state: Literal["block", "report"]
|
735
|
-
"""
|
736
|
-
Action to apply if the rule matches. Use 'block' to stop further processing
|
737
|
-
or 'report' to simply log the match.
|
738
|
-
"""
|
739
|
-
|
740
|
-
|
741
|
-
class RecipeConfig(APIResponseModel):
|
742
|
-
name: str
|
743
|
-
"""Human-readable name of the recipe"""
|
744
|
-
description: str
|
745
|
-
"""Detailed description of the recipe's purpose or use case"""
|
746
|
-
version: Optional[str] = "v1"
|
747
|
-
"""Optional version identifier for the recipe. Can be used to track changes."""
|
748
|
-
detectors: Optional[list[DetectorSetting]] = None
|
749
|
-
"""Setting for Detectors"""
|
750
|
-
access_rules: Optional[list[AccessRuleSettings]] = None
|
751
|
-
"""Configuration for access rules used in an AI Guard recipe."""
|
752
|
-
connector_settings: Optional[ConnectorSettings] = None
|
753
|
-
|
754
|
-
|
755
|
-
class ServiceConfig(PangeaResponseResult):
|
756
|
-
id: Optional[str] = None
|
757
|
-
"""ID of an AI Guard service configuration"""
|
758
|
-
name: Optional[str] = None
|
759
|
-
"""Human-readable name of the AI Guard service configuration"""
|
760
|
-
audit_data_activity: Optional[AuditDataActivityConfig] = None
|
761
|
-
connections: Optional[ConnectionsConfig] = None
|
762
|
-
recipes: Optional[dict[str, RecipeConfig]] = None
|
763
|
-
|
764
|
-
|
765
|
-
class ServiceConfigFilter(BaseModel):
|
766
|
-
model_config = ConfigDict(extra="forbid")
|
767
|
-
|
768
|
-
id: Optional[str] = None
|
769
|
-
"""
|
770
|
-
Only records where id equals this value.
|
771
|
-
"""
|
772
|
-
id__contains: Optional[list[str]] = None
|
773
|
-
"""
|
774
|
-
Only records where id includes each substring.
|
775
|
-
"""
|
776
|
-
id__in: Optional[list[str]] = None
|
777
|
-
"""
|
778
|
-
Only records where id equals one of the provided substrings.
|
779
|
-
"""
|
780
|
-
created_at: Optional[PangeaDateTime] = None
|
781
|
-
"""
|
782
|
-
Only records where created_at equals this value.
|
783
|
-
"""
|
784
|
-
created_at__gt: Optional[PangeaDateTime] = None
|
785
|
-
"""
|
786
|
-
Only records where created_at is greater than this value.
|
787
|
-
"""
|
788
|
-
created_at__gte: Optional[PangeaDateTime] = None
|
789
|
-
"""
|
790
|
-
Only records where created_at is greater than or equal to this value.
|
791
|
-
"""
|
792
|
-
created_at__lt: Optional[PangeaDateTime] = None
|
793
|
-
"""
|
794
|
-
Only records where created_at is less than this value.
|
795
|
-
"""
|
796
|
-
created_at__lte: Optional[PangeaDateTime] = None
|
797
|
-
"""
|
798
|
-
Only records where created_at is less than or equal to this value.
|
799
|
-
"""
|
800
|
-
updated_at: Optional[PangeaDateTime] = None
|
801
|
-
"""
|
802
|
-
Only records where updated_at equals this value.
|
803
|
-
"""
|
804
|
-
updated_at__gt: Optional[PangeaDateTime] = None
|
805
|
-
"""
|
806
|
-
Only records where updated_at is greater than this value.
|
807
|
-
"""
|
808
|
-
updated_at__gte: Optional[PangeaDateTime] = None
|
809
|
-
"""
|
810
|
-
Only records where updated_at is greater than or equal to this value.
|
811
|
-
"""
|
812
|
-
updated_at__lt: Optional[PangeaDateTime] = None
|
813
|
-
"""
|
814
|
-
Only records where updated_at is less than this value.
|
815
|
-
"""
|
816
|
-
updated_at__lte: Optional[PangeaDateTime] = None
|
817
|
-
"""
|
818
|
-
Only records where updated_at is less than or equal to this value.
|
819
|
-
"""
|
820
|
-
|
821
|
-
|
822
|
-
class ServiceConfigsPage(PangeaResponseResult):
|
823
|
-
count: Optional[int] = None
|
824
|
-
"""The total number of service configs matched by the list request."""
|
825
|
-
last: Optional[str] = None
|
826
|
-
"""
|
827
|
-
Used to fetch the next page of the current listing when provided in a
|
828
|
-
repeated request's last parameter.
|
829
|
-
"""
|
830
|
-
items: Optional[list[ServiceConfig]] = None
|
831
|
-
|
832
|
-
|
833
|
-
class ExtraInfoTyped(TypedDict, total=False):
|
834
|
-
"""(AIDR) Logging schema."""
|
835
|
-
|
836
|
-
app_name: str
|
837
|
-
"""Name of source application."""
|
838
|
-
|
839
|
-
app_group: str
|
840
|
-
"""The group of source application."""
|
841
|
-
|
842
|
-
app_version: str
|
843
|
-
"""Version of the source application."""
|
844
|
-
|
845
|
-
actor_name: str
|
846
|
-
"""Name of subject actor."""
|
847
|
-
|
848
|
-
actor_group: str
|
849
|
-
"""The group of subject actor."""
|
850
|
-
|
851
|
-
source_region: str
|
852
|
-
"""Geographic region or data center."""
|
853
|
-
|
854
|
-
data_sensitivity: str
|
855
|
-
"""Sensitivity level of data involved"""
|
856
|
-
|
857
|
-
customer_tier: str
|
858
|
-
"""Tier of the user or organization"""
|
859
|
-
|
860
|
-
use_case: str
|
861
|
-
"""Business-specific use case"""
|
862
|
-
|
863
|
-
|
864
|
-
ExtraInfo: TypeAlias = Union[ExtraInfoTyped, dict[str, object]]
|
865
|
-
|
866
|
-
|
867
339
|
class AIGuard(ServiceBase):
|
868
340
|
"""AI Guard service client.
|
869
341
|
|
@@ -938,11 +410,12 @@ class AIGuard(ServiceBase):
|
|
938
410
|
def guard_text(
|
939
411
|
self,
|
940
412
|
*,
|
941
|
-
messages: Sequence[Message],
|
413
|
+
messages: Sequence[Message | McpToolsMessage],
|
942
414
|
recipe: str | None = None,
|
943
415
|
debug: bool | None = None,
|
944
416
|
overrides: Overrides | None = None,
|
945
417
|
log_fields: LogFields | None = None,
|
418
|
+
only_relevant_content: bool = False,
|
946
419
|
) -> PangeaResponse[TextGuardResult]:
|
947
420
|
"""
|
948
421
|
Guard LLM input and output text
|
@@ -965,6 +438,8 @@ class AIGuard(ServiceBase):
|
|
965
438
|
recipe: Recipe key of a configuration of data types and settings
|
966
439
|
defined in the Pangea User Console. It specifies the rules that
|
967
440
|
are to be applied to the text, such as defang malicious URLs.
|
441
|
+
only_relevant_content: Whether or not to only send relevant content
|
442
|
+
to AI Guard.
|
968
443
|
|
969
444
|
Examples:
|
970
445
|
response = ai_guard.guard_text(messages=[Message(role="user", content="hello world")])
|
@@ -974,11 +449,12 @@ class AIGuard(ServiceBase):
|
|
974
449
|
self,
|
975
450
|
text: str | None = None,
|
976
451
|
*,
|
977
|
-
messages: Sequence[Message] | None = None,
|
452
|
+
messages: Sequence[Message | McpToolsMessage] | None = None,
|
978
453
|
debug: bool | None = None,
|
979
454
|
log_fields: LogFields | None = None,
|
980
455
|
overrides: Overrides | None = None,
|
981
456
|
recipe: str | None = None,
|
457
|
+
only_relevant_content: bool = False,
|
982
458
|
) -> PangeaResponse[TextGuardResult]:
|
983
459
|
"""
|
984
460
|
Guard LLM input and output text
|
@@ -1004,6 +480,8 @@ class AIGuard(ServiceBase):
|
|
1004
480
|
recipe: Recipe key of a configuration of data types and settings
|
1005
481
|
defined in the Pangea User Console. It specifies the rules that
|
1006
482
|
are to be applied to the text, such as defang malicious URLs.
|
483
|
+
only_relevant_content: Whether or not to only send relevant content
|
484
|
+
to AI Guard.
|
1007
485
|
|
1008
486
|
Examples:
|
1009
487
|
response = ai_guard.guard_text("text")
|
@@ -1012,7 +490,11 @@ class AIGuard(ServiceBase):
|
|
1012
490
|
if text is not None and messages is not None:
|
1013
491
|
raise ValueError("Exactly one of `text` or `messages` must be given")
|
1014
492
|
|
1015
|
-
|
493
|
+
if only_relevant_content and messages is not None:
|
494
|
+
original_messages = messages
|
495
|
+
messages, original_indices = get_relevant_content(messages)
|
496
|
+
|
497
|
+
response = self.request.post(
|
1016
498
|
"v1/text/guard",
|
1017
499
|
TextGuardResult,
|
1018
500
|
data={
|
@@ -1025,156 +507,64 @@ class AIGuard(ServiceBase):
|
|
1025
507
|
},
|
1026
508
|
)
|
1027
509
|
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
recipe: str | None = None,
|
1033
|
-
debug: bool | None = None,
|
1034
|
-
overrides: Overrides | None = None,
|
1035
|
-
app_id: str | None = None,
|
1036
|
-
actor_id: str | None = None,
|
1037
|
-
llm_provider: str | None = None,
|
1038
|
-
model: str | None = None,
|
1039
|
-
model_version: str | None = None,
|
1040
|
-
request_token_count: int | None = None,
|
1041
|
-
response_token_count: int | None = None,
|
1042
|
-
source_ip: str | None = None,
|
1043
|
-
source_location: str | None = None,
|
1044
|
-
tenant_id: str | None = None,
|
1045
|
-
event_type: Literal["input", "output"] | None = None,
|
1046
|
-
sensor_instance_id: str | None = None,
|
1047
|
-
extra_info: ExtraInfo | None = None,
|
1048
|
-
count_tokens: bool | None = None,
|
1049
|
-
) -> PangeaResponse[GuardResult]:
|
1050
|
-
"""
|
1051
|
-
Guard LLM input and output
|
510
|
+
if only_relevant_content and response.result and response.result.prompt_messages:
|
511
|
+
response.result.prompt_messages = patch_messages(
|
512
|
+
original_messages, original_indices, response.result.prompt_messages
|
513
|
+
) # type: ignore[assignment]
|
1052
514
|
|
1053
|
-
|
1054
|
-
of malicious content, and other undesirable data transfers.
|
515
|
+
return response
|
1055
516
|
|
1056
|
-
OperationId: ai_guard_post_v1beta_guard
|
1057
517
|
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
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.
|
1064
|
-
debug: Setting this value to true will provide a detailed analysis of the text data
|
1065
|
-
app_name: Name of source application.
|
1066
|
-
llm_provider: Underlying LLM. Example: 'OpenAI'.
|
1067
|
-
model: Model used to perform the event. Example: 'gpt'.
|
1068
|
-
model_version: Model version used to perform the event. Example: '3.5'.
|
1069
|
-
request_token_count: Number of tokens in the request.
|
1070
|
-
response_token_count: Number of tokens in the response.
|
1071
|
-
source_ip: IP address of user or app or agent.
|
1072
|
-
source_location: Location of user or app or agent.
|
1073
|
-
tenant_id: For gateway-like integrations with multi-tenant support.
|
1074
|
-
event_type: (AIDR) Event Type.
|
1075
|
-
sensor_instance_id: (AIDR) sensor instance id.
|
1076
|
-
extra_info: (AIDR) Logging schema.
|
1077
|
-
count_tokens: Provide input and output token count.
|
1078
|
-
"""
|
1079
|
-
return self.request.post(
|
1080
|
-
"v1beta/guard",
|
1081
|
-
GuardResult,
|
1082
|
-
data={
|
1083
|
-
"input": input,
|
1084
|
-
"recipe": recipe,
|
1085
|
-
"debug": debug,
|
1086
|
-
"overrides": overrides,
|
1087
|
-
"app_id": app_id,
|
1088
|
-
"actor_id": actor_id,
|
1089
|
-
"llm_provider": llm_provider,
|
1090
|
-
"model": model,
|
1091
|
-
"model_version": model_version,
|
1092
|
-
"request_token_count": request_token_count,
|
1093
|
-
"response_token_count": response_token_count,
|
1094
|
-
"source_ip": source_ip,
|
1095
|
-
"source_location": source_location,
|
1096
|
-
"tenant_id": tenant_id,
|
1097
|
-
"event_type": event_type,
|
1098
|
-
"sensor_instance_id": sensor_instance_id,
|
1099
|
-
"extra_info": extra_info,
|
1100
|
-
"count_tokens": count_tokens,
|
1101
|
-
},
|
1102
|
-
)
|
518
|
+
def get_relevant_content(
|
519
|
+
messages: Sequence[Message | McpToolsMessage],
|
520
|
+
) -> tuple[list[Message | McpToolsMessage], list[int]]:
|
521
|
+
"""
|
522
|
+
Returns relevant messages and their indices in the original list.
|
1103
523
|
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
524
|
+
1, If last message is "assistant", then the relevant messages are all system
|
525
|
+
messages that come before it, plus that last assistant message.
|
526
|
+
2. Else, find the last "assistant" message. Then the relevant messages are
|
527
|
+
all system messages that come before it, and all messages that come after
|
528
|
+
it.
|
529
|
+
"""
|
1109
530
|
|
1110
|
-
|
1111
|
-
|
1112
|
-
name: str,
|
1113
|
-
*,
|
1114
|
-
id: str | None = None,
|
1115
|
-
audit_data_activity: AuditDataActivityConfig | None = None,
|
1116
|
-
connections: ConnectionsConfig | None = None,
|
1117
|
-
recipes: Mapping[str, RecipeConfig] | None = None,
|
1118
|
-
) -> PangeaResponse[ServiceConfig]:
|
1119
|
-
"""
|
1120
|
-
OperationId: ai_guard_post_v1beta_config_create
|
1121
|
-
"""
|
1122
|
-
return self.request.post(
|
1123
|
-
"v1beta/config/create",
|
1124
|
-
data={
|
1125
|
-
"name": name,
|
1126
|
-
"id": id,
|
1127
|
-
"audit_data_activity": audit_data_activity,
|
1128
|
-
"connections": connections,
|
1129
|
-
"recipes": recipes,
|
1130
|
-
},
|
1131
|
-
result_class=ServiceConfig,
|
1132
|
-
)
|
531
|
+
if len(messages) == 0:
|
532
|
+
return [], []
|
1133
533
|
|
1134
|
-
|
1135
|
-
|
1136
|
-
id: str,
|
1137
|
-
name: str,
|
1138
|
-
*,
|
1139
|
-
audit_data_activity: AuditDataActivityConfig | None = None,
|
1140
|
-
connections: ConnectionsConfig | None = None,
|
1141
|
-
recipes: Mapping[str, RecipeConfig] | None = None,
|
1142
|
-
) -> PangeaResponse[ServiceConfig]:
|
1143
|
-
"""
|
1144
|
-
OperationId: ai_guard_post_v1beta_config_update
|
1145
|
-
"""
|
1146
|
-
return self.request.post(
|
1147
|
-
"v1beta/config/update",
|
1148
|
-
data={
|
1149
|
-
"id": id,
|
1150
|
-
"name": name,
|
1151
|
-
"audit_data_activity": audit_data_activity,
|
1152
|
-
"connections": connections,
|
1153
|
-
"recipes": recipes,
|
1154
|
-
},
|
1155
|
-
result_class=ServiceConfig,
|
1156
|
-
)
|
534
|
+
system_messages = [msg for msg in messages if msg.role == "system"]
|
535
|
+
system_indices = [i for i, msg in enumerate(messages) if msg.role == "system"]
|
1157
536
|
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
return self.request.post("v1beta/config/delete", data={"id": id}, result_class=ServiceConfig)
|
537
|
+
# If the last message is assistant, then return all system messages and that
|
538
|
+
# assistant message.
|
539
|
+
if messages[-1].role == "assistant":
|
540
|
+
return system_messages + [messages[-1]], system_indices + [len(messages) - 1]
|
1163
541
|
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
""
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
542
|
+
# Otherwise, work backwards until we find the last assistant message, then
|
543
|
+
# return all messages after that.
|
544
|
+
last_assistant_index = -1
|
545
|
+
for i in range(len(messages) - 1, -1, -1):
|
546
|
+
if messages[i].role == "assistant":
|
547
|
+
last_assistant_index = i
|
548
|
+
break
|
549
|
+
|
550
|
+
relevant_messages = []
|
551
|
+
indices = []
|
552
|
+
for i, msg in enumerate(messages):
|
553
|
+
if msg.role == "system" or i > last_assistant_index:
|
554
|
+
relevant_messages.append(msg)
|
555
|
+
indices.append(i)
|
556
|
+
|
557
|
+
return relevant_messages, indices
|
558
|
+
|
559
|
+
|
560
|
+
def patch_messages(
|
561
|
+
original: Sequence[Message | McpToolsMessage],
|
562
|
+
original_indices: list[int],
|
563
|
+
transformed: Sequence[PromptMessage],
|
564
|
+
) -> list[Message | McpToolsMessage | PromptMessage]:
|
565
|
+
if len(original) == len(transformed):
|
566
|
+
return list(transformed)
|
567
|
+
|
568
|
+
return [
|
569
|
+
transformed[original_indices.index(i)] if i in original_indices else orig for i, orig in enumerate(original)
|
570
|
+
]
|