sovergate 0.1.0a1__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.
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .coverage
9
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roberto Postolovski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.5
2
+ Name: sovergate
3
+ Version: 0.1.0a1
4
+ Summary: EU AI Act compliance SDK — automatic, non-invasive audit logging for LLM calls
5
+ Project-URL: Homepage, https://sovergate.com
6
+ Project-URL: Repository, https://github.com/Sovergate/sdk-python
7
+ Project-URL: Issues, https://github.com/Sovergate/sdk-python/issues
8
+ Author: Roberto Postolovski
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: anthropic,audit,compliance,eu-ai-act,gdpr,llm,observability,openai,pii
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: System :: Logging
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Provides-Extra: dev
26
+ Requires-Dist: anthropic>=2.0; extra == 'dev'
27
+ Requires-Dist: httpx2; extra == 'dev'
28
+ Requires-Dist: httpx>=0.27; extra == 'dev'
29
+ Requires-Dist: openai>=1.0; extra == 'dev'
30
+ Requires-Dist: pytest>=8.0; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # sovergate
34
+
35
+ Python SDK for [Sovergate](https://sovergate.com): EU AI Act Article 12 audit
36
+ logging for LLM calls, with PII scrubbed inside your own infrastructure.
37
+
38
+ > **Early access.** The SDK is published as an alpha and the hosted ingest
39
+ > service is not generally available yet. APIs may change before 1.0.
40
+
41
+ ## How it works
42
+
43
+ Your app keeps calling OpenAI or Anthropic directly. Sovergate does not proxy
44
+ or intercept the request. After a call completes, the SDK takes a copy,
45
+ scrubs personal data **locally**, and sends the scrubbed copy to Sovergate
46
+ from a background thread.
47
+
48
+ ```
49
+ Your app ─────────────────────────────► OpenAI / Anthropic
50
+ │
51
+ └──► scrub PII locally ──► Sovergate (background, fire and forget)
52
+ ```
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install --pre sovergate
58
+ ```
59
+
60
+ `--pre` is needed while the SDK is in alpha. Requires Python 3.10+. No runtime
61
+ dependencies.
62
+
63
+ ## Quick start
64
+
65
+ ```python
66
+ import openai
67
+ import sovergate
68
+
69
+ sovergate.init(api_key="sov_prod_xxxx")
70
+ sovergate.instrument(openai)
71
+
72
+ # Your existing code is unchanged
73
+ response = openai.chat.completions.create(
74
+ model="gpt-4o",
75
+ messages=[{"role": "user", "content": "Hello"}],
76
+ )
77
+ # The response is returned immediately; logging happens in the background
78
+ ```
79
+
80
+ You can also instrument a client instance, sync or async:
81
+
82
+ ```python
83
+ from openai import AsyncOpenAI
84
+
85
+ client = AsyncOpenAI()
86
+ sovergate.instrument(client)
87
+ ```
88
+
89
+ ### Anthropic
90
+
91
+ Pass a client instance (the `anthropic` module itself has no client to patch):
92
+
93
+ ```python
94
+ import anthropic
95
+ import sovergate
96
+
97
+ client = anthropic.Anthropic() # or anthropic.AsyncAnthropic()
98
+ sovergate.init(api_key="sov_prod_xxxx")
99
+ sovergate.instrument(client)
100
+ ```
101
+
102
+ ## Stable PII tokens (recommended)
103
+
104
+ By default, detected PII is replaced with positional placeholders
105
+ (`[EMAIL_REDACTED_1]`), which are not correlatable between logs. Pass a
106
+ `pii_key` to replace values with deterministic, irreversible HMAC-SHA256 tokens
107
+ instead. The same value always maps to the same token, so Sovergate can count
108
+ unique people, IBANs and so on without ever receiving the raw values.
109
+
110
+ ```python
111
+ sovergate.init(api_key="sov_prod_xxxx", pii_key="<your-secret>")
112
+ ```
113
+
114
+ The `pii_key` is your secret. Keep it stable, and never send it to us: it stays
115
+ on your infrastructure, and without it the tokens cannot be reversed.
116
+
117
+ ## Options
118
+
119
+ ```python
120
+ sovergate.init(
121
+ api_key="sov_prod_xxxx", # required
122
+ base_url="https://...", # default: https://api.sovergate.com
123
+ pii_key="<your-secret>", # optional, see above
124
+ debug=True, # verbose output from the "sovergate" logger only
125
+ )
126
+ ```
127
+
128
+ ## What is sent to Sovergate
129
+
130
+ For each completed call, one JSON record:
131
+
132
+ | Field | Content |
133
+ |---|---|
134
+ | `model` | Model name returned by the provider |
135
+ | `messages` | Your prompt messages (and Anthropic `system` prompt), **after** PII scrubbing |
136
+ | `response` | The model's text output, **after** PII scrubbing |
137
+ | `prompt_tokens`, `completion_tokens` | Token counts from the provider |
138
+ | `latency_ms`, `timestamp` | Call duration and start time |
139
+ | `finish_reason` | Why the model stopped |
140
+ | `pii_detections` | Category and placeholder of each scrubbed item, never the original value |
141
+
142
+ Never sent: the raw prompt or response, your `pii_key`, your LLM provider API
143
+ key, or request headers.
144
+
145
+ ### What the scrubber detects
146
+
147
+ Regex-based, applied to every text part of the prompt and response: email
148
+ addresses, IP addresses, IBANs (including the spaced format), card numbers,
149
+ phone numbers, dates of birth, ID numbers, and names that follow a title (Mr,
150
+ Mrs, Ms, Dr, Prof). Other personal data, such as names without a title, is
151
+ **not** detected yet, so keep data minimisation in mind when you build prompts.
152
+ The code is in [`sovergate/scrubber.py`](sovergate/scrubber.py).
153
+
154
+ ## Guarantees
155
+
156
+ - Never blocks your call: logging runs in a background daemon thread
157
+ - Never raises: if setup fails, the SDK logs a warning on the `sovergate`
158
+ logger and stays out of the way
159
+ - Returns the provider's response unchanged; `with_raw_response` keeps working
160
+ - Never changes your app's logging configuration, even with `debug=True`
161
+ - 2 second timeout on every HTTP call to Sovergate
162
+ - If Sovergate is unreachable, records are queued in memory (up to 10,000) and
163
+ retried with exponential backoff
164
+
165
+ ## Current limitations
166
+
167
+ - Streaming calls (`stream=True`) are passed through untouched but not logged yet.
168
+ - Calls made through `with_raw_response` are not logged.
169
+ - The retry queue is in memory, so queued records are lost if the process exits.
170
+
171
+ ## Development
172
+
173
+ ```bash
174
+ python -m venv .venv && source .venv/bin/activate
175
+ pip install -e ".[dev]"
176
+ pytest
177
+ ```
178
+
179
+ ## Security
180
+
181
+ Please report vulnerabilities privately. See [SECURITY.md](SECURITY.md).
182
+
183
+ ## License
184
+
185
+ [MIT](LICENSE)
@@ -0,0 +1,153 @@
1
+ # sovergate
2
+
3
+ Python SDK for [Sovergate](https://sovergate.com): EU AI Act Article 12 audit
4
+ logging for LLM calls, with PII scrubbed inside your own infrastructure.
5
+
6
+ > **Early access.** The SDK is published as an alpha and the hosted ingest
7
+ > service is not generally available yet. APIs may change before 1.0.
8
+
9
+ ## How it works
10
+
11
+ Your app keeps calling OpenAI or Anthropic directly. Sovergate does not proxy
12
+ or intercept the request. After a call completes, the SDK takes a copy,
13
+ scrubs personal data **locally**, and sends the scrubbed copy to Sovergate
14
+ from a background thread.
15
+
16
+ ```
17
+ Your app ─────────────────────────────► OpenAI / Anthropic
18
+ │
19
+ └──► scrub PII locally ──► Sovergate (background, fire and forget)
20
+ ```
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install --pre sovergate
26
+ ```
27
+
28
+ `--pre` is needed while the SDK is in alpha. Requires Python 3.10+. No runtime
29
+ dependencies.
30
+
31
+ ## Quick start
32
+
33
+ ```python
34
+ import openai
35
+ import sovergate
36
+
37
+ sovergate.init(api_key="sov_prod_xxxx")
38
+ sovergate.instrument(openai)
39
+
40
+ # Your existing code is unchanged
41
+ response = openai.chat.completions.create(
42
+ model="gpt-4o",
43
+ messages=[{"role": "user", "content": "Hello"}],
44
+ )
45
+ # The response is returned immediately; logging happens in the background
46
+ ```
47
+
48
+ You can also instrument a client instance, sync or async:
49
+
50
+ ```python
51
+ from openai import AsyncOpenAI
52
+
53
+ client = AsyncOpenAI()
54
+ sovergate.instrument(client)
55
+ ```
56
+
57
+ ### Anthropic
58
+
59
+ Pass a client instance (the `anthropic` module itself has no client to patch):
60
+
61
+ ```python
62
+ import anthropic
63
+ import sovergate
64
+
65
+ client = anthropic.Anthropic() # or anthropic.AsyncAnthropic()
66
+ sovergate.init(api_key="sov_prod_xxxx")
67
+ sovergate.instrument(client)
68
+ ```
69
+
70
+ ## Stable PII tokens (recommended)
71
+
72
+ By default, detected PII is replaced with positional placeholders
73
+ (`[EMAIL_REDACTED_1]`), which are not correlatable between logs. Pass a
74
+ `pii_key` to replace values with deterministic, irreversible HMAC-SHA256 tokens
75
+ instead. The same value always maps to the same token, so Sovergate can count
76
+ unique people, IBANs and so on without ever receiving the raw values.
77
+
78
+ ```python
79
+ sovergate.init(api_key="sov_prod_xxxx", pii_key="<your-secret>")
80
+ ```
81
+
82
+ The `pii_key` is your secret. Keep it stable, and never send it to us: it stays
83
+ on your infrastructure, and without it the tokens cannot be reversed.
84
+
85
+ ## Options
86
+
87
+ ```python
88
+ sovergate.init(
89
+ api_key="sov_prod_xxxx", # required
90
+ base_url="https://...", # default: https://api.sovergate.com
91
+ pii_key="<your-secret>", # optional, see above
92
+ debug=True, # verbose output from the "sovergate" logger only
93
+ )
94
+ ```
95
+
96
+ ## What is sent to Sovergate
97
+
98
+ For each completed call, one JSON record:
99
+
100
+ | Field | Content |
101
+ |---|---|
102
+ | `model` | Model name returned by the provider |
103
+ | `messages` | Your prompt messages (and Anthropic `system` prompt), **after** PII scrubbing |
104
+ | `response` | The model's text output, **after** PII scrubbing |
105
+ | `prompt_tokens`, `completion_tokens` | Token counts from the provider |
106
+ | `latency_ms`, `timestamp` | Call duration and start time |
107
+ | `finish_reason` | Why the model stopped |
108
+ | `pii_detections` | Category and placeholder of each scrubbed item, never the original value |
109
+
110
+ Never sent: the raw prompt or response, your `pii_key`, your LLM provider API
111
+ key, or request headers.
112
+
113
+ ### What the scrubber detects
114
+
115
+ Regex-based, applied to every text part of the prompt and response: email
116
+ addresses, IP addresses, IBANs (including the spaced format), card numbers,
117
+ phone numbers, dates of birth, ID numbers, and names that follow a title (Mr,
118
+ Mrs, Ms, Dr, Prof). Other personal data, such as names without a title, is
119
+ **not** detected yet, so keep data minimisation in mind when you build prompts.
120
+ The code is in [`sovergate/scrubber.py`](sovergate/scrubber.py).
121
+
122
+ ## Guarantees
123
+
124
+ - Never blocks your call: logging runs in a background daemon thread
125
+ - Never raises: if setup fails, the SDK logs a warning on the `sovergate`
126
+ logger and stays out of the way
127
+ - Returns the provider's response unchanged; `with_raw_response` keeps working
128
+ - Never changes your app's logging configuration, even with `debug=True`
129
+ - 2 second timeout on every HTTP call to Sovergate
130
+ - If Sovergate is unreachable, records are queued in memory (up to 10,000) and
131
+ retried with exponential backoff
132
+
133
+ ## Current limitations
134
+
135
+ - Streaming calls (`stream=True`) are passed through untouched but not logged yet.
136
+ - Calls made through `with_raw_response` are not logged.
137
+ - The retry queue is in memory, so queued records are lost if the process exits.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ python -m venv .venv && source .venv/bin/activate
143
+ pip install -e ".[dev]"
144
+ pytest
145
+ ```
146
+
147
+ ## Security
148
+
149
+ Please report vulnerabilities privately. See [SECURITY.md](SECURITY.md).
150
+
151
+ ## License
152
+
153
+ [MIT](LICENSE)
@@ -0,0 +1,21 @@
1
+ # Security policy
2
+
3
+ This SDK runs inside your infrastructure and sees prompts before PII is
4
+ scrubbed, so we take reports seriously.
5
+
6
+ ## Reporting a vulnerability
7
+
8
+ Please **do not open a public issue**. Instead, report it privately through
9
+ GitHub: open the **Security** tab of this repository and choose
10
+ **Report a vulnerability**.
11
+
12
+ If you cannot use GitHub, email **hello@sovergate.com** with the subject
13
+ "Security report".
14
+
15
+ We aim to acknowledge reports within 3 business days and will keep you updated
16
+ until the issue is resolved.
17
+
18
+ ## Supported versions
19
+
20
+ The SDK is in early access. Security fixes are released on the latest version
21
+ only.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "sovergate"
7
+ dynamic = ["version"]
8
+ description = "EU AI Act compliance SDK — automatic, non-invasive audit logging for LLM calls"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ dependencies = []
14
+ authors = [{ name = "Roberto Postolovski" }]
15
+ keywords = ["eu-ai-act", "compliance", "gdpr", "llm", "audit", "openai", "anthropic", "pii", "observability"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Security",
27
+ "Topic :: System :: Logging",
28
+ "Typing :: Typed",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://sovergate.com"
33
+ Repository = "https://github.com/Sovergate/sdk-python"
34
+ Issues = "https://github.com/Sovergate/sdk-python/issues"
35
+
36
+ [project.optional-dependencies]
37
+ dev = [
38
+ "pytest>=8.0",
39
+ "openai>=1.0",
40
+ "anthropic>=2.0",
41
+ "httpx>=0.27",
42
+ "httpx2",
43
+ ]
44
+
45
+ [tool.hatch.version]
46
+ path = "sovergate/__init__.py"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["sovergate"]
50
+
51
+ [tool.hatch.build.targets.sdist]
52
+ include = ["sovergate", "tests", "README.md", "LICENSE", "SECURITY.md"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ from .client import init, instrument, shutdown
2
+
3
+ __all__ = ["init", "instrument", "shutdown"]
4
+ __version__ = "0.1.0a1"
@@ -0,0 +1,138 @@
1
+ """Public entry point — init() and instrument()."""
2
+
3
+ import logging
4
+ from typing import Any, Optional
5
+
6
+ from .sender import send
7
+ from .queue import LogQueue
8
+
9
+ logger = logging.getLogger("sovergate")
10
+
11
+ _state: dict = {
12
+ "api_key": None,
13
+ "base_url": "https://api.sovergate.com",
14
+ "pii_key": None,
15
+ "queue": None,
16
+ }
17
+
18
+
19
+ def init(
20
+ api_key: str,
21
+ base_url: str = "https://api.sovergate.com",
22
+ pii_key: Optional[str] = None,
23
+ debug: bool = False,
24
+ ) -> None:
25
+ """
26
+ Call once at app startup before any LLM calls.
27
+
28
+ Args:
29
+ api_key: Your sovergate SDK key (starts with sov_).
30
+ base_url: Override API endpoint (useful for self-hosted or local dev).
31
+ pii_key: Secret used to derive deterministic, irreversible PII tokens.
32
+ The same value always maps to the same token, so the platform
33
+ can count unique entities without ever holding raw PII. Keep it
34
+ secret and stable — it NEVER leaves your infrastructure. If
35
+ omitted, tokens are positional and not correlatable across logs.
36
+ debug: Enable verbose logging from the SDK.
37
+ """
38
+ try:
39
+ if debug:
40
+ _enable_debug_logging()
41
+
42
+ _state["api_key"] = api_key
43
+ _state["base_url"] = base_url.rstrip("/")
44
+ _state["pii_key"] = pii_key.encode("utf-8") if pii_key else None
45
+ if _state["pii_key"] is None:
46
+ logger.debug(
47
+ "sovergate: no pii_key set — PII tokens will be positional and not "
48
+ "correlatable across logs (unique-entity metrics unavailable)."
49
+ )
50
+
51
+ def _send(payload: dict) -> bool:
52
+ return send(api_key, payload, base_url=_state["base_url"])
53
+
54
+ q = LogQueue(send_fn=_send)
55
+ q.start()
56
+ _state["queue"] = q
57
+
58
+ logger.debug("sovergate initialised (base_url=%s)", _state["base_url"])
59
+ except Exception as e:
60
+ # Never let SDK setup break the host app; logging just stays disabled.
61
+ _state["queue"] = None
62
+ logger.warning("sovergate: init failed, logging disabled: %s", e)
63
+
64
+
65
+ def _enable_debug_logging() -> None:
66
+ """Verbose output for the SDK's own loggers only — never the app's root logger."""
67
+ logger.setLevel(logging.DEBUG)
68
+ if not logger.handlers:
69
+ handler = logging.StreamHandler()
70
+ handler.setFormatter(logging.Formatter("%(name)s %(levelname)s %(message)s"))
71
+ logger.addHandler(handler)
72
+
73
+
74
+ def instrument(library: Any) -> None:
75
+ """
76
+ Patch an LLM client library so all calls are automatically logged.
77
+
78
+ Supported:
79
+ - the openai module, or an OpenAI / AsyncOpenAI client (chat.completions.create)
80
+ - an Anthropic / AsyncAnthropic client (messages.create)
81
+
82
+ Usage:
83
+ import openai
84
+ sovergate.instrument(openai)
85
+
86
+ import anthropic
87
+ client = anthropic.Anthropic()
88
+ sovergate.instrument(client)
89
+
90
+ The original library and its responses are completely unchanged.
91
+ Never raises: if instrumentation isn't possible, a warning is logged and
92
+ the library is left untouched.
93
+ """
94
+ if _state["queue"] is None:
95
+ logger.warning(
96
+ "sovergate: instrument() called before init(api_key=...) — LLM calls will not be logged."
97
+ )
98
+ return
99
+
100
+ try:
101
+ enqueue = _state["queue"].enqueue
102
+ pii_key = _state["pii_key"]
103
+ module_name = getattr(library, "__name__", "") or getattr(library, "__class__", type(library)).__name__
104
+
105
+ if module_name == "openai" or hasattr(library, "chat"):
106
+ from .patcher import patch_openai
107
+ patch_openai(library, enqueue, pii_key)
108
+ return
109
+
110
+ if module_name == "anthropic" and not hasattr(library, "messages"):
111
+ logger.warning(
112
+ "sovergate: pass an Anthropic client instance, not the anthropic module — "
113
+ "e.g. sovergate.instrument(anthropic.Anthropic()). LLM calls will not be logged."
114
+ )
115
+ return
116
+
117
+ if hasattr(library, "messages"):
118
+ from .patcher import patch_anthropic
119
+ patch_anthropic(library, enqueue, pii_key)
120
+ return
121
+
122
+ logger.warning(
123
+ "sovergate: unsupported library '%s' — LLM calls will not be logged. "
124
+ "Supported: openai, anthropic. Open an issue at "
125
+ "https://github.com/sovergate/sdk-python/issues",
126
+ module_name,
127
+ )
128
+ except Exception as e:
129
+ logger.warning("sovergate: instrument() failed, LLM calls will not be logged: %s", e)
130
+
131
+
132
+ def shutdown() -> None:
133
+ """Stop the background sender thread. Records still queued at that point are discarded."""
134
+ try:
135
+ if _state["queue"]:
136
+ _state["queue"].stop()
137
+ except Exception as e:
138
+ logger.debug("sovergate: shutdown error: %s", e)