opencomplai-core 0.1.0__tar.gz
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.
- opencomplai_core-0.1.0/.gitignore +69 -0
- opencomplai_core-0.1.0/PKG-INFO +108 -0
- opencomplai_core-0.1.0/README.md +90 -0
- opencomplai_core-0.1.0/pyproject.toml +29 -0
- opencomplai_core-0.1.0/src/opencomplai_core/__init__.py +22 -0
- opencomplai_core-0.1.0/src/opencomplai_core/adapters/__init__.py +25 -0
- opencomplai_core-0.1.0/src/opencomplai_core/adapters/base.py +33 -0
- opencomplai_core-0.1.0/src/opencomplai_core/adapters/http.py +57 -0
- opencomplai_core-0.1.0/src/opencomplai_core/adapters/offline.py +27 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/__init__.py +28 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/bridge.py +30 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/catalog.py +50 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/data/help_content.json +56 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/data/obligations.json +67 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/data/status_changes.json +34 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/engine.py +292 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/models.py +73 -0
- opencomplai_core-0.1.0/src/opencomplai_core/compliance_checker/report.py +141 -0
- opencomplai_core-0.1.0/src/opencomplai_core/dossier.py +155 -0
- opencomplai_core-0.1.0/src/opencomplai_core/engine.py +70 -0
- opencomplai_core-0.1.0/src/opencomplai_core/eval_engine.py +128 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/__init__.py +5 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/_hashing.py +26 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/base.py +26 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/bias.py +158 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/data/safety_signatures.json +27 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/leakage.py +174 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/registry.py +15 -0
- opencomplai_core-0.1.0/src/opencomplai_core/evaluators/safety.py +107 -0
- opencomplai_core-0.1.0/src/opencomplai_core/models.py +708 -0
- opencomplai_core-0.1.0/src/opencomplai_core/purge.py +23 -0
- opencomplai_core-0.1.0/src/opencomplai_core/rules.py +360 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scan_engine.py +644 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/__init__.py +11 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/_hashing.py +64 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/ai_usage_gate.py +320 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/base.py +34 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/cache.py +86 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/config_ai_sites.py +67 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/constants.py +5 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/data/ai_signals.json +10 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/__init__.py +1 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/_common.py +55 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/_signals.py +104 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/ai_dependency.py +61 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/artifact.py +56 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/ast_usage.py +95 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/biometric.py +74 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/dataflow.py +73 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/endpoint.py +61 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/detectors/semantic.py +98 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/extractors/__init__.py +1 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/extractors/artifacts.py +24 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/extractors/ast.py +80 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/extractors/config.py +69 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/extractors/manifests.py +109 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/extractors/notebooks.py +51 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/feature_types.py +99 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/features.py +84 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/file_ai_context.py +99 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/fusion.py +95 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/inventory.py +242 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/mapping.py +77 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/ocignore.py +315 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/registry.py +22 -0
- opencomplai_core-0.1.0/src/opencomplai_core/scanner/snippet_cache.py +58 -0
- opencomplai_core-0.1.0/src/opencomplai_core/signing.py +152 -0
- opencomplai_core-0.1.0/src/opencomplai_core/state_machine.py +123 -0
- opencomplai_core-0.1.0/src/opencomplai_core/telemetry.py +181 -0
- opencomplai_core-0.1.0/src/opencomplai_core/verification.py +121 -0
- opencomplai_core-0.1.0/tests/__init__.py +1 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/01_auth_rep_only.json +23 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/02_out_of_scope_not_ai.json +21 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/03_out_of_scope_s1.json +25 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/04_out_of_scope_excluded.json +27 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/05_prohibited.json +32 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/06_high_risk_provider.json +31 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/07_high_risk_deployer_fria.json +37 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/08_become_provider.json +34 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/09_gpai_provider.json +31 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/10_gpai_systemic_risk.json +34 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/11_transparency_only_deployer.json +30 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/12_product_manufacturer_out_of_scope.json +25 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/13_high_risk_exception_deployer.json +36 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/14_distributor_high_risk.json +29 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/15_distributor_not_high_risk.json +23 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/16_handover_provider.json +34 -0
- opencomplai_core-0.1.0/tests/fixtures/fli_golden/17_hr6_not_high_risk.json +28 -0
- opencomplai_core-0.1.0/tests/test_ai_usage_gate.py +159 -0
- opencomplai_core-0.1.0/tests/test_code_signal_matching.py +57 -0
- opencomplai_core-0.1.0/tests/test_compliance_checker_engine.py +134 -0
- opencomplai_core-0.1.0/tests/test_compliance_checker_golden.py +38 -0
- opencomplai_core-0.1.0/tests/test_compliance_checker_report.py +50 -0
- opencomplai_core-0.1.0/tests/test_config_ai_sites.py +121 -0
- opencomplai_core-0.1.0/tests/test_engine.py +79 -0
- opencomplai_core-0.1.0/tests/test_eval_engine.py +27 -0
- opencomplai_core-0.1.0/tests/test_evaluators_bias.py +54 -0
- opencomplai_core-0.1.0/tests/test_evaluators_leakage.py +43 -0
- opencomplai_core-0.1.0/tests/test_evaluators_safety.py +48 -0
- opencomplai_core-0.1.0/tests/test_false_positive_controls.py +55 -0
- opencomplai_core-0.1.0/tests/test_feature_extractors.py +69 -0
- opencomplai_core-0.1.0/tests/test_gold_set.py +114 -0
- opencomplai_core-0.1.0/tests/test_no_source_in_evidence.py +25 -0
- opencomplai_core-0.1.0/tests/test_ocignore.py +137 -0
- opencomplai_core-0.1.0/tests/test_purge.py +44 -0
- opencomplai_core-0.1.0/tests/test_repo_inventory.py +91 -0
- opencomplai_core-0.1.0/tests/test_scan_engine.py +268 -0
- opencomplai_core-0.1.0/tests/test_scanner_cache.py +47 -0
- opencomplai_core-0.1.0/tests/test_scanner_detectors.py +47 -0
- opencomplai_core-0.1.0/tests/test_scanner_mapping.py +61 -0
- opencomplai_core-0.1.0/tests/test_scanner_models.py +125 -0
- opencomplai_core-0.1.0/tests/test_signal_fusion.py +85 -0
- opencomplai_core-0.1.0/tests/test_signals_matching.py +28 -0
- opencomplai_core-0.1.0/tests/test_signing.py +104 -0
- opencomplai_core-0.1.0/tests/test_state_machine.py +74 -0
- opencomplai_core-0.1.0/tests/test_telemetry.py +72 -0
- opencomplai_core-0.1.0/tests/test_verification.py +172 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.coverage
|
|
10
|
+
htmlcov/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
|
|
13
|
+
# uv — DO NOT ignore uv.lock
|
|
14
|
+
.venv/
|
|
15
|
+
|
|
16
|
+
# Node.js
|
|
17
|
+
node_modules/
|
|
18
|
+
dist/
|
|
19
|
+
*.js.map
|
|
20
|
+
.pnpm-store/
|
|
21
|
+
|
|
22
|
+
# Editors
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
*.swp
|
|
26
|
+
.DS_Store
|
|
27
|
+
.cursor/*
|
|
28
|
+
.autopilot.json
|
|
29
|
+
|
|
30
|
+
# Secrets
|
|
31
|
+
.env
|
|
32
|
+
*.pem
|
|
33
|
+
*.key
|
|
34
|
+
|
|
35
|
+
# Docs build output
|
|
36
|
+
site/
|
|
37
|
+
docs/site/
|
|
38
|
+
docs-env/
|
|
39
|
+
.mkdocs/
|
|
40
|
+
|
|
41
|
+
# Build artifacts
|
|
42
|
+
.mintlify/
|
|
43
|
+
|
|
44
|
+
# Opencomplai generated artifacts (output of `opencomplai init` / `opencomplai check`)
|
|
45
|
+
compliance-artifact.json
|
|
46
|
+
system-manifest.json
|
|
47
|
+
# ...but keep the committed example fixtures used by the scanner docs
|
|
48
|
+
!examples/sample-system/**/system-manifest.json
|
|
49
|
+
!tests/fixtures/**/system-manifest.json
|
|
50
|
+
.venv-ci-check/*
|
|
51
|
+
# GitHub issues sync output (local mirror; may contain PII from issues)
|
|
52
|
+
github-issues/
|
|
53
|
+
|
|
54
|
+
docs/src/assets/js/checker-widget.js
|
|
55
|
+
docs/checker-widget/src/data/
|
|
56
|
+
docs/checker-widget/node_modules/
|
|
57
|
+
packages/cli/src/opencomplai_cli/data/checker-local.html
|
|
58
|
+
|
|
59
|
+
# --- Public-repo hardening (keep AI-tooling and internal artifacts out) ---
|
|
60
|
+
.claude/
|
|
61
|
+
.cursor/
|
|
62
|
+
.autopilot.json
|
|
63
|
+
.selfaudit/
|
|
64
|
+
compliance-reports/
|
|
65
|
+
*.plan.md
|
|
66
|
+
x.key
|
|
67
|
+
.tmp-*.db
|
|
68
|
+
ci-logs/
|
|
69
|
+
tmp/
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opencomplai-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Opencomplai core risk assessment engine for EU AI Act compliance
|
|
5
|
+
Project-URL: Homepage, https://opencomplai.com
|
|
6
|
+
Project-URL: Documentation, https://docs.opencomplai.com
|
|
7
|
+
Author-email: Opencomplai <hello@opencomplai.com>
|
|
8
|
+
License: AGPL-3.0-only
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Requires-Dist: cryptography>=48.0.1
|
|
11
|
+
Requires-Dist: pydantic>=2.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
15
|
+
Provides-Extra: reports
|
|
16
|
+
Requires-Dist: fpdf2>=2.7; extra == 'reports'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# opencomplai-core
|
|
20
|
+
|
|
21
|
+
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
22
|
+
[](https://pypi.org/project/opencomplai-core/)
|
|
23
|
+
[](https://www.python.org/)
|
|
24
|
+
|
|
25
|
+
The EU AI Act compliance risk engine at the heart of [Opencomplai](https://opencomplai.com).
|
|
26
|
+
`opencomplai-core` turns a declared `system-manifest.json` and your source tree into a
|
|
27
|
+
deterministic, rule-based risk classification — no LLM calls, no network access, fully
|
|
28
|
+
reproducible.
|
|
29
|
+
|
|
30
|
+
It powers risk classification (`UnacceptableRiskRule`, `AnnexIIIClassifierRule`,
|
|
31
|
+
`ProfilingDetectionRule`, `SubstantialModificationRule`) and the code-corroboration scan
|
|
32
|
+
engine that cross-checks what a manifest *claims* against what the code actually does.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install opencomplai-core
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For PDF report generation, install the optional extra:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install "opencomplai-core[reports]"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> Most users want the [`opencomplai`](https://pypi.org/project/opencomplai/) meta-package
|
|
47
|
+
> (engine + CLI) or the [`opencomplai-cli`](https://pypi.org/project/opencomplai-cli/)
|
|
48
|
+
> command-line tool. Install `opencomplai-core` directly when you are embedding the engine
|
|
49
|
+
> in your own application.
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
### Classify a model from a declared manifest
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from opencomplai import assess, AssessmentInput, ModelMetadata
|
|
57
|
+
|
|
58
|
+
result = assess(AssessmentInput(
|
|
59
|
+
model=ModelMetadata(
|
|
60
|
+
name="loan-scorer",
|
|
61
|
+
version="1.0.0",
|
|
62
|
+
modality="tabular",
|
|
63
|
+
use_case="creditworthiness scoring for consumer loans",
|
|
64
|
+
deployment_context="production",
|
|
65
|
+
)
|
|
66
|
+
))
|
|
67
|
+
|
|
68
|
+
print(result.risk_level) # e.g. RiskLevel.HIGH
|
|
69
|
+
for rule in result.rule_results:
|
|
70
|
+
print(rule.rule_id, "PASS" if rule.passed else "FAIL")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Corroborate a manifest against the code
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from pathlib import Path
|
|
77
|
+
from opencomplai_core.scan_engine import run_scan
|
|
78
|
+
|
|
79
|
+
report = run_scan(
|
|
80
|
+
repo_root=Path("."),
|
|
81
|
+
commit_ref="HEAD",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
print(report.summary.result) # PASS / CONTROL_FAIL / ...
|
|
85
|
+
for finding in report.findings:
|
|
86
|
+
print(finding.finding_id, finding.mapped_taxonomy)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The scan engine extracts features from the repository, fuses evidence across detectors,
|
|
90
|
+
and maps findings to EU AI Act taxonomy (Annex III high-risk areas, Article 5 prohibited
|
|
91
|
+
practices, profiling under Article 6).
|
|
92
|
+
|
|
93
|
+
## What you get
|
|
94
|
+
|
|
95
|
+
- **Deterministic risk classification** — same inputs always produce the same output, so
|
|
96
|
+
results are auditable and CI-gateable.
|
|
97
|
+
- **Code corroboration** — detect when a manifest under-declares (claims minimal risk while
|
|
98
|
+
the code does biometric identification, profiling, etc.).
|
|
99
|
+
- **Merkle-linked evidence** — findings carry verifiable evidence items for audit trails.
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
Full docs, the EU AI Act concepts guide, and the SDK reference live at
|
|
104
|
+
**[docs.opencomplai.com](https://docs.opencomplai.com)**.
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
AGPL-3.0-only. See [LICENSE](https://www.gnu.org/licenses/agpl-3.0).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# opencomplai-core
|
|
2
|
+
|
|
3
|
+
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
4
|
+
[](https://pypi.org/project/opencomplai-core/)
|
|
5
|
+
[](https://www.python.org/)
|
|
6
|
+
|
|
7
|
+
The EU AI Act compliance risk engine at the heart of [Opencomplai](https://opencomplai.com).
|
|
8
|
+
`opencomplai-core` turns a declared `system-manifest.json` and your source tree into a
|
|
9
|
+
deterministic, rule-based risk classification — no LLM calls, no network access, fully
|
|
10
|
+
reproducible.
|
|
11
|
+
|
|
12
|
+
It powers risk classification (`UnacceptableRiskRule`, `AnnexIIIClassifierRule`,
|
|
13
|
+
`ProfilingDetectionRule`, `SubstantialModificationRule`) and the code-corroboration scan
|
|
14
|
+
engine that cross-checks what a manifest *claims* against what the code actually does.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install opencomplai-core
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For PDF report generation, install the optional extra:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install "opencomplai-core[reports]"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> Most users want the [`opencomplai`](https://pypi.org/project/opencomplai/) meta-package
|
|
29
|
+
> (engine + CLI) or the [`opencomplai-cli`](https://pypi.org/project/opencomplai-cli/)
|
|
30
|
+
> command-line tool. Install `opencomplai-core` directly when you are embedding the engine
|
|
31
|
+
> in your own application.
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
### Classify a model from a declared manifest
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from opencomplai import assess, AssessmentInput, ModelMetadata
|
|
39
|
+
|
|
40
|
+
result = assess(AssessmentInput(
|
|
41
|
+
model=ModelMetadata(
|
|
42
|
+
name="loan-scorer",
|
|
43
|
+
version="1.0.0",
|
|
44
|
+
modality="tabular",
|
|
45
|
+
use_case="creditworthiness scoring for consumer loans",
|
|
46
|
+
deployment_context="production",
|
|
47
|
+
)
|
|
48
|
+
))
|
|
49
|
+
|
|
50
|
+
print(result.risk_level) # e.g. RiskLevel.HIGH
|
|
51
|
+
for rule in result.rule_results:
|
|
52
|
+
print(rule.rule_id, "PASS" if rule.passed else "FAIL")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Corroborate a manifest against the code
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from pathlib import Path
|
|
59
|
+
from opencomplai_core.scan_engine import run_scan
|
|
60
|
+
|
|
61
|
+
report = run_scan(
|
|
62
|
+
repo_root=Path("."),
|
|
63
|
+
commit_ref="HEAD",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(report.summary.result) # PASS / CONTROL_FAIL / ...
|
|
67
|
+
for finding in report.findings:
|
|
68
|
+
print(finding.finding_id, finding.mapped_taxonomy)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The scan engine extracts features from the repository, fuses evidence across detectors,
|
|
72
|
+
and maps findings to EU AI Act taxonomy (Annex III high-risk areas, Article 5 prohibited
|
|
73
|
+
practices, profiling under Article 6).
|
|
74
|
+
|
|
75
|
+
## What you get
|
|
76
|
+
|
|
77
|
+
- **Deterministic risk classification** — same inputs always produce the same output, so
|
|
78
|
+
results are auditable and CI-gateable.
|
|
79
|
+
- **Code corroboration** — detect when a manifest under-declares (claims minimal risk while
|
|
80
|
+
the code does biometric identification, profiling, etc.).
|
|
81
|
+
- **Merkle-linked evidence** — findings carry verifiable evidence items for audit trails.
|
|
82
|
+
|
|
83
|
+
## Documentation
|
|
84
|
+
|
|
85
|
+
Full docs, the EU AI Act concepts guide, and the SDK reference live at
|
|
86
|
+
**[docs.opencomplai.com](https://docs.opencomplai.com)**.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
AGPL-3.0-only. See [LICENSE](https://www.gnu.org/licenses/agpl-3.0).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "opencomplai-core"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Opencomplai core risk assessment engine for EU AI Act compliance"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "AGPL-3.0-only" }
|
|
7
|
+
authors = [{ name = "Opencomplai", email = "hello@opencomplai.com" }]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"pydantic>=2.0",
|
|
11
|
+
"cryptography>=48.0.1",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://opencomplai.com"
|
|
16
|
+
Documentation = "https://docs.opencomplai.com"
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=7.0",
|
|
21
|
+
"ruff>=0.1.0",
|
|
22
|
+
]
|
|
23
|
+
reports = [
|
|
24
|
+
"fpdf2>=2.7",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["hatchling"]
|
|
29
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Opencomplai core risk assessment engine."""
|
|
2
|
+
|
|
3
|
+
from opencomplai_core.engine import assess
|
|
4
|
+
from opencomplai_core.models import (
|
|
5
|
+
AssessmentInput,
|
|
6
|
+
EvidenceObject,
|
|
7
|
+
LedgerEvent,
|
|
8
|
+
RiskResult,
|
|
9
|
+
ScanStatusArtifact,
|
|
10
|
+
SystemManifest,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AssessmentInput",
|
|
16
|
+
"EvidenceObject",
|
|
17
|
+
"LedgerEvent",
|
|
18
|
+
"RiskResult",
|
|
19
|
+
"ScanStatusArtifact",
|
|
20
|
+
"SystemManifest",
|
|
21
|
+
"assess",
|
|
22
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Source adapter registry for the Ground Truth Verification Graph."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from opencomplai_core.adapters.base import SourceAdapter
|
|
6
|
+
from opencomplai_core.adapters.http import HTTPAdapter
|
|
7
|
+
from opencomplai_core.adapters.offline import OfflineAdapter
|
|
8
|
+
|
|
9
|
+
__all__ = ["HTTPAdapter", "OfflineAdapter", "SourceAdapter", "get_adapter"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_adapter(source_ref: str, egress_proxy_url: str = "") -> SourceAdapter:
|
|
13
|
+
"""
|
|
14
|
+
Return the appropriate SourceAdapter for source_ref.
|
|
15
|
+
|
|
16
|
+
Routing:
|
|
17
|
+
offline://* → OfflineAdapter (deterministic stub; safe in airgap/tests)
|
|
18
|
+
http://* | https://* → HTTPAdapter (routes through egress proxy)
|
|
19
|
+
(anything else) → OfflineAdapter as safe fallback
|
|
20
|
+
"""
|
|
21
|
+
if source_ref.startswith("offline://") or not source_ref.startswith(
|
|
22
|
+
("http://", "https://")
|
|
23
|
+
):
|
|
24
|
+
return OfflineAdapter()
|
|
25
|
+
return HTTPAdapter(egress_proxy_url=egress_proxy_url)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Abstract base class for ground-truth source adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SourceAdapter(ABC):
|
|
9
|
+
"""
|
|
10
|
+
Adapter that fetches a ground-truth value for a given claim.
|
|
11
|
+
|
|
12
|
+
Implementations must be safe to call concurrently and must not
|
|
13
|
+
store mutable state between calls.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def lookup(self, claim: dict) -> dict:
|
|
18
|
+
"""
|
|
19
|
+
Look up the ground-truth value for claim.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
claim: dict with at least claim_ref, source_ref, and optionally
|
|
23
|
+
expected_value, metric, threshold.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
dict with at least a "value" key containing the looked-up value.
|
|
27
|
+
May include additional metadata (e.g. source, timestamp).
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
RuntimeError: if the external source is unavailable (caller should
|
|
31
|
+
treat this as DEPENDENCY_UNAVAILABLE).
|
|
32
|
+
"""
|
|
33
|
+
...
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP adapter — routes external source lookups through the egress proxy.
|
|
3
|
+
|
|
4
|
+
All outbound HTTP calls must go through the egress proxy (REQ-ARC-001).
|
|
5
|
+
Raises RuntimeError on connectivity failure so the caller can handle
|
|
6
|
+
DEPENDENCY_UNAVAILABLE and queue for retry.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import urllib.error
|
|
13
|
+
import urllib.request
|
|
14
|
+
|
|
15
|
+
from opencomplai_core.adapters.base import SourceAdapter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HTTPAdapter(SourceAdapter):
|
|
19
|
+
"""
|
|
20
|
+
Fetches a ground-truth value from an external HTTP source via the egress proxy.
|
|
21
|
+
|
|
22
|
+
The egress proxy enforces the allowlisted schema and destinations
|
|
23
|
+
(PRD §4.4); this adapter just routes through it.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, egress_proxy_url: str = "http://egress-proxy:8004") -> None:
|
|
27
|
+
self._proxy_url = egress_proxy_url.rstrip("/")
|
|
28
|
+
|
|
29
|
+
async def lookup(self, claim: dict) -> dict:
|
|
30
|
+
source_ref = claim.get("source_ref", "")
|
|
31
|
+
if not source_ref:
|
|
32
|
+
raise RuntimeError("claim missing source_ref for HTTP lookup")
|
|
33
|
+
|
|
34
|
+
payload = json.dumps(
|
|
35
|
+
{
|
|
36
|
+
"destination": source_ref,
|
|
37
|
+
"claim_ref": claim.get("claim_ref", ""),
|
|
38
|
+
"expected_value": claim.get("expected_value"),
|
|
39
|
+
}
|
|
40
|
+
).encode("utf-8")
|
|
41
|
+
|
|
42
|
+
req = urllib.request.Request(
|
|
43
|
+
f"{self._proxy_url}/v1/egress/lookup",
|
|
44
|
+
data=payload,
|
|
45
|
+
headers={"Content-Type": "application/json"},
|
|
46
|
+
method="POST",
|
|
47
|
+
)
|
|
48
|
+
try:
|
|
49
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
50
|
+
return json.loads(resp.read())
|
|
51
|
+
except urllib.error.HTTPError as exc:
|
|
52
|
+
body = exc.read().decode(errors="replace")
|
|
53
|
+
raise RuntimeError(
|
|
54
|
+
f"Egress proxy returned {exc.code} for {source_ref}: {body}"
|
|
55
|
+
) from exc
|
|
56
|
+
except Exception as exc:
|
|
57
|
+
raise RuntimeError(f"HTTP adapter lookup failed: {exc}") from exc
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Offline (deterministic stub) adapter.
|
|
3
|
+
|
|
4
|
+
Used in airgap scan mode and all unit tests. Always returns the expected_value
|
|
5
|
+
from the claim, so every claim resolves as VERIFIED — this is intentional for
|
|
6
|
+
local/offline environments where external verification is unavailable.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from opencomplai_core.adapters.base import SourceAdapter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OfflineAdapter(SourceAdapter):
|
|
15
|
+
"""
|
|
16
|
+
Returns expected_value from the claim as the lookup result.
|
|
17
|
+
|
|
18
|
+
Produces a deterministic, dependency-free response suitable for
|
|
19
|
+
airgap mode and CI environments that cannot reach external sources.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
async def lookup(self, claim: dict) -> dict:
|
|
23
|
+
return {
|
|
24
|
+
"value": claim.get("expected_value"),
|
|
25
|
+
"source": "offline",
|
|
26
|
+
"deterministic": True,
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""EU AI Act Compliance Checker — FLI-parity decision engine."""
|
|
2
|
+
|
|
3
|
+
from opencomplai_core.compliance_checker.bridge import bridge_to_manifest_fields
|
|
4
|
+
from opencomplai_core.compliance_checker.engine import CHECKER_VERSION, evaluate
|
|
5
|
+
from opencomplai_core.compliance_checker.models import (
|
|
6
|
+
CheckerSession,
|
|
7
|
+
ComplianceCheckerResult,
|
|
8
|
+
EntityType,
|
|
9
|
+
)
|
|
10
|
+
from opencomplai_core.compliance_checker.report import (
|
|
11
|
+
export_all,
|
|
12
|
+
render_json,
|
|
13
|
+
render_markdown,
|
|
14
|
+
render_pdf,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"CHECKER_VERSION",
|
|
19
|
+
"CheckerSession",
|
|
20
|
+
"ComplianceCheckerResult",
|
|
21
|
+
"EntityType",
|
|
22
|
+
"bridge_to_manifest_fields",
|
|
23
|
+
"evaluate",
|
|
24
|
+
"export_all",
|
|
25
|
+
"render_json",
|
|
26
|
+
"render_markdown",
|
|
27
|
+
"render_pdf",
|
|
28
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Bridge compliance checker results to system manifest fields."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from opencomplai_core.compliance_checker.models import ComplianceCheckerResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def bridge_to_manifest_fields(result: ComplianceCheckerResult) -> dict[str, object]:
|
|
9
|
+
"""Map checker output to SystemManifest-compatible fields."""
|
|
10
|
+
entity = result.effective_entity
|
|
11
|
+
operator_role = entity.value if entity is not None else "unknown"
|
|
12
|
+
|
|
13
|
+
if result.is_prohibited:
|
|
14
|
+
intended_purpose = "prohibited_practice"
|
|
15
|
+
elif result.is_high_risk:
|
|
16
|
+
intended_purpose = "high_risk_ai_system"
|
|
17
|
+
elif any(item.id == "transparency" for item in result.obligations):
|
|
18
|
+
intended_purpose = "limited_risk_transparency"
|
|
19
|
+
elif any(item.id == "gpai_provider" for item in result.obligations):
|
|
20
|
+
intended_purpose = "general_purpose_ai_model"
|
|
21
|
+
elif result.in_scope:
|
|
22
|
+
intended_purpose = "in_scope_ai_system"
|
|
23
|
+
else:
|
|
24
|
+
intended_purpose = "out_of_scope"
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
"operator_role": operator_role,
|
|
28
|
+
"intended_purpose": intended_purpose,
|
|
29
|
+
"high_risk_presumption": result.is_high_risk,
|
|
30
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Load FLI catalog JSON from the bundled data directory."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from opencomplai_core.compliance_checker.models import ObligationItem, StatusChangeItem
|
|
10
|
+
|
|
11
|
+
_DATA_DIR = Path(__file__).resolve().parent / "data"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _load_json(name: str) -> dict:
|
|
15
|
+
path = _DATA_DIR / name
|
|
16
|
+
with path.open(encoding="utf-8") as handle:
|
|
17
|
+
return json.load(handle)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@lru_cache(maxsize=1)
|
|
21
|
+
def load_obligations() -> dict[str, ObligationItem]:
|
|
22
|
+
raw = _load_json("obligations.json")
|
|
23
|
+
return {key: ObligationItem(id=key, **value) for key, value in raw.items()}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@lru_cache(maxsize=1)
|
|
27
|
+
def load_status_changes() -> dict[str, StatusChangeItem]:
|
|
28
|
+
raw = _load_json("status_changes.json")
|
|
29
|
+
return {key: StatusChangeItem(id=key, **value) for key, value in raw.items()}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@lru_cache(maxsize=1)
|
|
33
|
+
def load_help_content() -> dict[str, dict[str, str]]:
|
|
34
|
+
return _load_json("help_content.json")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_obligation(obligation_id: str) -> ObligationItem:
|
|
38
|
+
obligations = load_obligations()
|
|
39
|
+
if obligation_id not in obligations:
|
|
40
|
+
msg = f"Unknown obligation id: {obligation_id}"
|
|
41
|
+
raise KeyError(msg)
|
|
42
|
+
return obligations[obligation_id]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_status_change(status_id: str) -> StatusChangeItem:
|
|
46
|
+
status_changes = load_status_changes()
|
|
47
|
+
if status_id not in status_changes:
|
|
48
|
+
msg = f"Unknown status change id: {status_id}"
|
|
49
|
+
raise KeyError(msg)
|
|
50
|
+
return status_changes[status_id]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity_definitions": {
|
|
3
|
+
"title": "Operator roles under the AI Act",
|
|
4
|
+
"body": "Article 3(3) defines operators: a provider develops an AI system or has it developed and places it on the market or puts it into service under its own name or trademark; a deployer uses an AI system under its authority; a distributor makes an AI system available on the Union market; an importer places an AI system from a third country on the Union market; a product manufacturer places on the market an AI system together with its product under its own name or trademark; an authorised representative is a natural or legal person established in the Union who has received a written mandate from a provider."
|
|
5
|
+
},
|
|
6
|
+
"entity_roles": {
|
|
7
|
+
"provider": {
|
|
8
|
+
"title": "Provider",
|
|
9
|
+
"description": "You develop an AI system (or have it developed) and place it on the market or put it into service under your own name or trademark. In short: you build and ship it."
|
|
10
|
+
},
|
|
11
|
+
"deployer": {
|
|
12
|
+
"title": "Deployer",
|
|
13
|
+
"description": "You use an AI system under your own authority in a professional capacity — you are not an affected end-user. In short: you bought it and run it on people or data."
|
|
14
|
+
},
|
|
15
|
+
"distributor": {
|
|
16
|
+
"title": "Distributor",
|
|
17
|
+
"description": "You make an AI system available on the EU market in the supply chain, without being its provider or importer. In short: you are a reseller or sales channel."
|
|
18
|
+
},
|
|
19
|
+
"importer": {
|
|
20
|
+
"title": "Importer",
|
|
21
|
+
"description": "You are established in the EU and place on the market an AI system carrying the name or trademark of a provider established outside the EU. In short: you bring a non-EU system into the EU."
|
|
22
|
+
},
|
|
23
|
+
"product_manufacturer": {
|
|
24
|
+
"title": "Product manufacturer",
|
|
25
|
+
"description": "You place on the market or put into service an AI system together with your product and under your own name or trademark. In short: AI is built into a product you sell."
|
|
26
|
+
},
|
|
27
|
+
"authorised_rep": {
|
|
28
|
+
"title": "Authorised representative",
|
|
29
|
+
"description": "You are a person or entity in the EU holding a written mandate from a non-EU provider to act on its behalf for AI Act obligations."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"ai_system_definition": {
|
|
33
|
+
"title": "Definition of an AI system",
|
|
34
|
+
"body": "An AI system is a machine-based system designed to operate with varying levels of autonomy, that may exhibit adaptiveness after deployment and that, for explicit or implicit objectives, infers, from the input it receives, how to generate outputs such as predictions, content, recommendations, or decisions that can influence physical or virtual environments (Article 3(1))."
|
|
35
|
+
},
|
|
36
|
+
"high_risk_overview": {
|
|
37
|
+
"title": "High-risk classification",
|
|
38
|
+
"body": "An AI system is high-risk if it is a product covered by Union harmonisation legislation listed in Annex I with a required third-party conformity assessment, and the product includes an AI system as a safety component; or if it is listed in Annex III and none of the Article 6(3) exceptions apply."
|
|
39
|
+
},
|
|
40
|
+
"scope_overview": {
|
|
41
|
+
"title": "Scope of the AI Act",
|
|
42
|
+
"body": "The AI Act applies to providers placing AI systems on the Union market or putting them into service, deployers with a place of establishment or located in the Union, and to providers and deployers in third countries where the output is used in the Union. Certain exclusions apply under Article 2 for military, national security, research, and personal non-professional activities."
|
|
43
|
+
},
|
|
44
|
+
"modifications_overview": {
|
|
45
|
+
"title": "Substantial modifications",
|
|
46
|
+
"body": "A substantial modification of an AI system is a change that affects compliance with the AI Act and requires a new conformity assessment. A deployer, distributor, importer, or product manufacturer that substantially modifies a system becomes a provider under Article 25(1)(b)."
|
|
47
|
+
},
|
|
48
|
+
"gpai_overview": {
|
|
49
|
+
"title": "General-purpose AI models",
|
|
50
|
+
"body": "A general-purpose AI model is an AI model, including where trained with a large amount of data using self-supervision at scale, that displays significant generality and is capable of competently performing a wide range of distinct tasks. Chapter V sets obligations for GPAI model providers distinct from high-risk AI system obligations."
|
|
51
|
+
},
|
|
52
|
+
"disclaimer": {
|
|
53
|
+
"title": "Not legal advice",
|
|
54
|
+
"body": "This tool automates the Future of Life Institute compliance checker logic for educational and planning purposes. It does not constitute legal advice. Seek qualified legal counsel and follow national guidance for formal compliance decisions."
|
|
55
|
+
}
|
|
56
|
+
}
|