mirror of
https://github.com/data-privacy-stack/presidio.git
synced 2026-07-23 11:20:55 -05:00
Support batch processing over the REST API. (#1806)
* Support batch processing over the REST API. * Partially fix e2e tests * Fix e2e tests * ruff * ruff * consistent use of strings * Update API docs * Support batch processing over the REST API. * Partially fix e2e tests * Fix e2e tests * ruff * ruff * consistent use of strings * Update API docs
This commit is contained in:
@@ -40,13 +40,20 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
description: "A list analysis results"
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/RecognizerResultWithAnalysisExplanation"
|
||||
oneOf:
|
||||
- description: "A list of analysis results (when input text is a single string)"
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/RecognizerResultWithAnalysisExplanation"
|
||||
- description: "A list of analysis result lists (when input text is an array of strings)"
|
||||
type: array
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/RecognizerResultWithAnalysisExplanation"
|
||||
|
||||
examples:
|
||||
Enhanced response:
|
||||
Enhanced response (single text):
|
||||
value:
|
||||
[
|
||||
{ "entity_type": "PERSON", "start": 0, "end": 10, "score": 0.85,
|
||||
@@ -66,7 +73,7 @@ paths:
|
||||
}
|
||||
}
|
||||
]
|
||||
Lean response:
|
||||
Lean response (single text):
|
||||
value:
|
||||
[
|
||||
{
|
||||
@@ -77,6 +84,17 @@ paths:
|
||||
"start": 30
|
||||
}
|
||||
]
|
||||
Batch response (array of texts):
|
||||
value:
|
||||
[
|
||||
[
|
||||
{ "entity_type": "PERSON", "start": 0, "end": 10, "score": 0.85, "analysis_explanation": null },
|
||||
{ "entity_type": "US_DRIVER_LICENSE", "start": 30, "end": 38, "score": 0.6499999999999999, "analysis_explanation": null }
|
||||
],
|
||||
[
|
||||
{ "entity_type": "EMAIL_ADDRESS", "start": 15, "end": 35, "score": 0.95, "analysis_explanation": null }
|
||||
]
|
||||
]
|
||||
|
||||
/recognizers:
|
||||
get:
|
||||
@@ -272,13 +290,19 @@ components:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AnalyzeRequest"
|
||||
examples:
|
||||
Minimal Request:
|
||||
Minimal Request (single text):
|
||||
value:
|
||||
{
|
||||
"text": "John Smith drivers license is AC432223",
|
||||
"language": "en"
|
||||
}
|
||||
Enhanced Request :
|
||||
Minimal Request (batch request):
|
||||
value:
|
||||
{
|
||||
"text": ["John Smith drivers license is AC432223", "John Smith drivers license is AC432223 and the zip code is 12345"],
|
||||
"language": "en"
|
||||
}
|
||||
Enhanced Request (single text):
|
||||
value:
|
||||
{
|
||||
"text": "John Smith drivers license is AC432223 and the zip code is 12345",
|
||||
@@ -370,8 +394,12 @@ components:
|
||||
- language
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
description: "The text to analyze"
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: array
|
||||
items:
|
||||
type: string
|
||||
description: "The text to analyze. Can be a single string or an array of strings."
|
||||
example: "hello world, my name is Jane Doe. My number is: 034453334"
|
||||
language:
|
||||
type: string
|
||||
|
||||
@@ -572,3 +572,34 @@ def test_given_regex_flags_and_normal_entities_are_returned():
|
||||
assert equal_json_strings(
|
||||
expected_response, response_content
|
||||
)
|
||||
|
||||
@pytest.mark.api
|
||||
def test_given_a_correct_analyze_input_then_return_full_response_batched():
|
||||
request_body = """
|
||||
{
|
||||
"text": [
|
||||
"John Smith drivers license is AC432223",
|
||||
"John Smith drivers license is AC432223"
|
||||
],
|
||||
"language": "en"
|
||||
}
|
||||
"""
|
||||
|
||||
response_status, response_content = analyze(request_body)
|
||||
|
||||
expected_response = """
|
||||
[
|
||||
[
|
||||
{"entity_type": "PERSON", "start": 0, "end": 10, "score": 0.85, "analysis_explanation":null},
|
||||
{"entity_type": "US_DRIVER_LICENSE", "start": 30, "end": 38, "score": 0.6499999999999999, "analysis_explanation":null}
|
||||
],
|
||||
[
|
||||
{"entity_type": "PERSON", "start": 0, "end": 10, "score": 0.85, "analysis_explanation":null},
|
||||
{"entity_type": "US_DRIVER_LICENSE", "start": 30, "end": 38, "score": 0.6499999999999999, "analysis_explanation":null}
|
||||
]
|
||||
]
|
||||
"""
|
||||
assert response_status == 200
|
||||
assert equal_json_strings(
|
||||
expected_response, response_content
|
||||
)
|
||||
|
||||
@@ -8,10 +8,17 @@ from pathlib import Path
|
||||
from typing import Tuple
|
||||
|
||||
from flask import Flask, Response, jsonify, request
|
||||
from presidio_analyzer import AnalyzerEngine, AnalyzerEngineProvider, AnalyzerRequest
|
||||
from presidio_analyzer import (
|
||||
AnalyzerEngine,
|
||||
AnalyzerEngineProvider,
|
||||
AnalyzerRequest,
|
||||
BatchAnalyzerEngine,
|
||||
)
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
DEFAULT_PORT = "3000"
|
||||
DEFAULT_BATCH_SIZE = "500"
|
||||
DEFAULT_N_PROCESS = "1"
|
||||
|
||||
LOGGING_CONF_FILE = "logging.ini"
|
||||
|
||||
@@ -46,6 +53,8 @@ class Server:
|
||||
nlp_engine_conf_file=nlp_engine_conf_file,
|
||||
recognizer_registry_conf_file=recognizer_registry_conf_file,
|
||||
).create_engine()
|
||||
|
||||
self.batch_engine = BatchAnalyzerEngine(self.engine)
|
||||
self.logger.info(WELCOME_MESSAGE)
|
||||
|
||||
@self.app.route("/health")
|
||||
@@ -62,11 +71,21 @@ class Server:
|
||||
if not req_data.text:
|
||||
raise Exception("No text provided")
|
||||
|
||||
batch_request = isinstance(req_data.text, list)
|
||||
batch = req_data.text if batch_request else [req_data.text]
|
||||
|
||||
if not req_data.language:
|
||||
raise Exception("No language provided")
|
||||
else:
|
||||
# Make sure the language is supported by the engine.
|
||||
self.engine.get_supported_entities(req_data.language)
|
||||
|
||||
recognizer_result_list = self.engine.analyze(
|
||||
text=req_data.text,
|
||||
iterator = self.batch_engine.analyze_iterator(
|
||||
texts=batch,
|
||||
batch_size=min(
|
||||
len(batch),
|
||||
int(os.environ.get("BATCH_SIZE", DEFAULT_BATCH_SIZE))
|
||||
),
|
||||
language=req_data.language,
|
||||
correlation_id=req_data.correlation_id,
|
||||
score_threshold=req_data.score_threshold,
|
||||
@@ -77,12 +96,19 @@ class Server:
|
||||
allow_list=req_data.allow_list,
|
||||
allow_list_match=req_data.allow_list_match,
|
||||
regex_flags=req_data.regex_flags,
|
||||
n_process=min(
|
||||
len(batch),
|
||||
int(os.environ.get("N_PROCESS", DEFAULT_N_PROCESS))
|
||||
)
|
||||
)
|
||||
_exclude_attributes_from_dto(recognizer_result_list)
|
||||
results = []
|
||||
for recognizer_result_list in iterator:
|
||||
_exclude_attributes_from_dto(recognizer_result_list)
|
||||
results.append(recognizer_result_list)
|
||||
|
||||
return Response(
|
||||
json.dumps(
|
||||
recognizer_result_list,
|
||||
results if batch_request else results[0],
|
||||
default=lambda o: o.to_dict(),
|
||||
sort_keys=True,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user