diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d08119c7..12bfd9071f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file. - The `kr` language code is deprecated in favor of the ISO 639-1 code `ko`. `KrRrnRecognizer` and `KrPassportRecognizer` still accept `kr` in `supported_languages` as a backward-compatibility alias for registries configured against their original class defaults; the alias will be removed in . `KrBrnRecognizer`, `KrDriverLicenseRecognizer` and `KrFrnRecognizer` have only ever defaulted to `ko`, so `kr` was removed from their `default_recognizers.yaml` entries in this release. (#2236) #### Fixed +- `PhoneRecognizer.analyze` assigned the region detected for a match back to the `region` variable it was iterating over, so an international-format number that parsed successfully overwrote the region used to explain every later match in that pass. With the default recognizer, `"My international number is +44 1234 567890, and my US one is (415) 555-0132"` labelled the US number `Recognized as GB region phone number`; swapping the two numbers hid it. Detected spans, scores and entity types were unaffected — only `analysis_explanation` named a region that never matched the number. The detected region is now held in a per-match `matched_region`, and the `NumberParseException` branch reports the region the match was actually found under. - `PhoneRecognizer.DEFAULT_SUPPORTED_REGIONS` used `"UK"`, which is not a valid `phonenumbers` (libphonenumber) region code — region codes are ISO 3166-1 alpha-2, where the United Kingdom is `"GB"`. The `"UK"` entry was a no-op, so UK numbers in national/local format (e.g. `020 7946 0958`) were never detected by default; only international-format `+44 …` numbers matched, because they carry the country code and match under any region. Replaced `"UK"` with `"GB"`. - Language model recognizers (`BasicLangExtractRecognizer`, `AzureOpenAILangExtractRecognizer`) configured in a recognizer registry YAML now honour `config_path` (and other recognizer-specific kwargs). Previously these entries were validated by the strict `PredefinedRecognizerConfig` schema, which has no `config_path` field and does not allow extra keys, so `config_path` was silently dropped and the recognizer fell back to its bundled default model configuration. Added a `LangExtractRecognizerConfig` model (`extra="allow"`) and registered both recognizer class names in `CONFIG_MODEL_MAP`. - `BasicLangExtractRecognizer` now honours values under `langextract.model.provider.language_model_params` (including `timeout` and `num_ctx`). Previously these were silently dropped because `langextract.extract()` ignores its `language_model_params` argument when a pre-built `ModelConfig` is passed via `config=`, causing Ollama-backed recognizers to fall back to langextract's 120s default regardless of the configured timeout. The recognizer now merges `language_model_params` into `ModelConfig.provider_kwargs`, which is the path that reaches the provider constructor. Explicit entries under `provider.kwargs:` still take precedence. Also fixed a `TypeError` when `kwargs:` or `language_model_params:` is `null` in the YAML. (#1943, Thanks @lsternlicht) diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/phone_recognizer.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/phone_recognizer.py index 1df82a8e0e..b6ae4be23d 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/phone_recognizer.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/phone_recognizer.py @@ -70,14 +70,14 @@ def analyze( ): try: parsed_number = phonenumbers.parse(text[match.start : match.end]) - region = phonenumbers.region_code_for_number(parsed_number) - results += [ - self._get_recognizer_result(match, text, region, nlp_artifacts) - ] + matched_region = phonenumbers.region_code_for_number(parsed_number) except NumberParseException: - results += [ - self._get_recognizer_result(match, text, region, nlp_artifacts) - ] + matched_region = region + results += [ + self._get_recognizer_result( + match, text, matched_region, nlp_artifacts + ) + ] return EntityRecognizer.remove_duplicates(results) diff --git a/presidio-analyzer/tests/test_phone_recognizer.py b/presidio-analyzer/tests/test_phone_recognizer.py index fbbe3ffe6e..b74cb7a977 100644 --- a/presidio-analyzer/tests/test_phone_recognizer.py +++ b/presidio-analyzer/tests/test_phone_recognizer.py @@ -123,6 +123,14 @@ def test_when_phone_with_leniency_then_succeed( 2, ["PHONE_NUMBER", "PHONE_NUMBER"], ((16, 30), (60, 77),), 0.4, ['Recognized as US region phone number, using PhoneRecognizer','Recognized as FR region phone number, using PhoneRecognizer']), + # The international number comes first here: it is parsed successfully and + # used to be assigned back to the loop variable, so the national-format + # number that follows was explained with the previous match's region. + # Every case above puts the national number first, which hides that order. + ("My international number is +44 1234 567890, and my US one is (415) 555-0132", + 2, ["PHONE_NUMBER", "PHONE_NUMBER"], + ((27, 42), (61, 75),), 0.4, + ['Recognized as GB region phone number, using PhoneRecognizer','Recognized as US region phone number, using PhoneRecognizer']), # fmt: on ], )