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.
- sovergate-0.1.0a1/.gitignore +9 -0
- sovergate-0.1.0a1/LICENSE +21 -0
- sovergate-0.1.0a1/PKG-INFO +185 -0
- sovergate-0.1.0a1/README.md +153 -0
- sovergate-0.1.0a1/SECURITY.md +21 -0
- sovergate-0.1.0a1/pyproject.toml +55 -0
- sovergate-0.1.0a1/sovergate/__init__.py +4 -0
- sovergate-0.1.0a1/sovergate/client.py +138 -0
- sovergate-0.1.0a1/sovergate/patcher.py +141 -0
- sovergate-0.1.0a1/sovergate/py.typed +0 -0
- sovergate-0.1.0a1/sovergate/queue.py +68 -0
- sovergate-0.1.0a1/sovergate/scrubber.py +115 -0
- sovergate-0.1.0a1/sovergate/sender.py +38 -0
- sovergate-0.1.0a1/tests/test_client.py +65 -0
- sovergate-0.1.0a1/tests/test_integration.py +195 -0
- sovergate-0.1.0a1/tests/test_patcher.py +104 -0
- sovergate-0.1.0a1/tests/test_queue.py +52 -0
- sovergate-0.1.0a1/tests/test_scrubber.py +141 -0
- sovergate-0.1.0a1/tests/test_sender.py +85 -0
|
@@ -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,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)
|