vereefy-sdk 0.2.1__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.
- vereefy_sdk-0.2.1/PKG-INFO +54 -0
- vereefy_sdk-0.2.1/README.md +30 -0
- vereefy_sdk-0.2.1/pyproject.toml +39 -0
- vereefy_sdk-0.2.1/setup.cfg +4 -0
- vereefy_sdk-0.2.1/tests/test_sdk.py +127 -0
- vereefy_sdk-0.2.1/vereefy/__init__.py +221 -0
- vereefy_sdk-0.2.1/vereefy_sdk.egg-info/PKG-INFO +54 -0
- vereefy_sdk-0.2.1/vereefy_sdk.egg-info/SOURCES.txt +10 -0
- vereefy_sdk-0.2.1/vereefy_sdk.egg-info/dependency_links.txt +1 -0
- vereefy_sdk-0.2.1/vereefy_sdk.egg-info/requires.txt +5 -0
- vereefy_sdk-0.2.1/vereefy_sdk.egg-info/top_level.txt +2 -0
- vereefy_sdk-0.2.1/verefy/__init__.py +33 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vereefy-sdk
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: VEREEFY Python SDK — EU AI Act Compliance-Infrastruktur
|
|
5
|
+
Author-email: VEREEFY <hello@vereefy.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://vereefy.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.vereefy.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/vereefy-ai/vereefy-python
|
|
10
|
+
Keywords: EU AI Act,compliance,KI,audit,vereefy
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: httpx>=0.27.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# vereefy-sdk
|
|
26
|
+
|
|
27
|
+
Python SDK for the VEREEFY EU AI Act compliance API.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install vereefy-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from vereefy import Verefy, Hasher
|
|
39
|
+
|
|
40
|
+
async with Verefy(api_key="vfy_...") as v:
|
|
41
|
+
evidence = await v.log_interaction(
|
|
42
|
+
provider="openai",
|
|
43
|
+
model_name="gpt-4o",
|
|
44
|
+
purpose="Kundenberatung",
|
|
45
|
+
sector="financial_services",
|
|
46
|
+
input_content="Was ist der Zins?",
|
|
47
|
+
output_content="Der aktuelle Zinssatz beträgt 3,5 %.",
|
|
48
|
+
)
|
|
49
|
+
print(evidence.compliance_score)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
See [vereefy.ai/docs](https://vereefy.ai/docs) for the full API reference.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# vereefy-sdk
|
|
2
|
+
|
|
3
|
+
Python SDK for the VEREEFY EU AI Act compliance API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install vereefy-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from vereefy import Verefy, Hasher
|
|
15
|
+
|
|
16
|
+
async with Verefy(api_key="vfy_...") as v:
|
|
17
|
+
evidence = await v.log_interaction(
|
|
18
|
+
provider="openai",
|
|
19
|
+
model_name="gpt-4o",
|
|
20
|
+
purpose="Kundenberatung",
|
|
21
|
+
sector="financial_services",
|
|
22
|
+
input_content="Was ist der Zins?",
|
|
23
|
+
output_content="Der aktuelle Zinssatz beträgt 3,5 %.",
|
|
24
|
+
)
|
|
25
|
+
print(evidence.compliance_score)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
See [vereefy.ai/docs](https://vereefy.ai/docs) for the full API reference.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vereefy-sdk"
|
|
7
|
+
version = "0.2.1"
|
|
8
|
+
description = "VEREEFY Python SDK — EU AI Act Compliance-Infrastruktur"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "VEREEFY", email = "hello@vereefy.ai"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["EU AI Act", "compliance", "KI", "audit", "vereefy"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"httpx>=0.27.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = ["pytest>=8.0", "pytest-asyncio>=0.24"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["."]
|
|
34
|
+
include = ["vereefy*", "verefy*"]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://vereefy.ai"
|
|
38
|
+
Documentation = "https://docs.vereefy.ai"
|
|
39
|
+
Repository = "https://github.com/vereefy-ai/vereefy-python"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""SDK-Tests: Standard-URL, Hash-Determinismus, Deprecation-Shim, Staging-Integration."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
import pytest_asyncio
|
|
11
|
+
|
|
12
|
+
from vereefy import Verefy, Hasher, VEREEFY_API_URL
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ─── Standard-URL ────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
def test_default_base_url():
|
|
18
|
+
v = Verefy("vfy_testkey")
|
|
19
|
+
assert v.base_url == "https://api.vereefy.ai"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_custom_base_url():
|
|
23
|
+
v = Verefy("vfy_testkey", base_url="https://staging.api.vereefy.ai")
|
|
24
|
+
assert v.base_url == "https://staging.api.vereefy.ai"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_base_url_trailing_slash_stripped():
|
|
28
|
+
v = Verefy("vfy_testkey", base_url="https://api.vereefy.ai/")
|
|
29
|
+
assert v.base_url == "https://api.vereefy.ai"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_invalid_api_key_raises():
|
|
33
|
+
with pytest.raises(ValueError, match="vfy_"):
|
|
34
|
+
Verefy("sk-notakey")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ─── Kanonischer Hash ─────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
def test_canonical_hash_string_deterministic():
|
|
40
|
+
h1 = Hasher.canonical_hash("Hallo Welt")
|
|
41
|
+
h2 = Hasher.canonical_hash("Hallo Welt")
|
|
42
|
+
assert h1 == h2
|
|
43
|
+
assert len(h1) == 64
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_canonical_hash_string_strips_whitespace():
|
|
47
|
+
h1 = Hasher.canonical_hash("Hallo")
|
|
48
|
+
h2 = Hasher.canonical_hash(" Hallo ")
|
|
49
|
+
assert h1 == h2
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_canonical_hash_dict_key_order_independent():
|
|
53
|
+
h1 = Hasher.canonical_hash({"a": 1, "b": 2})
|
|
54
|
+
h2 = Hasher.canonical_hash({"b": 2, "a": 1})
|
|
55
|
+
assert h1 == h2
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_canonical_hash_matches_reference():
|
|
59
|
+
# Referenzwert: sha256 von '{"a":1,"b":2}' (sort_keys, no spaces)
|
|
60
|
+
expected = hashlib.sha256(
|
|
61
|
+
json.dumps({"a": 1, "b": 2}, sort_keys=True, ensure_ascii=True, separators=(',', ':')).encode("utf-8")
|
|
62
|
+
).hexdigest()
|
|
63
|
+
assert Hasher.canonical_hash({"b": 2, "a": 1}) == expected
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_canonical_hash_bytes():
|
|
67
|
+
raw = b"\x00\x01\x02"
|
|
68
|
+
h = Hasher.canonical_hash(raw)
|
|
69
|
+
assert h == hashlib.sha256(raw).hexdigest()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_canonical_hash_none():
|
|
73
|
+
assert Hasher.canonical_hash(None) == hashlib.sha256(b"null").hexdigest()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_canonical_hash_is_64_hex_chars():
|
|
77
|
+
for value in ["text", {"k": "v"}, [1, 2], b"bytes", None]:
|
|
78
|
+
h = Hasher.canonical_hash(value)
|
|
79
|
+
assert len(h) == 64
|
|
80
|
+
assert all(c in "0123456789abcdef" for c in h)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ─── Deprecation-Shim ────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
def test_import_verefy_emits_deprecation_warning():
|
|
86
|
+
# verefy-Paket bereits importiert — DeprecationWarning beim ersten Import
|
|
87
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
88
|
+
warnings.simplefilter("always")
|
|
89
|
+
import importlib
|
|
90
|
+
import verefy # noqa: F401
|
|
91
|
+
importlib.reload(verefy)
|
|
92
|
+
depr = [w for w in caught if issubclass(w.category, DeprecationWarning)]
|
|
93
|
+
assert len(depr) >= 1
|
|
94
|
+
assert "vereefy" in str(depr[0].message).lower()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_verefy_shim_exports_verefy_class():
|
|
98
|
+
import verefy as _old
|
|
99
|
+
assert hasattr(_old, "Verefy")
|
|
100
|
+
assert hasattr(_old, "Hasher")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_verefy_shim_backward_compat_constants():
|
|
104
|
+
import verefy as _old
|
|
105
|
+
assert _old.VEREFY_API_URL == VEREEFY_API_URL
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ─── Staging-Integration (optional, nur wenn VEREEFY_STAGING_KEY gesetzt) ───
|
|
109
|
+
|
|
110
|
+
STAGING_KEY = os.environ.get("VEREEFY_STAGING_KEY", "")
|
|
111
|
+
STAGING_URL = "https://staging.api.vereefy.ai"
|
|
112
|
+
|
|
113
|
+
@pytest.mark.asyncio
|
|
114
|
+
@pytest.mark.skipif(not STAGING_KEY, reason="VEREEFY_STAGING_KEY nicht gesetzt")
|
|
115
|
+
async def test_staging_log_interaction_returns_201():
|
|
116
|
+
async with Verefy(api_key=STAGING_KEY, base_url=STAGING_URL) as v:
|
|
117
|
+
evidence = await v.log_interaction(
|
|
118
|
+
provider="openai",
|
|
119
|
+
model_name="gpt-4o",
|
|
120
|
+
purpose="SDK-Integrationstest",
|
|
121
|
+
sector="financial_services",
|
|
122
|
+
input_content="Was ist der Zins?",
|
|
123
|
+
output_content="Der aktuelle Zinssatz beträgt 3,5 %.",
|
|
124
|
+
)
|
|
125
|
+
assert evidence.interaction_id
|
|
126
|
+
assert len(evidence.chain_tip_hash) == 64
|
|
127
|
+
assert evidence.compliance_score >= 0
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VEREEFY Python SDK v0.2
|
|
3
|
+
Verwendung:
|
|
4
|
+
from vereefy import Verefy, Hasher
|
|
5
|
+
async with Verefy(api_key="vfy_...") as v:
|
|
6
|
+
nachweis = await v.log_interaction(
|
|
7
|
+
provider="openai", model_name="gpt-4o",
|
|
8
|
+
purpose="Kundenberatung Retail-Banking",
|
|
9
|
+
sector="financial_services",
|
|
10
|
+
input_content=nachrichten,
|
|
11
|
+
output_content=antwort,
|
|
12
|
+
)
|
|
13
|
+
"""
|
|
14
|
+
import hashlib, json, asyncio
|
|
15
|
+
import httpx
|
|
16
|
+
from typing import Any, Optional, Dict, List
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
|
|
19
|
+
VEREEFY_API_URL = "https://api.vereefy.ai"
|
|
20
|
+
SDK_VERSION = "0.2.1"
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ComplianceEvidence:
|
|
24
|
+
interaction_id: str
|
|
25
|
+
risk_class: str
|
|
26
|
+
article_matches: List[str]
|
|
27
|
+
required_actions: List[str]
|
|
28
|
+
required_disclosures: List[str]
|
|
29
|
+
regulatory_flags: List[str]
|
|
30
|
+
confidence: str
|
|
31
|
+
compliance_score: int
|
|
32
|
+
audit_entry_id: str
|
|
33
|
+
chain_tip_hash: str
|
|
34
|
+
timestamp: str
|
|
35
|
+
classification_version: str
|
|
36
|
+
policy_version: str
|
|
37
|
+
|
|
38
|
+
def is_high_risk(self) -> bool:
|
|
39
|
+
return self.risk_class in ("high", "unacceptable")
|
|
40
|
+
|
|
41
|
+
def summary(self) -> str:
|
|
42
|
+
return (f"Risiko: {self.risk_class.upper()} | Score: {self.compliance_score}/100 | "
|
|
43
|
+
f"Artikel: {', '.join(self.article_matches[:3])}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Hasher:
|
|
47
|
+
"""Client-seitiges Hashing. Rohinhalte verlassen nie Ihre Umgebung."""
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def canonical_hash(content: Any) -> str:
|
|
51
|
+
if content is None:
|
|
52
|
+
return hashlib.sha256(b"null").hexdigest()
|
|
53
|
+
if isinstance(content, bytes):
|
|
54
|
+
return hashlib.sha256(content).hexdigest()
|
|
55
|
+
if isinstance(content, str):
|
|
56
|
+
return hashlib.sha256(content.strip().encode("utf-8")).hexdigest()
|
|
57
|
+
if isinstance(content, (dict, list)):
|
|
58
|
+
c = json.dumps(content, sort_keys=True, ensure_ascii=True, separators=(',', ':'))
|
|
59
|
+
return hashlib.sha256(c.encode("utf-8")).hexdigest()
|
|
60
|
+
return hashlib.sha256(str(content).encode("utf-8")).hexdigest()
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def hash_messages(messages: List[Dict]) -> str:
|
|
64
|
+
normalized = [{"role": m.get("role", ""), "content": m.get("content", "")} for m in messages]
|
|
65
|
+
return Hasher.canonical_hash(normalized)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Verefy:
|
|
69
|
+
def __init__(self, api_key: str, base_url: str = VEREEFY_API_URL,
|
|
70
|
+
timeout: float = 15.0, max_retries: int = 3):
|
|
71
|
+
if not api_key.startswith("vfy_"):
|
|
72
|
+
raise ValueError("API-Schlüssel muss mit 'vfy_' beginnen.")
|
|
73
|
+
self.api_key = api_key
|
|
74
|
+
self.base_url = base_url.rstrip("/")
|
|
75
|
+
self.timeout = timeout
|
|
76
|
+
self.max_retries = max_retries
|
|
77
|
+
self._client: Optional[httpx.AsyncClient] = None
|
|
78
|
+
|
|
79
|
+
def _get_client(self) -> httpx.AsyncClient:
|
|
80
|
+
if self._client is None or self._client.is_closed:
|
|
81
|
+
self._client = httpx.AsyncClient(
|
|
82
|
+
base_url=self.base_url,
|
|
83
|
+
headers={"X-API-Key": self.api_key,
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"X-SDK-Version": SDK_VERSION},
|
|
86
|
+
timeout=self.timeout,
|
|
87
|
+
)
|
|
88
|
+
return self._client
|
|
89
|
+
|
|
90
|
+
async def close(self):
|
|
91
|
+
if self._client and not self._client.is_closed:
|
|
92
|
+
await self._client.aclose()
|
|
93
|
+
self._client = None
|
|
94
|
+
|
|
95
|
+
async def __aenter__(self): return self
|
|
96
|
+
async def __aexit__(self, *args): await self.close()
|
|
97
|
+
|
|
98
|
+
async def _request(self, method: str, path: str, **kwargs):
|
|
99
|
+
client = self._get_client()
|
|
100
|
+
last_err = None
|
|
101
|
+
for attempt in range(self.max_retries):
|
|
102
|
+
try:
|
|
103
|
+
r = await client.request(method, path, **kwargs)
|
|
104
|
+
if r.status_code < 500:
|
|
105
|
+
return r
|
|
106
|
+
last_err = VereefyAPIError(r.status_code, r.text)
|
|
107
|
+
if attempt < self.max_retries - 1:
|
|
108
|
+
await asyncio.sleep(2 ** attempt)
|
|
109
|
+
except httpx.TransportError as e:
|
|
110
|
+
last_err = VereefyConnectionError(str(e))
|
|
111
|
+
if attempt < self.max_retries - 1:
|
|
112
|
+
await asyncio.sleep(2 ** attempt)
|
|
113
|
+
raise last_err
|
|
114
|
+
|
|
115
|
+
async def log_interaction(
|
|
116
|
+
self, *, provider: str, model_name: str, purpose: str, sector: str,
|
|
117
|
+
input_hash: Optional[str] = None, output_hash: Optional[str] = None,
|
|
118
|
+
input_content: Optional[Any] = None, output_content: Optional[Any] = None,
|
|
119
|
+
model_version: Optional[str] = None, interaction_type: str = "chat",
|
|
120
|
+
customer_facing: bool = True, automation_level: str = "assisted",
|
|
121
|
+
decision_impact: str = "low", user_role: Optional[str] = None,
|
|
122
|
+
jurisdiction: str = "EU", external_ref: Optional[str] = None,
|
|
123
|
+
response_time_ms: Optional[int] = None, extra_data: Optional[Dict] = None,
|
|
124
|
+
) -> ComplianceEvidence:
|
|
125
|
+
if input_content is not None:
|
|
126
|
+
resolved_input = Hasher.canonical_hash(input_content)
|
|
127
|
+
elif input_hash is not None:
|
|
128
|
+
resolved_input = input_hash
|
|
129
|
+
else:
|
|
130
|
+
raise VereefyValidationError("input_content oder input_hash angeben.")
|
|
131
|
+
|
|
132
|
+
if output_content is not None:
|
|
133
|
+
resolved_output = Hasher.canonical_hash(output_content)
|
|
134
|
+
elif output_hash is not None:
|
|
135
|
+
resolved_output = output_hash
|
|
136
|
+
else:
|
|
137
|
+
raise VereefyValidationError("output_content oder output_hash angeben.")
|
|
138
|
+
|
|
139
|
+
payload = {k: v for k, v in {
|
|
140
|
+
"provider": provider, "model_name": model_name,
|
|
141
|
+
"model_version": model_version, "purpose": purpose,
|
|
142
|
+
"sector": sector, "interaction_type": interaction_type,
|
|
143
|
+
"customer_facing": customer_facing, "automation_level": automation_level,
|
|
144
|
+
"decision_impact": decision_impact, "user_role": user_role,
|
|
145
|
+
"jurisdiction": jurisdiction, "input_hash": resolved_input,
|
|
146
|
+
"output_hash": resolved_output, "external_ref": external_ref,
|
|
147
|
+
"response_time_ms": response_time_ms,
|
|
148
|
+
"extra_data": extra_data,
|
|
149
|
+
}.items() if v is not None}
|
|
150
|
+
|
|
151
|
+
r = await self._request("POST", "/v1/interactions", json=payload)
|
|
152
|
+
if r.status_code == 201:
|
|
153
|
+
return ComplianceEvidence(**r.json())
|
|
154
|
+
raise VereefyAPIError(r.status_code, r.json().get("detail", r.text))
|
|
155
|
+
|
|
156
|
+
async def get_interaction(self, iid: str) -> Dict:
|
|
157
|
+
r = await self._request("GET", f"/v1/interactions/{iid}")
|
|
158
|
+
if r.status_code == 200: return r.json()
|
|
159
|
+
raise VereefyAPIError(r.status_code, r.text)
|
|
160
|
+
|
|
161
|
+
async def verify_chain(self, iid: str) -> Dict:
|
|
162
|
+
r = await self._request("GET", f"/v1/audit/{iid}/chain")
|
|
163
|
+
if r.status_code == 200: return r.json()
|
|
164
|
+
raise VereefyAPIError(r.status_code, r.text)
|
|
165
|
+
|
|
166
|
+
async def request_report(self, report_type: str = "eu_ai_act",
|
|
167
|
+
date_from=None, date_to=None,
|
|
168
|
+
interaction_ids=None, format: str = "json") -> Dict:
|
|
169
|
+
payload = {k: v for k, v in {
|
|
170
|
+
"report_type": report_type, "format": format,
|
|
171
|
+
"date_from": date_from, "date_to": date_to,
|
|
172
|
+
"interaction_ids": interaction_ids,
|
|
173
|
+
}.items() if v is not None}
|
|
174
|
+
r = await self._request("POST", "/v1/reports", json=payload)
|
|
175
|
+
if r.status_code in (200, 202): return r.json()
|
|
176
|
+
raise VereefyAPIError(r.status_code, r.text)
|
|
177
|
+
|
|
178
|
+
async def get_report(self, rid: str) -> Dict:
|
|
179
|
+
r = await self._request("GET", f"/v1/reports/{rid}")
|
|
180
|
+
if r.status_code == 200: return r.json()
|
|
181
|
+
raise VereefyAPIError(r.status_code, r.text)
|
|
182
|
+
|
|
183
|
+
class VereefySync:
|
|
184
|
+
"""
|
|
185
|
+
Synchrone Wrapper-Klasse für Flask/Django/Skripte.
|
|
186
|
+
|
|
187
|
+
WARNUNG — in Umgebungen mit bereits laufendem Event Loop
|
|
188
|
+
(Jupyter, FastAPI, ASGI-Apps) kann dieser Wrapper zu Konflikten führen.
|
|
189
|
+
Für produktive Verwendung wird der async Verefy-Client empfohlen.
|
|
190
|
+
"""
|
|
191
|
+
def __init__(self, **kwargs):
|
|
192
|
+
self._async = Verefy(**kwargs)
|
|
193
|
+
try:
|
|
194
|
+
asyncio.get_running_loop()
|
|
195
|
+
import warnings
|
|
196
|
+
warnings.warn(
|
|
197
|
+
"VereefySync wird in einem bereits laufenden Event Loop erstellt. "
|
|
198
|
+
"Verwende stattdessen 'async with Verefy(...)' für async-Umgebungen.",
|
|
199
|
+
RuntimeWarning, stacklevel=2
|
|
200
|
+
)
|
|
201
|
+
except RuntimeError:
|
|
202
|
+
pass
|
|
203
|
+
self._loop = asyncio.new_event_loop()
|
|
204
|
+
|
|
205
|
+
def _run(self, coro): return self._loop.run_until_complete(coro)
|
|
206
|
+
def log_interaction(self, **kw): return self._run(self._async.log_interaction(**kw))
|
|
207
|
+
def verify_chain(self, iid): return self._run(self._async.verify_chain(iid))
|
|
208
|
+
def close(self):
|
|
209
|
+
self._run(self._async.close())
|
|
210
|
+
self._loop.close()
|
|
211
|
+
def __enter__(self): return self
|
|
212
|
+
def __exit__(self, *a): self.close()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class VereefyError(Exception): pass
|
|
216
|
+
class VereefyAPIError(VereefyError):
|
|
217
|
+
def __init__(self, sc, detail):
|
|
218
|
+
self.status_code = sc; self.detail = detail
|
|
219
|
+
super().__init__(f"VEREEFY API {sc}: {detail}")
|
|
220
|
+
class VereefyConnectionError(VereefyError): pass
|
|
221
|
+
class VereefyValidationError(VereefyError): pass
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vereefy-sdk
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: VEREEFY Python SDK — EU AI Act Compliance-Infrastruktur
|
|
5
|
+
Author-email: VEREEFY <hello@vereefy.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://vereefy.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.vereefy.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/vereefy-ai/vereefy-python
|
|
10
|
+
Keywords: EU AI Act,compliance,KI,audit,vereefy
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: httpx>=0.27.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# vereefy-sdk
|
|
26
|
+
|
|
27
|
+
Python SDK for the VEREEFY EU AI Act compliance API.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install vereefy-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from vereefy import Verefy, Hasher
|
|
39
|
+
|
|
40
|
+
async with Verefy(api_key="vfy_...") as v:
|
|
41
|
+
evidence = await v.log_interaction(
|
|
42
|
+
provider="openai",
|
|
43
|
+
model_name="gpt-4o",
|
|
44
|
+
purpose="Kundenberatung",
|
|
45
|
+
sector="financial_services",
|
|
46
|
+
input_content="Was ist der Zins?",
|
|
47
|
+
output_content="Der aktuelle Zinssatz beträgt 3,5 %.",
|
|
48
|
+
)
|
|
49
|
+
print(evidence.compliance_score)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
See [vereefy.ai/docs](https://vereefy.ai/docs) for the full API reference.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tests/test_sdk.py
|
|
4
|
+
vereefy/__init__.py
|
|
5
|
+
vereefy_sdk.egg-info/PKG-INFO
|
|
6
|
+
vereefy_sdk.egg-info/SOURCES.txt
|
|
7
|
+
vereefy_sdk.egg-info/dependency_links.txt
|
|
8
|
+
vereefy_sdk.egg-info/requires.txt
|
|
9
|
+
vereefy_sdk.egg-info/top_level.txt
|
|
10
|
+
verefy/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deprecated: Dieses Paket heisst jetzt 'vereefy'.
|
|
3
|
+
Bitte auf 'from vereefy import ...' umsteigen.
|
|
4
|
+
"""
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
warnings.warn(
|
|
8
|
+
"Das Paket 'verefy' ist veraltet. "
|
|
9
|
+
"Bitte 'from vereefy import Verefy, Hasher' verwenden.",
|
|
10
|
+
DeprecationWarning,
|
|
11
|
+
stacklevel=2,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from vereefy import ( # noqa: F401, E402
|
|
15
|
+
Verefy,
|
|
16
|
+
VereefySync,
|
|
17
|
+
Hasher,
|
|
18
|
+
ComplianceEvidence,
|
|
19
|
+
VereefyError,
|
|
20
|
+
VereefyAPIError,
|
|
21
|
+
VereefyConnectionError,
|
|
22
|
+
VereefyValidationError,
|
|
23
|
+
VEREEFY_API_URL,
|
|
24
|
+
SDK_VERSION,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Rückwärtskompatible Aliase
|
|
28
|
+
VerefySync = VereefySync
|
|
29
|
+
VerefyError = VereefyError
|
|
30
|
+
VerefyAPIError = VereefyAPIError
|
|
31
|
+
VerefyConnectionError = VereefyConnectionError
|
|
32
|
+
VerefyValidationError = VereefyValidationError
|
|
33
|
+
VEREFY_API_URL = VEREEFY_API_URL
|