aisafe 0.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.
- aisafe/__init__.py +141 -0
- aisafe/cli.py +197 -0
- aisafe/config/__init__.py +54 -0
- aisafe/config/attest-levels.yaml +58 -0
- aisafe/config/default.yaml +163 -0
- aisafe/config/eu_high_risk.yaml +180 -0
- aisafe/config/nemo_rails/config.yml +57 -0
- aisafe/config/nemo_rails/rails.co +140 -0
- aisafe/config/prices.yaml +72 -0
- aisafe/config/settings.py +372 -0
- aisafe/core/__init__.py +34 -0
- aisafe/core/base.py +283 -0
- aisafe/core/exceptions.py +38 -0
- aisafe/core/measurement.py +435 -0
- aisafe/core/pipeline.py +470 -0
- aisafe/core/registry.py +24 -0
- aisafe/devtools/__init__.py +0 -0
- aisafe/devtools/_config.py +114 -0
- aisafe/devtools/_git.py +148 -0
- aisafe/devtools/attest.py +1409 -0
- aisafe/devtools/audit/__init__.py +26 -0
- aisafe/devtools/audit/adapters.py +1796 -0
- aisafe/devtools/audit/baseline.py +589 -0
- aisafe/devtools/audit/configs.py +1080 -0
- aisafe/devtools/audit/discover.py +2053 -0
- aisafe/devtools/audit/html.py +1497 -0
- aisafe/devtools/audit/lint_bridge.py +137 -0
- aisafe/devtools/audit/main.py +986 -0
- aisafe/devtools/audit/model.py +951 -0
- aisafe/devtools/audit/patterns.py +1557 -0
- aisafe/devtools/audit/persons_patterns.py +380 -0
- aisafe/devtools/audit/pydeep.py +1942 -0
- aisafe/devtools/audit/report.py +1218 -0
- aisafe/devtools/audit/rules/__init__.py +551 -0
- aisafe/devtools/audit/rules/blast_radius.py +1140 -0
- aisafe/devtools/audit/rules/evals.py +720 -0
- aisafe/devtools/audit/rules/governance.py +398 -0
- aisafe/devtools/audit/rules/guards.py +1270 -0
- aisafe/devtools/audit/rules/irreversible.py +394 -0
- aisafe/devtools/audit/rules/observability.py +402 -0
- aisafe/devtools/audit/rules/persons.py +1125 -0
- aisafe/devtools/audit/rules/secrets_pii.py +675 -0
- aisafe/devtools/audit/rules/sinks.py +467 -0
- aisafe/devtools/audit/rules/supply_chain.py +703 -0
- aisafe/devtools/audit/rules/trust_boundary.py +761 -0
- aisafe/devtools/audit/semgrep/aisafe-sinks.yaml +435 -0
- aisafe/devtools/audit/vocab.py +475 -0
- aisafe/devtools/audit/walk.py +794 -0
- aisafe/devtools/decide.py +1056 -0
- aisafe/devtools/euaiact_lint.py +337 -0
- aisafe/devtools/fleet.py +1025 -0
- aisafe/devtools/hook.py +1712 -0
- aisafe/devtools/hosts.py +230 -0
- aisafe/devtools/measure.py +1058 -0
- aisafe/devtools/misalignment/__init__.py +0 -0
- aisafe/devtools/misalignment/rules/__init__.py +527 -0
- aisafe/devtools/misalignment_check.py +299 -0
- aisafe/devtools/prices.py +277 -0
- aisafe/devtools/probe.py +918 -0
- aisafe/devtools/skill.py +357 -0
- aisafe/devtools/system_card.py +375 -0
- aisafe/integrations/__init__.py +10 -0
- aisafe/integrations/anthropic_middleware.py +115 -0
- aisafe/integrations/fastapi_middleware.py +233 -0
- aisafe/integrations/langchain_callback.py +135 -0
- aisafe/integrations/nemo_rails.py +433 -0
- aisafe/modules/__init__.py +1 -0
- aisafe/modules/input/__init__.py +6 -0
- aisafe/modules/input/advanced_injection_detectors.py +643 -0
- aisafe/modules/input/llm_input_filter.py +194 -0
- aisafe/modules/input/pii_detector.py +348 -0
- aisafe/modules/input/prompt_injection.py +422 -0
- aisafe/modules/input/rate_limiter.py +172 -0
- aisafe/modules/llm_judges/__init__.py +80 -0
- aisafe/modules/llm_judges/base.py +289 -0
- aisafe/modules/llm_judges/cache.py +198 -0
- aisafe/modules/llm_judges/claude_judge.py +261 -0
- aisafe/modules/llm_judges/estimate.py +204 -0
- aisafe/modules/llm_judges/llamaguard.py +379 -0
- aisafe/modules/llm_judges/openai_mod.py +151 -0
- aisafe/modules/observability/__init__.py +4 -0
- aisafe/modules/observability/audit_logger.py +266 -0
- aisafe/modules/observability/otel.py +402 -0
- aisafe/modules/output/__init__.py +13 -0
- aisafe/modules/output/ai_disclosure.py +197 -0
- aisafe/modules/output/llm_output_filter.py +193 -0
- aisafe/modules/output/manipulation.py +371 -0
- aisafe/modules/output/toxicity.py +208 -0
- aisafe/modules/policy/__init__.py +4 -0
- aisafe/modules/policy/code_analyzer/__init__.py +21 -0
- aisafe/modules/policy/code_analyzer/analyzer.py +430 -0
- aisafe/modules/policy/code_analyzer/reporters.py +606 -0
- aisafe/modules/policy/code_analyzer/rules/__init__.py +877 -0
- aisafe/modules/policy/eu_ai_act.py +478 -0
- aisafe/modules/policy/nist_ai_rmf.py +511 -0
- aisafe/modules/processing/__init__.py +4 -0
- aisafe/modules/processing/llm_tool_filter.py +412 -0
- aisafe/modules/processing/tool_policy.py +445 -0
- aisafe/probes/__init__.py +13 -0
- aisafe/probes/benign_traffic.yaml +252 -0
- aisafe/probes/encoding_bypass.yaml +89 -0
- aisafe/probes/manipulation_elicitation.yaml +82 -0
- aisafe/probes/manipulation_responses.yaml +121 -0
- aisafe/probes/pii_echo.yaml +86 -0
- aisafe/probes/prompt_injection.yaml +90 -0
- aisafe/probes/system_prompt_extraction.yaml +91 -0
- aisafe/probes/tool_abuse.yaml +71 -0
- aisafe/probes/toxicity_elicitation.yaml +87 -0
- aisafe/skills/__init__.py +86 -0
- aisafe/skills/ai-safety-audit/SKILL.md +371 -0
- aisafe/skills/ai-safety-audit/agents/openai.yaml +6 -0
- aisafe/skills/ai-safety-audit/references/apply/generic.md +112 -0
- aisafe/skills/ai-safety-audit/references/apply/go.md +106 -0
- aisafe/skills/ai-safety-audit/references/apply/python.md +547 -0
- aisafe/skills/ai-safety-audit/references/apply/typescript.md +114 -0
- aisafe/skills/ai-safety-audit/references/controls.md +720 -0
- aisafe/skills/ai-safety-audit/references/hosts.md +113 -0
- aisafe/skills/ai-safety-audit/references/levels.md +80 -0
- aisafe/skills/ai-safety-audit/references/report-format.md +389 -0
- aisafe/skills/ai-safety-audit/scripts/attest.ps1 +27 -0
- aisafe/skills/ai-safety-audit/scripts/attest.sh +27 -0
- aisafe/skills/ai-safety-audit/scripts/audit.ps1 +27 -0
- aisafe/skills/ai-safety-audit/scripts/audit.sh +27 -0
- aisafe/skills/ai-safety-audit/scripts/hook.ps1 +25 -0
- aisafe/skills/ai-safety-audit/scripts/hook.sh +24 -0
- aisafe/skills/ai-safety-audit/scripts/verify.ps1 +140 -0
- aisafe/skills/ai-safety-audit/scripts/verify.sh +131 -0
- aisafe-0.1.0.dist-info/METADATA +1493 -0
- aisafe-0.1.0.dist-info/RECORD +133 -0
- aisafe-0.1.0.dist-info/WHEEL +5 -0
- aisafe-0.1.0.dist-info/entry_points.txt +4 -0
- aisafe-0.1.0.dist-info/licenses/LICENSE +21 -0
- aisafe-0.1.0.dist-info/top_level.txt +1 -0
aisafe/__init__.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
__init__.py
|
|
3
|
+
-----------
|
|
4
|
+
AnyAISafe — public API surface.
|
|
5
|
+
|
|
6
|
+
Runtime guardrails (use in your application):
|
|
7
|
+
from aisafe import GuardrailPipeline
|
|
8
|
+
from aisafe import PIIDetector, PromptInjectionGuard
|
|
9
|
+
from aisafe import ToxicityFilter
|
|
10
|
+
from aisafe import ToolPolicyGuard, ToolPolicy
|
|
11
|
+
from aisafe import EUAIActCompliance, RiskTier
|
|
12
|
+
from aisafe import AuditLogger
|
|
13
|
+
|
|
14
|
+
Static code analyzer (use in CI / pre-commit):
|
|
15
|
+
from aisafe import EUAIActCodeAnalyzer, ScanReport
|
|
16
|
+
from aisafe import TerminalReporter, JSONReporter, SARIFReporter
|
|
17
|
+
# or via CLI: aisafe lint src/
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import importlib
|
|
21
|
+
|
|
22
|
+
from aisafe.core.base import (
|
|
23
|
+
Action,
|
|
24
|
+
CheckResult,
|
|
25
|
+
Finding,
|
|
26
|
+
GuardrailBase,
|
|
27
|
+
GuardrailStage,
|
|
28
|
+
PipelineResult,
|
|
29
|
+
Severity,
|
|
30
|
+
)
|
|
31
|
+
from aisafe.core.exceptions import (
|
|
32
|
+
GuardrailBlockedError,
|
|
33
|
+
GuardrailConfigError,
|
|
34
|
+
PolicyViolationError,
|
|
35
|
+
)
|
|
36
|
+
from aisafe.core.pipeline import GuardrailPipeline
|
|
37
|
+
|
|
38
|
+
# Input modules
|
|
39
|
+
from aisafe.modules.input.pii_detector import PIIDetector, PIIRestorer
|
|
40
|
+
from aisafe.modules.input.prompt_injection import PromptInjectionGuard
|
|
41
|
+
from aisafe.modules.input.rate_limiter import RateLimiter
|
|
42
|
+
|
|
43
|
+
# LLM judges (the base class; concrete judges need their provider SDK)
|
|
44
|
+
from aisafe.modules.llm_judges.base import LLMJudgeBase
|
|
45
|
+
|
|
46
|
+
# Output modules
|
|
47
|
+
from aisafe.modules.output.ai_disclosure import AIDisclosureGuard
|
|
48
|
+
from aisafe.modules.output.llm_output_filter import LLMOutputFilter
|
|
49
|
+
from aisafe.modules.output.manipulation import ManipulationGuard
|
|
50
|
+
from aisafe.modules.output.toxicity import ToxicityFilter
|
|
51
|
+
|
|
52
|
+
# Policy — static code analyzer
|
|
53
|
+
from aisafe.modules.policy.code_analyzer.analyzer import EUAIActCodeAnalyzer, ScanReport
|
|
54
|
+
from aisafe.modules.policy.code_analyzer.reporters import (
|
|
55
|
+
JSONReporter,
|
|
56
|
+
MarkdownReporter,
|
|
57
|
+
SARIFReporter,
|
|
58
|
+
TerminalReporter,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Policy — runtime guardrails
|
|
62
|
+
from aisafe.modules.policy.eu_ai_act import EUAIActCompliance, RiskTier
|
|
63
|
+
from aisafe.modules.policy.nist_ai_rmf import ImpactLevel, NISTAIRMFCompliance
|
|
64
|
+
|
|
65
|
+
# Processing modules
|
|
66
|
+
from aisafe.modules.processing.llm_tool_filter import LLMToolFilter
|
|
67
|
+
from aisafe.modules.processing.tool_policy import ToolPolicy, ToolPolicyGuard
|
|
68
|
+
|
|
69
|
+
# Observability is imported on first use (PEP 562). `otel.py` pulls in the
|
|
70
|
+
# OpenTelemetry SDK, about a quarter of a second that every `aisafe` CLI run and every
|
|
71
|
+
# agent hook would otherwise pay before doing anything; `otel.py` still guards the
|
|
72
|
+
# optional dependency itself. Guards stay eager: `@register_guard` fills the registry
|
|
73
|
+
# `GuardrailPipeline.from_config` reads, and a lazy guard would be an unregistered name.
|
|
74
|
+
_LAZY_ATTRIBUTES = {
|
|
75
|
+
"AuditLogger": ("aisafe.modules.observability.audit_logger", "AuditLogger"),
|
|
76
|
+
"TelemetryProvider": ("aisafe.modules.observability.otel", "TelemetryProvider"),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def __getattr__(name: str):
|
|
81
|
+
target = _LAZY_ATTRIBUTES.get(name)
|
|
82
|
+
if target is None:
|
|
83
|
+
raise AttributeError(f"module 'aisafe' has no attribute {name!r}")
|
|
84
|
+
module = importlib.import_module(target[0])
|
|
85
|
+
value = getattr(module, target[1])
|
|
86
|
+
globals()[name] = value
|
|
87
|
+
return value
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def __dir__() -> list[str]:
|
|
91
|
+
return sorted(set(globals()) | set(__all__))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
__version__ = "0.1.0"
|
|
95
|
+
__all__ = [
|
|
96
|
+
# Pipeline
|
|
97
|
+
"GuardrailPipeline",
|
|
98
|
+
# Base types
|
|
99
|
+
"GuardrailBase",
|
|
100
|
+
"CheckResult",
|
|
101
|
+
"PipelineResult",
|
|
102
|
+
"Finding",
|
|
103
|
+
"Severity",
|
|
104
|
+
"Action",
|
|
105
|
+
"GuardrailStage",
|
|
106
|
+
# Exceptions
|
|
107
|
+
"GuardrailBlockedError",
|
|
108
|
+
"GuardrailConfigError",
|
|
109
|
+
"PolicyViolationError",
|
|
110
|
+
# Input
|
|
111
|
+
"PIIDetector",
|
|
112
|
+
"PIIRestorer",
|
|
113
|
+
"PromptInjectionGuard",
|
|
114
|
+
"RateLimiter",
|
|
115
|
+
# Processing
|
|
116
|
+
"ToolPolicyGuard",
|
|
117
|
+
"ToolPolicy",
|
|
118
|
+
"LLMToolFilter",
|
|
119
|
+
# Output
|
|
120
|
+
"ToxicityFilter",
|
|
121
|
+
"LLMOutputFilter",
|
|
122
|
+
"ManipulationGuard",
|
|
123
|
+
"AIDisclosureGuard",
|
|
124
|
+
# LLM judges
|
|
125
|
+
"LLMJudgeBase",
|
|
126
|
+
# Policy — runtime
|
|
127
|
+
"EUAIActCompliance",
|
|
128
|
+
"RiskTier",
|
|
129
|
+
"NISTAIRMFCompliance",
|
|
130
|
+
"ImpactLevel",
|
|
131
|
+
# Policy — static analyzer
|
|
132
|
+
"EUAIActCodeAnalyzer",
|
|
133
|
+
"ScanReport",
|
|
134
|
+
"TerminalReporter",
|
|
135
|
+
"JSONReporter",
|
|
136
|
+
"SARIFReporter",
|
|
137
|
+
"MarkdownReporter",
|
|
138
|
+
# Observability
|
|
139
|
+
"AuditLogger",
|
|
140
|
+
"TelemetryProvider",
|
|
141
|
+
]
|
aisafe/cli.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""
|
|
2
|
+
src/aisafe/cli.py
|
|
3
|
+
---------------
|
|
4
|
+
Single entry point for the AnyAISafe developer tools.
|
|
5
|
+
|
|
6
|
+
aisafe --version
|
|
7
|
+
aisafe lint src/
|
|
8
|
+
aisafe lint src/ --format sarif --output results.sarif
|
|
9
|
+
aisafe misalign src/ --fail-on-warnings
|
|
10
|
+
aisafe audit . --format json -o audit-report.json
|
|
11
|
+
aisafe skill install --host all
|
|
12
|
+
|
|
13
|
+
Every subcommand is a thin wrapper around a tool in `aisafe.devtools`: `lint`,
|
|
14
|
+
`misalign`, `init`, `probe`, `measure`, `audit`, `skill`, `decide`, `hook`, `attest` and
|
|
15
|
+
`fleet`. Each is imported
|
|
16
|
+
only when invoked, so `aisafe lint` never pays for the audit engine. Everything
|
|
17
|
+
after the subcommand is handed to the underlying tool untouched, so their
|
|
18
|
+
flags, defaults and exit codes are unchanged:
|
|
19
|
+
|
|
20
|
+
0 no errors (warnings/info allowed)
|
|
21
|
+
1 errors found
|
|
22
|
+
2 fatal error (bad args, parse failure)
|
|
23
|
+
|
|
24
|
+
`audit` adds 130 for an interrupted run; `skill` returns 1 when an install is
|
|
25
|
+
refused or an installed copy differs from the packaged skill.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import argparse
|
|
31
|
+
import sys
|
|
32
|
+
from typing import Callable, Sequence
|
|
33
|
+
|
|
34
|
+
from aisafe import __version__
|
|
35
|
+
|
|
36
|
+
__all__ = ["main", "build_parser"]
|
|
37
|
+
|
|
38
|
+
_EPILOG = """
|
|
39
|
+
Commands:
|
|
40
|
+
lint EU AI Act static compliance analysis (was: euaiact-lint)
|
|
41
|
+
misalign AI misalignment static analysis (was: misalignment-check)
|
|
42
|
+
init Write an ai-system-card.yaml for the system under assessment
|
|
43
|
+
probe Send a fixed attack corpus to a live endpoint and report what got through
|
|
44
|
+
measure Report what each guard catches, what it breaks, and what it costs
|
|
45
|
+
audit Static AI-safety audit of a repository: findings describe evidence, never verdicts
|
|
46
|
+
skill Install the packaged ai-safety-audit agent skill into host skill directories
|
|
47
|
+
decide What each proposed control costs (latency, tokens, usd, human load) against the declared budgets
|
|
48
|
+
hook Agent-host hooks: gate protected paths, secret literals and unsafe shell; re-audit edits; Stop gate
|
|
49
|
+
attest Evaluate the level criteria against the written documents; a named owner signs the result
|
|
50
|
+
fleet Audit every repository in a list, one subprocess each, and aggregate the counts
|
|
51
|
+
|
|
52
|
+
Run `aisafe <command> --help` for a command's own options.
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
aisafe lint src/
|
|
56
|
+
aisafe lint src/ --format sarif --output results.sarif
|
|
57
|
+
aisafe lint src/ --errors-only
|
|
58
|
+
aisafe lint --staged # pre-commit
|
|
59
|
+
aisafe misalign src/ --fail-on-warnings
|
|
60
|
+
aisafe init --defaults
|
|
61
|
+
aisafe probe http://localhost:8000/chat --response-path '$.response'
|
|
62
|
+
aisafe measure
|
|
63
|
+
aisafe audit . --format json -o audit-report.json
|
|
64
|
+
aisafe audit . --baseline audit-baseline.json --fail-on high
|
|
65
|
+
aisafe skill install --host all
|
|
66
|
+
aisafe skill diff --host all
|
|
67
|
+
aisafe decide --requests-per-day 20000 --p99-budget-ms 250 --monthly-budget-usd 300
|
|
68
|
+
aisafe hook install --host claude --yes
|
|
69
|
+
aisafe hook list
|
|
70
|
+
aisafe attest . --owner "J. Doe <risk owner>" --html
|
|
71
|
+
aisafe attest . --check --require-level 2 # CI
|
|
72
|
+
aisafe fleet fleet.yaml --jobs 4 --proposal
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _lint(argv: Sequence[str]) -> int:
|
|
77
|
+
from aisafe.devtools.euaiact_lint import main as lint_main
|
|
78
|
+
|
|
79
|
+
return lint_main(list(argv))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _misalign(argv: Sequence[str]) -> int:
|
|
83
|
+
from aisafe.devtools.misalignment_check import main as misalign_main
|
|
84
|
+
|
|
85
|
+
return misalign_main(list(argv))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _init(argv: Sequence[str]) -> int:
|
|
89
|
+
from aisafe.devtools.system_card import main as init_main
|
|
90
|
+
|
|
91
|
+
return init_main(list(argv))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _probe(argv: Sequence[str]) -> int:
|
|
95
|
+
from aisafe.devtools.probe import main as probe_main
|
|
96
|
+
|
|
97
|
+
return probe_main(list(argv))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _measure(argv: Sequence[str]) -> int:
|
|
101
|
+
from aisafe.devtools.measure import main as measure_main
|
|
102
|
+
|
|
103
|
+
return measure_main(list(argv))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _audit(argv: Sequence[str]) -> int:
|
|
107
|
+
from aisafe.devtools.audit.main import main as audit_main
|
|
108
|
+
|
|
109
|
+
return audit_main(list(argv))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _skill(argv: Sequence[str]) -> int:
|
|
113
|
+
from aisafe.devtools.skill import main as skill_main
|
|
114
|
+
|
|
115
|
+
return skill_main(list(argv))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _decide(argv: Sequence[str]) -> int:
|
|
119
|
+
from aisafe.devtools.decide import main as decide_main
|
|
120
|
+
|
|
121
|
+
return decide_main(list(argv))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _hook(argv: Sequence[str]) -> int:
|
|
125
|
+
from aisafe.devtools.hook import main as hook_main
|
|
126
|
+
|
|
127
|
+
return hook_main(list(argv))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _attest(argv: Sequence[str]) -> int:
|
|
131
|
+
from aisafe.devtools.attest import main as attest_main
|
|
132
|
+
|
|
133
|
+
return attest_main(list(argv))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _fleet(argv: Sequence[str]) -> int:
|
|
137
|
+
from aisafe.devtools.fleet import main as fleet_main
|
|
138
|
+
|
|
139
|
+
return fleet_main(list(argv))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
COMMANDS: dict[str, Callable[[Sequence[str]], int]] = {
|
|
143
|
+
"lint": _lint,
|
|
144
|
+
"misalign": _misalign,
|
|
145
|
+
"init": _init,
|
|
146
|
+
"probe": _probe,
|
|
147
|
+
"measure": _measure,
|
|
148
|
+
"audit": _audit,
|
|
149
|
+
"skill": _skill,
|
|
150
|
+
"decide": _decide,
|
|
151
|
+
"hook": _hook,
|
|
152
|
+
"attest": _attest,
|
|
153
|
+
"fleet": _fleet,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
158
|
+
p = argparse.ArgumentParser(
|
|
159
|
+
prog="aisafe",
|
|
160
|
+
description="AnyAISafe developer tools.",
|
|
161
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
162
|
+
epilog=_EPILOG,
|
|
163
|
+
)
|
|
164
|
+
p.add_argument(
|
|
165
|
+
"--version",
|
|
166
|
+
"-V",
|
|
167
|
+
action="version",
|
|
168
|
+
version=f"aisafe {__version__}",
|
|
169
|
+
)
|
|
170
|
+
p.add_argument(
|
|
171
|
+
"command",
|
|
172
|
+
choices=sorted(COMMANDS),
|
|
173
|
+
help="Subcommand to run",
|
|
174
|
+
)
|
|
175
|
+
# REMAINDER hands the rest through verbatim, so each tool keeps its own
|
|
176
|
+
# flags, help text and pyproject-sourced defaults.
|
|
177
|
+
p.add_argument(
|
|
178
|
+
"args",
|
|
179
|
+
nargs=argparse.REMAINDER,
|
|
180
|
+
help="Arguments passed through to the subcommand",
|
|
181
|
+
)
|
|
182
|
+
return p
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main(argv: list[str] | None = None) -> int:
|
|
186
|
+
parser = build_parser()
|
|
187
|
+
if argv is None:
|
|
188
|
+
argv = sys.argv[1:]
|
|
189
|
+
if not argv:
|
|
190
|
+
parser.print_help()
|
|
191
|
+
return 2
|
|
192
|
+
ns = parser.parse_args(argv)
|
|
193
|
+
return COMMANDS[ns.command](ns.args)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == "__main__":
|
|
197
|
+
sys.exit(main())
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
src/aisafe/config/__init__.py
|
|
3
|
+
---------------------------
|
|
4
|
+
Packaged configuration presets.
|
|
5
|
+
|
|
6
|
+
The YAML presets ship as package data, so they are readable from an installed
|
|
7
|
+
wheel via importlib.resources -- not just from a source checkout. A copy is
|
|
8
|
+
also kept at the repo-root `config/` directory for local development; the two
|
|
9
|
+
are identical.
|
|
10
|
+
|
|
11
|
+
from aisafe.config import preset_path, load_preset
|
|
12
|
+
|
|
13
|
+
GuardrailPipeline.from_config(preset_path("default.yaml"))
|
|
14
|
+
text = load_preset("eu_high_risk.yaml")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from contextlib import ExitStack
|
|
20
|
+
from importlib import resources
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
__all__ = ["PRESETS", "PRICES_FILE", "ATTEST_LEVELS_FILE", "preset_path", "load_preset"]
|
|
24
|
+
|
|
25
|
+
# Pipeline presets: each builds a GuardrailPipeline.
|
|
26
|
+
PRESETS = ("default.yaml", "eu_high_risk.yaml")
|
|
27
|
+
# Other packaged data read through `preset_path()`; not pipelines, so not in PRESETS.
|
|
28
|
+
PRICES_FILE = "prices.yaml" # the dated price table `aisafe decide` joins tokens with
|
|
29
|
+
ATTEST_LEVELS_FILE = "attest-levels.yaml" # the level criteria `aisafe attest` checks
|
|
30
|
+
|
|
31
|
+
# Keeps extracted resources alive for the process lifetime. Needed when the
|
|
32
|
+
# package is loaded from a zip, where as_file() materialises a temp copy.
|
|
33
|
+
_FILES = ExitStack()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def preset_path(name: str) -> Path:
|
|
37
|
+
"""
|
|
38
|
+
Filesystem path to a packaged preset (e.g. "default.yaml", or
|
|
39
|
+
"nemo_rails/config.yml"). Works from a wheel as well as a checkout.
|
|
40
|
+
"""
|
|
41
|
+
resource = resources.files(__package__)
|
|
42
|
+
for part in name.split("/"):
|
|
43
|
+
resource = resource / part
|
|
44
|
+
if not resource.is_file():
|
|
45
|
+
raise FileNotFoundError(f"No packaged config named {name!r}")
|
|
46
|
+
return _FILES.enter_context(resources.as_file(resource))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_preset(name: str) -> str:
|
|
50
|
+
"""Read a packaged preset as text."""
|
|
51
|
+
resource = resources.files(__package__)
|
|
52
|
+
for part in name.split("/"):
|
|
53
|
+
resource = resource / part
|
|
54
|
+
return resource.read_text(encoding="utf-8")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# attest-levels.yaml
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# The criteria `aisafe attest` evaluates. A level is reached when every criterion of
|
|
4
|
+
# that level and of every level below it is met. The level is a list of criteria met
|
|
5
|
+
# on a date, signed by a named owner; it is not a compliance verdict, and no renderer
|
|
6
|
+
# calls it one.
|
|
7
|
+
#
|
|
8
|
+
# Shipped in the wheel (src/aisafe/config/) and mirrored at config/; the two are kept
|
|
9
|
+
# identical by a test. `--level-criteria PATH` points `aisafe attest` at another file.
|
|
10
|
+
#
|
|
11
|
+
# Criterion kinds (attest.CRITERION_KINDS):
|
|
12
|
+
# report_present(max_age_days) an audit report, fresh enough
|
|
13
|
+
# report_names_head report.target.git_sha equals HEAD
|
|
14
|
+
# unknown_listed the report carries its UNKNOWN list
|
|
15
|
+
# no_open_findings_at_or_above(severity) every finding at or above it has an accepted reason
|
|
16
|
+
# external_tools_attempted no external scanner skipped by --no-external
|
|
17
|
+
# accepted_reasons_present every baseline fingerprint carries a reason
|
|
18
|
+
# document_present(document, max_age_days) a measure | probe | decision document, fresh
|
|
19
|
+
# measure_thresholds no guard over a declared threshold, none unavailable
|
|
20
|
+
# probe_passing_if_present failed == 0 and errors == 0 when a probe report exists
|
|
21
|
+
# rules_closed(rules) no open finding of those rules
|
|
22
|
+
# card_fields_set(fields) the system card's listed fields are not placeholders
|
|
23
|
+
# card_section_fresh(section, fields, date_field, max_age_days) a card block set, recently dated
|
|
24
|
+
# accepted_risks_owned every accepted risk is written next to the owner
|
|
25
|
+
# tree_clean no uncommitted changes (governed = reproducible)
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
schema: aisg/1
|
|
28
|
+
kind: attest-levels
|
|
29
|
+
review_interval_days: 90
|
|
30
|
+
levels:
|
|
31
|
+
- level: 0
|
|
32
|
+
name: audited
|
|
33
|
+
criteria:
|
|
34
|
+
- {id: L0.report, kind: report_present, max_age_days: 30}
|
|
35
|
+
- {id: L0.commit, kind: report_names_head}
|
|
36
|
+
- {id: L0.unknown, kind: unknown_listed}
|
|
37
|
+
- level: 1
|
|
38
|
+
name: triaged
|
|
39
|
+
criteria:
|
|
40
|
+
- {id: L1.no_open_high, kind: no_open_findings_at_or_above, severity: high}
|
|
41
|
+
- {id: L1.scanners, kind: external_tools_attempted}
|
|
42
|
+
- {id: L1.reasons, kind: accepted_reasons_present}
|
|
43
|
+
- level: 2
|
|
44
|
+
name: measured
|
|
45
|
+
criteria:
|
|
46
|
+
- {id: L2.measure, kind: document_present, document: measure, max_age_days: 30}
|
|
47
|
+
- {id: L2.thresholds, kind: measure_thresholds}
|
|
48
|
+
- {id: L2.probe, kind: probe_passing_if_present}
|
|
49
|
+
- {id: L2.decision, kind: document_present, document: decision, max_age_days: 30}
|
|
50
|
+
- {id: L2.evals_ci, kind: rules_closed, rules: [AUD-901, AUD-903]}
|
|
51
|
+
- level: 3
|
|
52
|
+
name: governed
|
|
53
|
+
criteria:
|
|
54
|
+
- {id: L3.card, kind: card_fields_set, fields: [system.purpose, affected_persons, incident_contact, risk_tier, deployment]}
|
|
55
|
+
- {id: L3.prompt_audit, kind: card_section_fresh, section: prompt_audit, fields: [reviewed_at, reviewer], date_field: reviewed_at, max_age_days: 180}
|
|
56
|
+
- {id: L3.controls, kind: rules_closed, rules: [AUD-107, AUD-701, AUD-702, AUD-703, AUD-1107]}
|
|
57
|
+
- {id: L3.owner, kind: accepted_risks_owned}
|
|
58
|
+
- {id: L3.tree, kind: tree_clean}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# config/default.yaml
|
|
2
|
+
# -------------------------------------------------------
|
|
3
|
+
# AnyAISafe — Default Pipeline Configuration
|
|
4
|
+
# -------------------------------------------------------
|
|
5
|
+
# All modules are optional. Set enabled: false to skip any.
|
|
6
|
+
# Override per environment: production.yaml, eu_high_risk.yaml, etc.
|
|
7
|
+
|
|
8
|
+
pipeline:
|
|
9
|
+
mode: async
|
|
10
|
+
parallel_checks: true # Run input checks in parallel for lower latency
|
|
11
|
+
fail_open: false # If true, guardrail errors allow traffic through (NOT recommended for prod)
|
|
12
|
+
request_timeout: 30.0 # Max seconds for the LLM call in run_full() (0 = no timeout)
|
|
13
|
+
|
|
14
|
+
# ⑤ OBSERVABILITY (OpenTelemetry)
|
|
15
|
+
# Install: pip install "aisafe[otel]"
|
|
16
|
+
# Pass a TelemetryProvider instance to GuardrailPipeline(telemetry_provider=...).
|
|
17
|
+
# Backends: set exporter to "otlp" and point otlp_endpoint at any OTLP collector
|
|
18
|
+
# (Jaeger, Zipkin, Datadog, Honeycomb, Grafana Tempo, AWS X-Ray via ADOT, etc.)
|
|
19
|
+
#
|
|
20
|
+
# observability:
|
|
21
|
+
# enabled: true
|
|
22
|
+
# service_name: my-ai-system
|
|
23
|
+
# exporter: console # "console" | "otlp" | "none"
|
|
24
|
+
# otlp_endpoint: http://localhost:4317
|
|
25
|
+
# otlp_protocol: grpc # "grpc" | "http"
|
|
26
|
+
# metric_export_interval_ms: 60000
|
|
27
|
+
|
|
28
|
+
# ① INPUT GUARDRAILS
|
|
29
|
+
input:
|
|
30
|
+
pii_detector:
|
|
31
|
+
enabled: true
|
|
32
|
+
action: redact # redact | block | flag
|
|
33
|
+
entities:
|
|
34
|
+
- EMAIL
|
|
35
|
+
- PHONE_US
|
|
36
|
+
- PHONE_INTL
|
|
37
|
+
- SSN
|
|
38
|
+
- CREDIT_CARD
|
|
39
|
+
- IP_ADDRESS
|
|
40
|
+
use_presidio: false # Set true if presidio-analyzer is installed
|
|
41
|
+
|
|
42
|
+
prompt_injection:
|
|
43
|
+
enabled: true
|
|
44
|
+
sensitivity: medium # low | medium | high
|
|
45
|
+
check_base64: true
|
|
46
|
+
llm_judge: false # Enable for high-security deployments (+latency)
|
|
47
|
+
log_attempts: true
|
|
48
|
+
|
|
49
|
+
topic_classifier:
|
|
50
|
+
enabled: false # Enable and configure allowed_topics for your use case
|
|
51
|
+
# allowed_topics:
|
|
52
|
+
# - customer_support
|
|
53
|
+
# - billing
|
|
54
|
+
# - product_info
|
|
55
|
+
# fallback_message: "I can only help with customer support, billing, and product topics."
|
|
56
|
+
|
|
57
|
+
toxicity_input:
|
|
58
|
+
enabled: false
|
|
59
|
+
# threshold: 0.8
|
|
60
|
+
# categories: [self_harm_promotion, violent_threats, dangerous_instructions]
|
|
61
|
+
|
|
62
|
+
rate_limiter:
|
|
63
|
+
enabled: true
|
|
64
|
+
requests_per_minute: 60
|
|
65
|
+
tokens_per_day: 100000
|
|
66
|
+
key_field: user_id # context field to key limits on
|
|
67
|
+
count_tokens: true
|
|
68
|
+
|
|
69
|
+
# ② PROCESSING GUARDRAILS (tool use / agent control)
|
|
70
|
+
processing:
|
|
71
|
+
tool_policy:
|
|
72
|
+
enabled: true
|
|
73
|
+
default_deny: true
|
|
74
|
+
default_role: user
|
|
75
|
+
approval_timeout: 30.0 # Seconds to wait for approval callback before denying
|
|
76
|
+
max_tool_calls_per_session: 0 # 0 = unlimited; set e.g. 50 to cap agentic loops
|
|
77
|
+
max_calls_per_tool: 0 # 0 = unlimited per-tool cap
|
|
78
|
+
# require_approval:
|
|
79
|
+
# - send_email
|
|
80
|
+
# - database_write
|
|
81
|
+
# - payment_*
|
|
82
|
+
# policies:
|
|
83
|
+
# user:
|
|
84
|
+
# allow: [search, calculator, fetch_url, read_file]
|
|
85
|
+
# deny: [file_write, exec_code, shell_command]
|
|
86
|
+
# argument_rules: # Argument-level constraints per tool
|
|
87
|
+
# read_file:
|
|
88
|
+
# path: "./data/*" # Only allow reads inside ./data/
|
|
89
|
+
# fetch_url:
|
|
90
|
+
# url: "https://*.trusted-domain.com/*"
|
|
91
|
+
# admin:
|
|
92
|
+
# allow: ["*"]
|
|
93
|
+
# deny: [shell_command]
|
|
94
|
+
|
|
95
|
+
context_control:
|
|
96
|
+
enabled: false # Not yet implemented
|
|
97
|
+
# max_rag_results: 5
|
|
98
|
+
# allowed_collections: [public_docs, kb_articles]
|
|
99
|
+
|
|
100
|
+
memory_hygiene:
|
|
101
|
+
enabled: false # Not yet implemented
|
|
102
|
+
# ttl_seconds: 3600
|
|
103
|
+
# max_memory_tokens: 4096
|
|
104
|
+
|
|
105
|
+
# ③ OUTPUT GUARDRAILS
|
|
106
|
+
output:
|
|
107
|
+
toxicity_output:
|
|
108
|
+
enabled: true
|
|
109
|
+
threshold: 0.7
|
|
110
|
+
action: block # block | warn | flag
|
|
111
|
+
categories:
|
|
112
|
+
- self_harm_promotion
|
|
113
|
+
- violent_threats
|
|
114
|
+
- dangerous_instructions
|
|
115
|
+
use_llm_judge: false # Enable for higher accuracy (+latency)
|
|
116
|
+
|
|
117
|
+
manipulation_output:
|
|
118
|
+
enabled: true
|
|
119
|
+
action: flag # flag | block; manipulation is contextual, measure before blocking
|
|
120
|
+
min_severity: medium # findings below it are recorded, never acted on
|
|
121
|
+
use_llm_judge: false # ClaudeJudge(mode="manipulation"), one model call per response
|
|
122
|
+
|
|
123
|
+
ai_disclosure_output:
|
|
124
|
+
enabled: true
|
|
125
|
+
action: flag # flag | redact | block
|
|
126
|
+
append_when: on_claim # never | on_claim | always
|
|
127
|
+
|
|
128
|
+
pii_restorer:
|
|
129
|
+
enabled: false # Enable when using PIIDetector(action="tokenize") on input
|
|
130
|
+
# Restores <PII:TYPE:N> tokens to original values after LLM response
|
|
131
|
+
|
|
132
|
+
hallucination:
|
|
133
|
+
enabled: false # Not yet implemented
|
|
134
|
+
# threshold: 0.6
|
|
135
|
+
# citation_required: false
|
|
136
|
+
|
|
137
|
+
schema_validator:
|
|
138
|
+
enabled: false # Not yet implemented
|
|
139
|
+
# schema: {}
|
|
140
|
+
# strict: false
|
|
141
|
+
|
|
142
|
+
content_policy:
|
|
143
|
+
enabled: false # Not yet implemented
|
|
144
|
+
# rules: []
|
|
145
|
+
|
|
146
|
+
# ④ POLICY COMPLIANCE
|
|
147
|
+
policy:
|
|
148
|
+
eu_ai_act:
|
|
149
|
+
enabled: true
|
|
150
|
+
risk_tier: limited # unacceptable | high | limited | minimal
|
|
151
|
+
system_id: my-ai-system
|
|
152
|
+
provider_name: My Organization
|
|
153
|
+
enable_audit_log: false # Set true for high-risk tier
|
|
154
|
+
audit_log_path: audit/eu_ai_act.jsonl
|
|
155
|
+
audit_retention_days: 365
|
|
156
|
+
check_prohibited: true
|
|
157
|
+
check_high_risk_indicators: true
|
|
158
|
+
strict_mode: true
|
|
159
|
+
|
|
160
|
+
gdpr:
|
|
161
|
+
enabled: false
|
|
162
|
+
# data_minimization: true
|
|
163
|
+
# purpose_limitation: true
|