Metadata-Version: 2.5
Name: pyprocessors-coreference
Version: 1.6.1
Summary: Processor grouping the mentions of the same entity
Project-URL: Homepage, https://bitbucket.org/kairntech/pyprocessors_coreference
Author-email: Olivier Terrier <olivier.terrier@kairntech.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Requires-Dist: pymultirole-plugins<1.7.0,>=1.6.0
Provides-Extra: sbom
Requires-Dist: cyclonedx-bom; extra == 'sbom'
Requires-Dist: pip-audit; extra == 'sbom'
Provides-Extra: test
Requires-Dist: pip; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: ruff; extra == 'test'
Description-Content-Type: text/markdown

# pyprocessors-coreference

Processor grouping the mentions of the same entity.

Ce dépôt enregistre un processeur dans le groupe d'entry-points `pyprocessors.plugins` :

| Plugin | Classe |
|--------|--------|
| `coreference` | `pyprocessors_coreference.coreference:CoreferenceProcessor` |

Il regroupe les mentions d'une même entité **avant** le linking, pour qu'elles soient
liées une seule fois, à partir de leur forme la plus longue. C'est l'étape de
regroupement des mentions de l'ADR-0001 de `pyannotators_entityfishing`
([`docs/adr/`](docs/adr/0001-linking-candidats-entityfishing-decision-jev.md)), palier 1 :
les noms seulement.

## Ce qu'il fait

Pour chaque document, entre mentions de même label :

- **même surface normalisée → même groupe.** La normalisation ignore la casse, les
  espaces, le style des apostrophes, et les civilités en tête d'un nom de personne
  (« M. », « Mme », « Dr »…) ;
- **tous les labels de personne forment une seule famille** : le `person` du NER et
  l'`afpperson` des lexiques AFP annotent le même span, et vont dans le même groupe ;
- **un nom partiel de personne rejoint le nom complet dont il est la fin**, que le
  nom complet soit avant ou après lui dans le document : « Macron » et « M. Macron »
  rejoignent « Emmanuel Macron » ;
- **un prénom seul ne rejoint jamais un nom complet** : « Emmanuel » reste à part ;
- **un nom partiel ambigu n'est pas fusionné** : avec « Emmanuel Macron » et
  « Brigitte Macron » dans le texte, « Macron » garde son propre groupe, et liste les
  groupes possibles pour que l'étape de linking tranche ;
- **la règle du nom partiel ne vaut que pour les personnes** : « Airbus » ne rejoint pas
  « Airbus Defence and Space ».

Sur chaque annotation d'un label regroupé, il écrit dans `properties` :

| Propriété | Valeur |
|-----------|--------|
| `entity_group` | l'identifiant du groupe dans le document : `g1`, `g2`… dans l'ordre de première apparition |
| `canonical_form` | la forme la plus longue du groupe, sans civilité : c'est elle qu'on cherche dans la base de connaissances |
| `mention_type` | `name` (les périphrases et les pronoms, `nominal` et `pronoun`, viendront avec les paliers 2 et 3) |
| `entity_group_candidates` | seulement sur un nom partiel ambigu : les groupes auxquels il peut appartenir |

Les autres propriétés de l'annotation sont gardées, et traiter deux fois un document
donne le même résultat. La portée est le document entier, ce qui convient à une
dépêche ; les documents longs sont une question ouverte (SHERPA-3048).

## Requirements

- Python 3.12+
- `pymultirole_plugins` (>=1.6.0,<1.7.0)

## Installation

```
pip install pyprocessors-coreference
```

## Usage

```python
from pymultirole_plugins.v1.schema import Annotation, Document

from pyprocessors_coreference.coreference import CoreferenceParameters, CoreferenceProcessor

text = "Emmanuel Macron est arrivé. M. Macron a parlé."
document = Document(
    text=text,
    annotations=[
        Annotation(start=0, end=15, labelName="person", text="Emmanuel Macron"),
        Annotation(start=28, end=37, labelName="person", text="M. Macron"),
    ],
)
[document] = CoreferenceProcessor().process([document], CoreferenceParameters())
# Both annotations: entity_group "g1", canonical_form "Emmanuel Macron".
```

### Parameters

| Paramètre | Défaut | Description |
|-----------|--------|-------------|
| `labels` | vide | Labels des mentions à regrouper. Vide : tous les labels. |
| `person_labels` | vide | Labels de personne : ils forment une seule famille, et la règle du nom partiel s'y applique. Vide : tout label dont le nom contient « person » (`person`, `afpperson`…). |
| `honorifics` | `M.`, `Mme`, `Dr`, `Mr`… | Civilités ignorées en tête d'un nom de personne. Paramètre avancé. |

## Development

Le build est piloté par [Task](https://taskfile.dev) et [uv](https://docs.astral.sh/uv/),
les stages venant du submodule `python-archetype` :

```
git submodule update --init
task stages          # les stages du pipeline, dans l'ordre
task                 # tout sauf le dernier stage — donc tout sauf la publication
task -- --skip-tests # idem, sans le stage de test
task up-to -- py:lint
task jenkins         # tout, exactement ce que joue Jenkins
task --list
```

`uv.lock` n'est pas versionné ici, donc `py:sync` résout les dépendances à neuf.
