datafog-core 0.4.5b2__tar.gz → 0.4.5b4__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.
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/Cargo.lock +4 -4
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/PKG-INFO +7 -7
- {datafog_core-0.4.5b2/crates/core → datafog_core-0.4.5b4}/README.md +6 -6
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/Cargo.toml +2 -2
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/src/models.rs +48 -4
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/Cargo.toml +1 -1
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4/crates/core}/README.md +6 -6
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/models/gliner2.rs +106 -19
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/models/tests.rs +270 -3
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/models.rs +275 -13
- datafog_core-0.4.5b4/crates/core/tests/model_datafog.rs +134 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/pyproject.toml +1 -1
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/python/datafog_core/__init__.pyi +2 -1
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/python/datafog_core/_gliner2_worker.py +1 -1
- datafog_core-0.4.5b2/crates/core/tests/model_datafog.rs +0 -44
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/Cargo.toml +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/src/lib.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/api_key_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/bearer_token_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/capabilities_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/credential_overlap_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/credential_uri_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/email_context_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/german_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/jwt_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/npi_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/private_key_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/requirements-typing.txt +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/synthetic_fixtures.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/test_installed.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/test_python49_integration.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/test_typing.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/us_routing_number_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/bindings/python/tests/uuid_conformance.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/examples/german_benchmark.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/examples/scan_benchmark.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/api_key.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/bearer_token.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/capabilities.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/credential_uri.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/email_context.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/german.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/jwt.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/lib.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/models/gliner2-lock.json +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/models/process.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/npi.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/offsets.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/private_key.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/scan_selection_tests.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/selection_tests.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/structured.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/us_routing_number.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/src/uuid.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/api_key.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/bearer_token.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/capabilities.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/credential_overlaps.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/credential_uri.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/email_context.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/german.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/jwt.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/npi.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/private_key.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/support/synthetic_fixtures.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/us_routing_number.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/crates/core/tests/uuid.rs +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/python/datafog_core/__init__.py +0 -0
- {datafog_core-0.4.5b2 → datafog_core-0.4.5b4}/python/datafog_core/py.typed +0 -0
|
@@ -80,7 +80,7 @@ checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d"
|
|
|
80
80
|
|
|
81
81
|
[[package]]
|
|
82
82
|
name = "datafog-core"
|
|
83
|
-
version = "0.4.5-beta.
|
|
83
|
+
version = "0.4.5-beta.4"
|
|
84
84
|
dependencies = [
|
|
85
85
|
"base64",
|
|
86
86
|
"futures",
|
|
@@ -94,7 +94,7 @@ dependencies = [
|
|
|
94
94
|
|
|
95
95
|
[[package]]
|
|
96
96
|
name = "datafog-core-python"
|
|
97
|
-
version = "0.4.5-beta.
|
|
97
|
+
version = "0.4.5-beta.4"
|
|
98
98
|
dependencies = [
|
|
99
99
|
"datafog-core",
|
|
100
100
|
"pyo3",
|
|
@@ -105,7 +105,7 @@ dependencies = [
|
|
|
105
105
|
|
|
106
106
|
[[package]]
|
|
107
107
|
name = "datafog-node"
|
|
108
|
-
version = "0.4.5-beta.
|
|
108
|
+
version = "0.4.5-beta.4"
|
|
109
109
|
dependencies = [
|
|
110
110
|
"datafog-core",
|
|
111
111
|
"napi",
|
|
@@ -116,7 +116,7 @@ dependencies = [
|
|
|
116
116
|
|
|
117
117
|
[[package]]
|
|
118
118
|
name = "datafog-wasm"
|
|
119
|
-
version = "0.4.5-beta.
|
|
119
|
+
version = "0.4.5-beta.4"
|
|
120
120
|
dependencies = [
|
|
121
121
|
"datafog-core",
|
|
122
122
|
"serde",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datafog-core
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5b4
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: License :: OSI Approved :: MIT License
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -59,7 +59,7 @@ DataFog Core is a Rust library with Python, Node.js, and browser WebAssembly bin
|
|
|
59
59
|
|
|
60
60
|
Rule-based scanning runs locally without model weights or downloads. Optional local model sessions add DataFog and GLiNER2 support in Rust, Python, and Node.js.
|
|
61
61
|
|
|
62
|
-
**Stable:** [0.4.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.4) · **Model beta:** [0.4.5-beta.
|
|
62
|
+
**Stable:** [0.4.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.4) · **Model beta:** [0.4.5-beta.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.5-beta.4)
|
|
63
63
|
|
|
64
64
|
## Quickstart
|
|
65
65
|
|
|
@@ -198,23 +198,23 @@ Keep scan settings under `scan` and transformation policy under `transform`. A d
|
|
|
198
198
|
|
|
199
199
|
## Local models (beta)
|
|
200
200
|
|
|
201
|
-
[0.4.5-beta.
|
|
201
|
+
[0.4.5-beta.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.5-beta.4) adds local sessions for the DataFog PII model and GLiNER2 in Rust, Python, and Node.js on all five native platforms.
|
|
202
202
|
|
|
203
203
|
Install the beta for your runtime:
|
|
204
204
|
|
|
205
205
|
```bash
|
|
206
|
-
python -m pip install 'datafog-core==0.4.
|
|
206
|
+
python -m pip install 'datafog-core==0.4.5b4'
|
|
207
207
|
npm install @datafog/node@beta
|
|
208
|
-
cargo add datafog-core@0.4.5-beta.
|
|
208
|
+
cargo add datafog-core@0.4.5-beta.4 --features models
|
|
209
209
|
```
|
|
210
210
|
|
|
211
211
|
For GLiNER2, install its optional dependencies in the Python environment used by the runner:
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
|
-
python -m pip install 'datafog-core[gliner2]==0.4.
|
|
214
|
+
python -m pip install 'datafog-core[gliner2]==0.4.5b4'
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
The GLiNER2 runner ships in the Python package and also serves Node.js and Rust callers. Download and verify model files separately, then use `ModelSession.open`, text `scan`, and `close`. Reuse sessions across calls. Both backends return consistent results and errors, with cancellation and cleanup support.
|
|
217
|
+
GLiNER2 supports the PII checkpoint and compatible general-purpose span models with caller-defined labels. The GLiNER2 runner ships in the Python package and also serves Node.js and Rust callers. Download and verify model files separately, then use `ModelSession.open`, text `scan`, and `close`. Reuse sessions across calls. Both backends return consistent results and errors, with cancellation and cleanup support.
|
|
218
218
|
|
|
219
219
|
[DataFog setup and session API →](docs/model-sessions.md) · [GLiNER2 setup →](docs/gliner2-model.md)
|
|
220
220
|
|
|
@@ -26,7 +26,7 @@ DataFog Core is a Rust library with Python, Node.js, and browser WebAssembly bin
|
|
|
26
26
|
|
|
27
27
|
Rule-based scanning runs locally without model weights or downloads. Optional local model sessions add DataFog and GLiNER2 support in Rust, Python, and Node.js.
|
|
28
28
|
|
|
29
|
-
**Stable:** [0.4.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.4) · **Model beta:** [0.4.5-beta.
|
|
29
|
+
**Stable:** [0.4.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.4) · **Model beta:** [0.4.5-beta.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.5-beta.4)
|
|
30
30
|
|
|
31
31
|
## Quickstart
|
|
32
32
|
|
|
@@ -165,23 +165,23 @@ Keep scan settings under `scan` and transformation policy under `transform`. A d
|
|
|
165
165
|
|
|
166
166
|
## Local models (beta)
|
|
167
167
|
|
|
168
|
-
[0.4.5-beta.
|
|
168
|
+
[0.4.5-beta.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.5-beta.4) adds local sessions for the DataFog PII model and GLiNER2 in Rust, Python, and Node.js on all five native platforms.
|
|
169
169
|
|
|
170
170
|
Install the beta for your runtime:
|
|
171
171
|
|
|
172
172
|
```bash
|
|
173
|
-
python -m pip install 'datafog-core==0.4.
|
|
173
|
+
python -m pip install 'datafog-core==0.4.5b4'
|
|
174
174
|
npm install @datafog/node@beta
|
|
175
|
-
cargo add datafog-core@0.4.5-beta.
|
|
175
|
+
cargo add datafog-core@0.4.5-beta.4 --features models
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
For GLiNER2, install its optional dependencies in the Python environment used by the runner:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
|
-
python -m pip install 'datafog-core[gliner2]==0.4.
|
|
181
|
+
python -m pip install 'datafog-core[gliner2]==0.4.5b4'
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
The GLiNER2 runner ships in the Python package and also serves Node.js and Rust callers. Download and verify model files separately, then use `ModelSession.open`, text `scan`, and `close`. Reuse sessions across calls. Both backends return consistent results and errors, with cancellation and cleanup support.
|
|
184
|
+
GLiNER2 supports the PII checkpoint and compatible general-purpose span models with caller-defined labels. The GLiNER2 runner ships in the Python package and also serves Node.js and Rust callers. Download and verify model files separately, then use `ModelSession.open`, text `scan`, and `close`. Reuse sessions across calls. Both backends return consistent results and errors, with cancellation and cleanup support.
|
|
185
185
|
|
|
186
186
|
[DataFog setup and session API →](docs/model-sessions.md) · [GLiNER2 setup →](docs/gliner2-model.md)
|
|
187
187
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "datafog-core-python"
|
|
3
|
-
version = "0.4.5-beta.
|
|
3
|
+
version = "0.4.5-beta.4"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.88"
|
|
6
6
|
description = "Python bindings for datafog-core"
|
|
@@ -12,7 +12,7 @@ name = "datafog_core"
|
|
|
12
12
|
crate-type = ["cdylib"]
|
|
13
13
|
|
|
14
14
|
[dependencies]
|
|
15
|
-
datafog-core = { version = "0.4.5-beta.
|
|
15
|
+
datafog-core = { version = "0.4.5-beta.4", path = "../../crates/core", features = ["models"] }
|
|
16
16
|
pyo3 = { version = "0.29", features = ["abi3-py310"] }
|
|
17
17
|
pyo3-async-runtimes = { version = "0.29", features = ["tokio-runtime"] }
|
|
18
18
|
serde_json = "1"
|
|
@@ -91,8 +91,49 @@ impl ModelSession {
|
|
|
91
91
|
.map(|session| Self { session })
|
|
92
92
|
.map_err(|error| model_error(py, error))
|
|
93
93
|
}
|
|
94
|
-
|
|
94
|
+
/// Rule detectors handle the entity types they support and the model runs
|
|
95
|
+
/// only for requested types it alone provides. Omitting `config` equals
|
|
96
|
+
/// `{}`: every enabled rule detector plus the model's remaining labels.
|
|
97
|
+
#[pyo3(signature = (text, config=None, *, cancellation=None))]
|
|
95
98
|
fn scan(
|
|
99
|
+
&self,
|
|
100
|
+
py: Python<'_>,
|
|
101
|
+
text: String,
|
|
102
|
+
config: Option<&Bound<'_, PyAny>>,
|
|
103
|
+
cancellation: Option<PyRef<'_, ModelCancellation>>,
|
|
104
|
+
) -> PyResult<ModelScanResult> {
|
|
105
|
+
let routed = match config {
|
|
106
|
+
Some(config) => {
|
|
107
|
+
let value = py_to_json(py, config, "")?;
|
|
108
|
+
self.session
|
|
109
|
+
.routed_config(&value)
|
|
110
|
+
.map_err(|error| model_error(py, error))?
|
|
111
|
+
}
|
|
112
|
+
None => self.session.default_routed_config(),
|
|
113
|
+
};
|
|
114
|
+
// Rule-only routing never reserves the model worker.
|
|
115
|
+
let request = if routed.needs_model() {
|
|
116
|
+
Some(
|
|
117
|
+
self.session
|
|
118
|
+
.prepare_request()
|
|
119
|
+
.map_err(|error| model_error(py, error))?,
|
|
120
|
+
)
|
|
121
|
+
} else {
|
|
122
|
+
None
|
|
123
|
+
};
|
|
124
|
+
let cancellation = cancellation
|
|
125
|
+
.map(|v| Arc::clone(&v.cancellation))
|
|
126
|
+
.unwrap_or_default();
|
|
127
|
+
py.detach(|| {
|
|
128
|
+
self.session
|
|
129
|
+
.scan_routed_request(request.as_ref(), &text, &routed, &cancellation)
|
|
130
|
+
})
|
|
131
|
+
.map(ModelScanResult::from)
|
|
132
|
+
.map_err(|error| model_error(py, error))
|
|
133
|
+
}
|
|
134
|
+
/// Raw model output: every model label, with no rule detectors.
|
|
135
|
+
#[pyo3(signature = (text, *, cancellation=None))]
|
|
136
|
+
fn scan_model(
|
|
96
137
|
&self,
|
|
97
138
|
py: Python<'_>,
|
|
98
139
|
text: String,
|
|
@@ -105,9 +146,12 @@ impl ModelSession {
|
|
|
105
146
|
let cancellation = cancellation
|
|
106
147
|
.map(|v| Arc::clone(&v.cancellation))
|
|
107
148
|
.unwrap_or_default();
|
|
108
|
-
py.detach(||
|
|
109
|
-
.
|
|
110
|
-
|
|
149
|
+
py.detach(|| {
|
|
150
|
+
self.session
|
|
151
|
+
.scan_model_request(&request, &text, &cancellation)
|
|
152
|
+
})
|
|
153
|
+
.map(ModelScanResult::from)
|
|
154
|
+
.map_err(|error| model_error(py, error))
|
|
111
155
|
}
|
|
112
156
|
fn close(&self, py: Python<'_>) -> PyResult<()> {
|
|
113
157
|
py.detach(|| self.session.close())
|
|
@@ -26,7 +26,7 @@ DataFog Core is a Rust library with Python, Node.js, and browser WebAssembly bin
|
|
|
26
26
|
|
|
27
27
|
Rule-based scanning runs locally without model weights or downloads. Optional local model sessions add DataFog and GLiNER2 support in Rust, Python, and Node.js.
|
|
28
28
|
|
|
29
|
-
**Stable:** [0.4.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.4) · **Model beta:** [0.4.5-beta.
|
|
29
|
+
**Stable:** [0.4.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.4) · **Model beta:** [0.4.5-beta.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.5-beta.4)
|
|
30
30
|
|
|
31
31
|
## Quickstart
|
|
32
32
|
|
|
@@ -165,23 +165,23 @@ Keep scan settings under `scan` and transformation policy under `transform`. A d
|
|
|
165
165
|
|
|
166
166
|
## Local models (beta)
|
|
167
167
|
|
|
168
|
-
[0.4.5-beta.
|
|
168
|
+
[0.4.5-beta.4](https://github.com/DataFog/datafog-core/releases/tag/v0.4.5-beta.4) adds local sessions for the DataFog PII model and GLiNER2 in Rust, Python, and Node.js on all five native platforms.
|
|
169
169
|
|
|
170
170
|
Install the beta for your runtime:
|
|
171
171
|
|
|
172
172
|
```bash
|
|
173
|
-
python -m pip install 'datafog-core==0.4.
|
|
173
|
+
python -m pip install 'datafog-core==0.4.5b4'
|
|
174
174
|
npm install @datafog/node@beta
|
|
175
|
-
cargo add datafog-core@0.4.5-beta.
|
|
175
|
+
cargo add datafog-core@0.4.5-beta.4 --features models
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
For GLiNER2, install its optional dependencies in the Python environment used by the runner:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
|
-
python -m pip install 'datafog-core[gliner2]==0.4.
|
|
181
|
+
python -m pip install 'datafog-core[gliner2]==0.4.5b4'
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
The GLiNER2 runner ships in the Python package and also serves Node.js and Rust callers. Download and verify model files separately, then use `ModelSession.open`, text `scan`, and `close`. Reuse sessions across calls. Both backends return consistent results and errors, with cancellation and cleanup support.
|
|
184
|
+
GLiNER2 supports the PII checkpoint and compatible general-purpose span models with caller-defined labels. The GLiNER2 runner ships in the Python package and also serves Node.js and Rust callers. Download and verify model files separately, then use `ModelSession.open`, text `scan`, and `close`. Reuse sessions across calls. Both backends return consistent results and errors, with cancellation and cleanup support.
|
|
185
185
|
|
|
186
186
|
[DataFog setup and session API →](docs/model-sessions.md) · [GLiNER2 setup →](docs/gliner2-model.md)
|
|
187
187
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
//!
|
|
1
|
+
//! Official GLiNER2 span runtime, executed in an explicitly installed Python environment.
|
|
2
2
|
use super::*;
|
|
3
3
|
use std::process::Command;
|
|
4
4
|
|
|
5
5
|
pub const MODEL: &str = "fastino/gliner2-privacy-filter-PII-multi";
|
|
6
6
|
const ADAPTER: &str = "gliner2-worker-v1";
|
|
7
7
|
const SCALE: &str = "normalized_span_sigmoid";
|
|
8
|
-
const LABELS: &str = "person full_name first_name middle_name last_name date_of_birth email phone_number address street_address city state_or_region postal_code country government_id national_id_number passport_number drivers_license_number license_number tax_id tax_number bank_account account_number routing_number iban payment_card card_number card_expiry card_cvv username ip_address account_id sensitive_account_id password secret api_key access_token recovery_code sensitive_date document_date expiration_date transaction_date";
|
|
9
8
|
|
|
10
9
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
11
10
|
#[serde(deny_unknown_fields)]
|
|
@@ -64,9 +63,13 @@ fn validate_config(config: &Gliner2Config) -> Result<(), ModelError> {
|
|
|
64
63
|
return Err(ModelError::config("empty_value", "/gliner2/labels"));
|
|
65
64
|
}
|
|
66
65
|
let mut seen = std::collections::BTreeSet::new();
|
|
66
|
+
let mut entity_types = std::collections::BTreeSet::new();
|
|
67
67
|
for (index, label) in config.labels.iter().enumerate() {
|
|
68
68
|
let path = format!("/gliner2/labels/{index}");
|
|
69
|
-
if
|
|
69
|
+
if label.label.trim().is_empty()
|
|
70
|
+
|| label.label.trim() != label.label
|
|
71
|
+
|| !seen.insert(&label.label)
|
|
72
|
+
|| !entity_types.insert(mapping(&label.label))
|
|
70
73
|
{
|
|
71
74
|
return Err(ModelError::config(
|
|
72
75
|
"invalid_value",
|
|
@@ -142,24 +145,50 @@ pub(super) fn open(
|
|
|
142
145
|
return Err(invalid());
|
|
143
146
|
}
|
|
144
147
|
let manifest: BundleManifest = serde_json::from_slice(&bytes).map_err(|_| invalid())?;
|
|
145
|
-
let
|
|
146
|
-
|
|
147
|
-
let
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
let identity = validate_model_identity(&manifest, "gliner2")?;
|
|
149
|
+
let model_name = identity.map_or_else(|| MODEL.to_owned(), |model| model.id.clone());
|
|
150
|
+
let adapter_version = identity.map_or_else(
|
|
151
|
+
|| ADAPTER.to_owned(),
|
|
152
|
+
|model| format!("{ADAPTER}:{}", model.revision),
|
|
153
|
+
);
|
|
154
|
+
if manifest.platform != "portable-cpu" || manifest.files.is_empty() {
|
|
150
155
|
return Err(invalid());
|
|
151
156
|
}
|
|
152
|
-
|
|
153
|
-
let
|
|
154
|
-
|
|
157
|
+
if identity.is_none() {
|
|
158
|
+
let lock: serde_json::Value =
|
|
159
|
+
serde_json::from_str(include_str!("gliner2-lock.json")).map_err(|_| invalid())?;
|
|
160
|
+
let expected: BTreeMap<String, AssetIdentity> =
|
|
161
|
+
serde_json::from_value(lock["files"].clone()).map_err(|_| invalid())?;
|
|
162
|
+
if manifest.files.len() != expected.len() {
|
|
155
163
|
return Err(invalid());
|
|
156
164
|
}
|
|
165
|
+
for (name, expected) in expected {
|
|
166
|
+
let asset = manifest.files.get(&name).ok_or_else(invalid)?;
|
|
167
|
+
if asset.sha256 != expected.sha256 || asset.bytes != expected.bytes {
|
|
168
|
+
return Err(invalid());
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// The existing worker loads this local, single-file GLiNER2 span-model layout.
|
|
173
|
+
for required in [
|
|
174
|
+
"config.json",
|
|
175
|
+
"encoder_config/config.json",
|
|
176
|
+
"tokenizer.json",
|
|
177
|
+
"tokenizer_config.json",
|
|
178
|
+
"model.safetensors",
|
|
179
|
+
] {
|
|
180
|
+
if !manifest.files.contains_key(required) {
|
|
181
|
+
return Err(invalid());
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (name, asset) in &manifest.files {
|
|
185
|
+
validate_asset_name(name)?;
|
|
157
186
|
let path = bundle.join(name).canonicalize().map_err(|_| invalid())?;
|
|
158
187
|
if !path.starts_with(&bundle) || !path.is_file() {
|
|
159
188
|
return Err(invalid());
|
|
160
189
|
}
|
|
161
190
|
let (hash, size) = file_digest(&path, deadline)?;
|
|
162
|
-
if hash !=
|
|
191
|
+
if hash != asset.sha256 || size != asset.bytes {
|
|
163
192
|
return Err(invalid());
|
|
164
193
|
}
|
|
165
194
|
}
|
|
@@ -178,8 +207,8 @@ pub(super) fn open(
|
|
|
178
207
|
let session = ModelSession {
|
|
179
208
|
descriptor: ModelSessionDescriptor {
|
|
180
209
|
backend: "gliner2_worker".into(),
|
|
181
|
-
model:
|
|
182
|
-
adapter_version
|
|
210
|
+
model: model_name,
|
|
211
|
+
adapter_version,
|
|
183
212
|
labels: options
|
|
184
213
|
.labels
|
|
185
214
|
.iter()
|
|
@@ -255,7 +284,7 @@ pub(super) fn decode(bytes: &[u8], max_findings: usize) -> Result<BackendResult,
|
|
|
255
284
|
serde_json::from_value(value).map_err(|_| ModelError::invalid_output())?;
|
|
256
285
|
let expected = runtime();
|
|
257
286
|
if output.runtime != expected
|
|
258
|
-
|| output.worker_version != "0.4.
|
|
287
|
+
|| output.worker_version != "0.4.5b4"
|
|
259
288
|
|| output.findings.len() > max_findings
|
|
260
289
|
{
|
|
261
290
|
return Err(ModelError::invalid_output());
|
|
@@ -303,13 +332,71 @@ mod tests {
|
|
|
303
332
|
bad.labels.push(bad.labels[0].clone());
|
|
304
333
|
assert!(validate_config(&bad).is_err());
|
|
305
334
|
let mut bad = parsed.clone();
|
|
306
|
-
bad.labels[0].label = "
|
|
307
|
-
|
|
335
|
+
bad.labels[0].label = "current_employer".into();
|
|
336
|
+
bad.labels[0].description = Some("The caller's current employer".into());
|
|
337
|
+
assert!(validate_config(&bad).is_ok());
|
|
338
|
+
for label in ["", " ", " employer", "employer "] {
|
|
339
|
+
bad.labels[0].label = label.into();
|
|
340
|
+
assert!(validate_config(&bad).is_err());
|
|
341
|
+
}
|
|
308
342
|
let mut bad = parsed;
|
|
309
343
|
bad.chunk_overlap = bad.chunk_size;
|
|
310
344
|
assert!(validate_config(&bad).is_err());
|
|
311
345
|
}
|
|
312
346
|
#[test]
|
|
347
|
+
fn labels_must_have_distinct_output_types() {
|
|
348
|
+
for (first, second) in [("employer", "EMPLOYER"), ("phone_number", "phone")] {
|
|
349
|
+
let config = Gliner2Config {
|
|
350
|
+
python: std::env::current_exe().unwrap(),
|
|
351
|
+
labels: [first, second]
|
|
352
|
+
.into_iter()
|
|
353
|
+
.map(|label| Gliner2Label {
|
|
354
|
+
label: label.into(),
|
|
355
|
+
threshold: 0.5,
|
|
356
|
+
description: None,
|
|
357
|
+
})
|
|
358
|
+
.collect(),
|
|
359
|
+
intra_threads: 1,
|
|
360
|
+
chunk_size: 384,
|
|
361
|
+
chunk_overlap: 64,
|
|
362
|
+
};
|
|
363
|
+
assert!(validate_config(&config).is_err());
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#[test]
|
|
368
|
+
fn custom_model_labels_keep_types_offsets_and_transformation_support() {
|
|
369
|
+
let text = "👋 Cedar Labs";
|
|
370
|
+
let mut descriptor = super::super::tests::descriptor();
|
|
371
|
+
descriptor.backend = "gliner2_worker".into();
|
|
372
|
+
descriptor.model = "fastino/gliner2-base-v1".into();
|
|
373
|
+
descriptor.labels = [("current_employer".into(), "CURRENT_EMPLOYER".into())].into();
|
|
374
|
+
descriptor.thresholds = [("current_employer".into(), 0.5)].into();
|
|
375
|
+
descriptor.score_scale = SCALE.into();
|
|
376
|
+
let response = serde_json::json!({
|
|
377
|
+
"worker_version": "0.4.5b4", "runtime": runtime(),
|
|
378
|
+
"findings": [{"label": "current_employer", "start": 2, "end": 12,
|
|
379
|
+
"text": "Cedar Labs", "raw_score": 0.9}]
|
|
380
|
+
})
|
|
381
|
+
.to_string();
|
|
382
|
+
let entries = convert(text, decode(response.as_bytes(), 1).unwrap(), &descriptor).unwrap();
|
|
383
|
+
let findings: Vec<_> = entries.into_iter().map(|(finding, _)| finding).collect();
|
|
384
|
+
assert_eq!(findings[0].entity_type, "CURRENT_EMPLOYER");
|
|
385
|
+
assert_eq!(findings[0].byte_range, TextRange { start: 5, end: 15 });
|
|
386
|
+
assert_eq!(findings[0].codepoint_range, TextRange { start: 2, end: 12 });
|
|
387
|
+
assert_eq!(findings[0].confidence, None);
|
|
388
|
+
let transformed = crate::transform(
|
|
389
|
+
text,
|
|
390
|
+
&findings,
|
|
391
|
+
&crate::TransformationConfig::new(crate::TransformationStrategy::Redact),
|
|
392
|
+
)
|
|
393
|
+
.unwrap();
|
|
394
|
+
assert_eq!(transformed.text, "👋 [CURRENT_EMPLOYER]");
|
|
395
|
+
let mut unexpected = descriptor;
|
|
396
|
+
unexpected.labels.clear();
|
|
397
|
+
assert!(convert(text, decode(response.as_bytes(), 1).unwrap(), &unexpected).is_err());
|
|
398
|
+
}
|
|
399
|
+
#[test]
|
|
313
400
|
fn uncalibrated_scores_are_validated_before_thresholding_and_rebased() {
|
|
314
401
|
let mut descriptor = super::super::tests::descriptor();
|
|
315
402
|
descriptor.backend = "gliner2_worker".into();
|
|
@@ -318,7 +405,7 @@ mod tests {
|
|
|
318
405
|
descriptor.thresholds = [("street_address".into(), 0.5)].into();
|
|
319
406
|
descriptor.score_scale = SCALE.into();
|
|
320
407
|
let response = |score: f64| {
|
|
321
|
-
serde_json::json!({"worker_version":"0.4.
|
|
408
|
+
serde_json::json!({"worker_version":"0.4.5b4","runtime":runtime(),
|
|
322
409
|
"findings":[{"label":"street_address","start":2,"end":17,"text":"123 Main Street","raw_score":score}]}).to_string()
|
|
323
410
|
};
|
|
324
411
|
let valid = decode(response(0.9).as_bytes(), 100).unwrap();
|
|
@@ -346,7 +433,7 @@ mod tests {
|
|
|
346
433
|
.is_empty()
|
|
347
434
|
);
|
|
348
435
|
let corrupt =
|
|
349
|
-
serde_json::json!({"worker_version":"0.4.
|
|
436
|
+
serde_json::json!({"worker_version":"0.4.5b4","runtime":{},"findings":[]}).to_string();
|
|
350
437
|
assert!(decode(corrupt.as_bytes(), 100).is_err());
|
|
351
438
|
}
|
|
352
439
|
}
|