pyprocessors-coreference 1.6.8__tar.gz → 1.6.12__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.
Files changed (34) hide show
  1. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/PKG-INFO +1 -1
  2. pyprocessors_coreference-1.6.12/docs/sherpa/coreference.en.md +224 -0
  3. pyprocessors_coreference-1.6.12/docs/sherpa/coreference.fr.md +224 -0
  4. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/src/pyprocessors_coreference/__init__.py +1 -1
  5. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/.claude-plugin/plugin.json +1 -1
  6. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/resources/help/help_examples.py +82 -4
  7. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/resources/help/plugin-help.py +340 -63
  8. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/plugin-help/SKILL.md +25 -6
  9. pyprocessors_coreference-1.6.8/docs/sherpa/coreference.en.md +0 -55
  10. pyprocessors_coreference-1.6.8/docs/sherpa/coreference.fr.md +0 -55
  11. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/.claude/settings.json +0 -0
  12. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/.gitignore +0 -0
  13. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/.gitmodules +0 -0
  14. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/.python-version +0 -0
  15. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/README.md +0 -0
  16. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/docs/adr/0001-linking-candidats-entityfishing-decision-jev.md +0 -0
  17. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/pyproject.toml +0 -0
  18. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/src/pyprocessors_coreference/coreference.py +0 -0
  19. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/.claude-plugin/marketplace.json +0 -0
  20. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/HOWTO.md +0 -0
  21. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/README.md +0 -0
  22. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/resources/Stages.yml +0 -0
  23. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/resources/help/template.en.md +0 -0
  24. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/resources/help/template.fr.md +0 -0
  25. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/SKILL.md +0 -0
  26. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/annotator.md +0 -0
  27. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/converter.md +0 -0
  28. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/formatter.md +0 -0
  29. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/importer.md +0 -0
  30. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/processor.md +0 -0
  31. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/segmenter.md +0 -0
  32. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/skills/new-plugin/references/types.md +0 -0
  33. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/tools/check-marketplace.py +0 -0
  34. {pyprocessors_coreference-1.6.8 → pyprocessors_coreference-1.6.12}/submodules/python-archetype/vars/pythonPipeline.groovy +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyprocessors-coreference
3
- Version: 1.6.8
3
+ Version: 1.6.12
4
4
  Summary: Processor grouping the mentions of the same entity
5
5
  Project-URL: Homepage, https://bitbucket.org/kairntech/pyprocessors_coreference
6
6
  Author-email: Olivier Terrier <olivier.terrier@kairntech.com>
@@ -0,0 +1,224 @@
1
+ # Coreference Processor (`coreference`)
2
+
3
+ Groups the mentions of the same entity in a document: "Emmanuel Macron", "M. Macron" and "MACRON" get the same group identifier and the same canonical form, "Emmanuel Macron".
4
+
5
+ ## When to use it
6
+
7
+ - Before linking to a knowledge base (Entity-Fishing, a lexicon): linking then looks for the full form of the name, easier to identify, and applies the same result to all the mentions of the group.
8
+ - To count or follow the entities of a newswire corpus, where a full name is often taken up by the last name alone, with or without title.
9
+
10
+ ## When not to use it
11
+
12
+ - The processor only groups **names**. It does not link a pronoun ("il", "elle") or a description ("le président") to the name they designate.
13
+ - It finds no entity: it works on the annotations made by an annotator placed before it (a named-entity model, a lexicon).
14
+ - It does not group mentions across documents: groups are specific to each document.
15
+
16
+ ## Prerequisites
17
+
18
+ | Prerequisite | Required | Why |
19
+ |---|---|---|
20
+ | Entity annotations | **Yes** | The processor only groups existing annotations. A document without annotation is returned as is. |
21
+
22
+ <!-- generated:languages -->
23
+
24
+ **Supported languages:** the plugin declares no language restriction.
25
+
26
+ <!-- /generated:languages -->
27
+
28
+ Name comparison does not depend on the language, but the default **Honorifics** list only contains French and English titles.
29
+
30
+ ## What it produces
31
+
32
+ The processor neither creates nor removes annotations. It adds **properties** to each annotation it groups:
33
+
34
+ | Property | Content | Example |
35
+ |---|---|---|
36
+ | `entity_group` | the group identifier, the same for all the mentions of an entity in the document, numbered in order of appearance | `g1` |
37
+ | `canonical_form` | the longest form of the group, without title: the most words, then the most characters | `Emmanuel Macron` |
38
+ | `mention_type` | the type of mention, always `name` | `name` |
39
+ | `entity_group_candidates` | only for an ambiguous partial name: the groups it could belong to | `["g1", "g2"]` |
40
+
41
+ Properties the annotation already carried are kept. Processing the same document twice gives the same result.
42
+
43
+ ## Parameters
44
+
45
+ <!-- generated:parameters -->
46
+
47
+ | Parameter | Default | Effect |
48
+ |---|---|---|
49
+ | **Labels** (`labels`) | — (all labels) | Labels of the mentions to group. Annotations of other labels get no property. Empty, all annotations are grouped. |
50
+ | **Person Labels** (`person_labels`) | — (labels containing "person") | Labels of person names. They form a single family, and only these names benefit from the partial-name rule ("Macron" joins "Emmanuel Macron"). Empty, these are the labels whose name contains "person": `person`, `afpperson`… A `PER` label must therefore be declared here. |
51
+ | **Honorifics** (`honorifics`) *(advanced)* | `["M.", "MM.", "Mme", "Mmes", "Mlle", "Mlles", "Me", "Dr", "Dr.", "Pr", "Pr.", "Mr", "Mr.", "Mrs", …` | Titles ignored in front of a person name, regardless of case. They are not part of the canonical form either. |
52
+
53
+ <!-- /generated:parameters -->
54
+
55
+ ## How it works
56
+
57
+ Two mentions go in the same group in the following cases:
58
+
59
+ 1. **Same form and same label.** The comparison ignores case and apostrophe variants: "Airbus" and "AIRBUS" are grouped.
60
+ 2. **Same family of persons.** All person labels count as one: a "George Lucas" annotated `person` by a model and `afpperson` by a lexicon forms a single group.
61
+ 3. **Partial name, for persons only.** A name that ends a longer name joins this name, whether it appears before or after it in the document: "Macron" and "M. Macron" join "Emmanuel Macron". The **Honorifics** titles are ignored for the comparison.
62
+
63
+ Are **never** grouped:
64
+
65
+ - **a first name alone**: "Emmanuel" does not join "Emmanuel Macron", as it does not end the name;
66
+ - **an ambiguous partial name**: with "Emmanuel Macron" and "Brigitte Macron" in the same document, "Macron" forms its own group. Its `entity_group_candidates` property lists the two possible groups, and the linking step decides;
67
+ - **different labels**, persons aside: the person "George Washington" and the place "Washington" stay separate;
68
+ - **a partial name of an organization or a place**: "Airbus" does not join "Airbus Defence and Space".
69
+
70
+ ## Examples
71
+
72
+ In each example, the output table shows the properties added to each annotation.
73
+
74
+ ### 1. A partial name joins the full name
75
+
76
+ "M. Macron" joins "Emmanuel Macron": same group `g1`, same canonical form. The first name alone, "Emmanuel", forms its own group:
77
+
78
+ <!-- generated:example:partial-name -->
79
+
80
+ Text (French):
81
+
82
+ > Emmanuel Macron parle. M. Macron sourit. Emmanuel repart.
83
+
84
+ Input annotations:
85
+
86
+ | Annotated text | Label | Score |
87
+ |---|---|---|
88
+ | Emmanuel Macron | `person` | 1.00 |
89
+ | M. Macron | `person` | 1.00 |
90
+ | Emmanuel | `person` | 1.00 |
91
+
92
+ Default parameters.
93
+
94
+ Output annotations:
95
+
96
+ | Annotated text | Label | Score | Properties |
97
+ |---|---|---|---|
98
+ | Emmanuel Macron | `person` | 1.00 | `canonical_form`: Emmanuel Macron<br>`entity_group`: g1<br>`mention_type`: name |
99
+ | M. Macron | `person` | 1.00 | `canonical_form`: Emmanuel Macron<br>`entity_group`: g1<br>`mention_type`: name |
100
+ | Emmanuel | `person` | 1.00 | `canonical_form`: Emmanuel<br>`entity_group`: g2<br>`mention_type`: name |
101
+
102
+ <!-- /generated:example:partial-name -->
103
+
104
+ ### 2. An ambiguous partial name
105
+
106
+ Two people bear the name "Macron": the mention "Macron" forms its own group, and `entity_group_candidates` lists the two possible groups:
107
+
108
+ <!-- generated:example:ambiguous -->
109
+
110
+ Text (French):
111
+
112
+ > Emmanuel Macron et Brigitte Macron voyagent. Macron sourit.
113
+
114
+ Input annotations:
115
+
116
+ | Annotated text | Label | Score |
117
+ |---|---|---|
118
+ | Emmanuel Macron | `person` | 1.00 |
119
+ | Brigitte Macron | `person` | 1.00 |
120
+ | Macron | `person` | 1.00 |
121
+
122
+ Default parameters.
123
+
124
+ Output annotations:
125
+
126
+ | Annotated text | Label | Score | Properties |
127
+ |---|---|---|---|
128
+ | Emmanuel Macron | `person` | 1.00 | `canonical_form`: Emmanuel Macron<br>`entity_group`: g1<br>`mention_type`: name |
129
+ | Brigitte Macron | `person` | 1.00 | `canonical_form`: Brigitte Macron<br>`entity_group`: g2<br>`mention_type`: name |
130
+ | Macron | `person` | 1.00 | `canonical_form`: Macron<br>`entity_group`: g3<br>`entity_group_candidates`: ["g1", "g2"]<br>`mention_type`: name |
131
+
132
+ <!-- /generated:example:ambiguous -->
133
+
134
+ ### 3. Organizations and places
135
+
136
+ Persons aside, there is no partial name: "AIRBUS" does not join "Airbus Defence and Space". And different labels stay separate: the person "George Washington" and the place "Washington" each have their own group:
137
+
138
+ <!-- generated:example:other-labels -->
139
+
140
+ Text (French):
141
+
142
+ > Airbus Defence and Space recrute. AIRBUS aussi. George Washington est né loin de Washington.
143
+
144
+ Input annotations:
145
+
146
+ | Annotated text | Label | Score |
147
+ |---|---|---|
148
+ | Airbus Defence and Space | `organisation` | 1.00 |
149
+ | AIRBUS | `organisation` | 1.00 |
150
+ | George Washington | `person` | 1.00 |
151
+ | Washington | `lieu` | 1.00 |
152
+
153
+ Default parameters.
154
+
155
+ Output annotations:
156
+
157
+ | Annotated text | Label | Score | Properties |
158
+ |---|---|---|---|
159
+ | Airbus Defence and Space | `organisation` | 1.00 | `canonical_form`: Airbus Defence and Space<br>`entity_group`: g1<br>`mention_type`: name |
160
+ | AIRBUS | `organisation` | 1.00 | `canonical_form`: AIRBUS<br>`entity_group`: g2<br>`mention_type`: name |
161
+ | George Washington | `person` | 1.00 | `canonical_form`: George Washington<br>`entity_group`: g3<br>`mention_type`: name |
162
+ | Washington | `lieu` | 1.00 | `canonical_form`: Washington<br>`entity_group`: g4<br>`mention_type`: name |
163
+
164
+ <!-- /generated:example:other-labels -->
165
+
166
+ ### 4. Grouping only some labels
167
+
168
+ With **Labels** = `person`, the place "Washington" gets no property:
169
+
170
+ <!-- generated:example:labels -->
171
+
172
+ Text (French):
173
+
174
+ > Emmanuel Macron à Washington.
175
+
176
+ Input annotations:
177
+
178
+ | Annotated text | Label | Score |
179
+ |---|---|---|
180
+ | Emmanuel Macron | `person` | 1.00 |
181
+ | Washington | `lieu` | 1.00 |
182
+
183
+ Parameters: **Labels** = `["person"]`.
184
+
185
+ Output annotations:
186
+
187
+ | Annotated text | Label | Score | Properties |
188
+ |---|---|---|---|
189
+ | Emmanuel Macron | `person` | 1.00 | `canonical_form`: Emmanuel Macron<br>`entity_group`: g1<br>`mention_type`: name |
190
+ | Washington | `lieu` | 1.00 | — |
191
+
192
+ <!-- /generated:example:labels -->
193
+
194
+ ## Known limitations
195
+
196
+ - **A `PER` label is not recognized as a person by default.** Only the labels whose name contains "person" are. With `PER` annotations, "Macron" does not join "Emmanuel Macron" as long as `PER` is not declared in **Person Labels**. Once declared, it groups them:
197
+
198
+ <!-- generated:example:person-labels -->
199
+
200
+ Text (French):
201
+
202
+ > Emmanuel Macron parle. Macron sourit.
203
+
204
+ Input annotations:
205
+
206
+ | Annotated text | Label | Score |
207
+ |---|---|---|
208
+ | Emmanuel Macron | `PER` | 1.00 |
209
+ | Macron | `PER` | 1.00 |
210
+
211
+ Parameters: **Person Labels** = `["PER"]`.
212
+
213
+ Output annotations:
214
+
215
+ | Annotated text | Label | Score | Properties |
216
+ |---|---|---|---|
217
+ | Emmanuel Macron | `PER` | 1.00 | `canonical_form`: Emmanuel Macron<br>`entity_group`: g1<br>`mention_type`: name |
218
+ | Macron | `PER` | 1.00 | `canonical_form`: Emmanuel Macron<br>`entity_group`: g1<br>`mention_type`: name |
219
+
220
+ <!-- /generated:example:person-labels -->
221
+
222
+ - **The partial-name rule only looks at the end of the name.** Two different people sharing a last name are grouped if only one appears in full: in a document that only cites "Brigitte Macron", a "Macron" designating her husband joins her.
223
+ - **A title missing from the list is part of the name.** "Général Martin" is not reduced to "Martin"; it is grouped with "Martin" by the partial-name rule, but the canonical form of the group then becomes "Général Martin".
224
+ - **Mentions of labels excluded by Labels receive nothing**, not even a group of their own.
@@ -0,0 +1,224 @@
1
+ # Processeur Coréférence (`coreference`)
2
+
3
+ Regroupe les mentions d'une même entité dans un document : « Emmanuel Macron », « M. Macron » et « MACRON » reçoivent le même identifiant de groupe et la même forme canonique, « Emmanuel Macron ».
4
+
5
+ ## Quand l'utiliser
6
+
7
+ - Avant la liaison à une base de connaissances (Entity-Fishing, un lexique) : la liaison cherche alors la forme complète du nom, plus facile à identifier, et applique le même résultat à toutes les mentions du groupe.
8
+ - Pour compter ou suivre les entités d'un corpus de dépêches, où un nom complet est souvent repris par le seul nom de famille, avec ou sans civilité.
9
+
10
+ ## Quand ne pas l'utiliser
11
+
12
+ - Le processeur ne regroupe que des **noms**. Il ne relie pas un pronom (« il », « elle ») ni une description (« le président ») au nom qu'ils désignent.
13
+ - Il ne trouve aucune entité : il travaille sur les annotations posées par un annotateur placé avant lui (un modèle d'entités nommées, un lexique).
14
+ - Il ne regroupe pas les mentions entre documents : les groupes sont propres à chaque document.
15
+
16
+ ## Prérequis
17
+
18
+ | Prérequis | Obligatoire | Pourquoi |
19
+ |---|---|---|
20
+ | Annotations d'entités | **Oui** | Le processeur ne regroupe que des annotations existantes. Un document sans annotation est rendu tel quel. |
21
+
22
+ <!-- generated:languages -->
23
+
24
+ **Langues prises en charge :** le plugin ne déclare aucune restriction de langue.
25
+
26
+ <!-- /generated:languages -->
27
+
28
+ La comparaison des noms ne dépend pas de la langue, mais la liste par défaut de **Honorifics** ne contient que des civilités françaises et anglaises.
29
+
30
+ ## Ce qu'il produit
31
+
32
+ Le processeur ne crée ni ne supprime d'annotation. Il ajoute des **propriétés** à chaque annotation qu'il regroupe :
33
+
34
+ | Propriété | Contenu | Exemple |
35
+ |---|---|---|
36
+ | `entity_group` | l'identifiant du groupe, le même pour toutes les mentions d'une entité dans le document, numéroté dans l'ordre d'apparition | `g1` |
37
+ | `canonical_form` | la forme la plus longue du groupe, sans civilité : le plus de mots, puis le plus de caractères | `Emmanuel Macron` |
38
+ | `mention_type` | le type de mention, toujours `name` | `name` |
39
+ | `entity_group_candidates` | seulement pour un nom partiel ambigu : les groupes auxquels il pourrait appartenir | `["g1", "g2"]` |
40
+
41
+ Les propriétés que l'annotation portait déjà sont conservées. Traiter deux fois le même document donne le même résultat.
42
+
43
+ ## Paramètres
44
+
45
+ <!-- generated:parameters -->
46
+
47
+ | Paramètre | Valeur par défaut | Effet |
48
+ |---|---|---|
49
+ | **Labels** (`labels`) | — (tous les labels) | Labels des mentions à regrouper. Les annotations des autres labels ne reçoivent aucune propriété. Vide, toutes les annotations sont regroupées. |
50
+ | **Person Labels** (`person_labels`) | — (les labels contenant « person ») | Labels des noms de personnes. Ils forment une seule famille, et seuls ces noms bénéficient de la règle du nom partiel (« Macron » rejoint « Emmanuel Macron »). Vide, ce sont les labels dont le nom contient « person » : `person`, `afpperson`… Un label `PER` doit donc être déclaré ici. |
51
+ | **Honorifics** (`honorifics`) *(avancé)* | `["M.", "MM.", "Mme", "Mmes", "Mlle", "Mlles", "Me", "Dr", "Dr.", "Pr", "Pr.", "Mr", "Mr.", "Mrs", …` | Civilités ignorées devant un nom de personne, sans tenir compte des majuscules. Elles ne figurent pas non plus dans la forme canonique. |
52
+
53
+ <!-- /generated:parameters -->
54
+
55
+ ## Fonctionnement
56
+
57
+ Deux mentions vont dans le même groupe dans les cas suivants :
58
+
59
+ 1. **Même forme et même label.** La comparaison ignore les majuscules et les variantes d'apostrophe : « Airbus » et « AIRBUS » sont regroupés.
60
+ 2. **Même famille de personnes.** Tous les labels de personnes comptent comme un seul : un « George Lucas » annoté `person` par un modèle et `afpperson` par un lexique forme un seul groupe.
61
+ 3. **Nom partiel, pour les personnes seulement.** Un nom qui termine un nom plus long rejoint ce nom, qu'il apparaisse avant ou après lui dans le document : « Macron » et « M. Macron » rejoignent « Emmanuel Macron ». Les civilités de **Honorifics** sont ignorées pour comparer.
62
+
63
+ Ne sont **jamais** regroupés :
64
+
65
+ - **un prénom seul** : « Emmanuel » ne rejoint pas « Emmanuel Macron », car il ne termine pas le nom ;
66
+ - **un nom partiel ambigu** : avec « Emmanuel Macron » et « Brigitte Macron » dans le même document, « Macron » forme son propre groupe. Sa propriété `entity_group_candidates` liste les deux groupes possibles, et c'est l'étape de liaison qui tranche ;
67
+ - **des labels différents**, hors personnes : la personne « George Washington » et le lieu « Washington » restent séparés ;
68
+ - **un nom partiel d'organisation ou de lieu** : « Airbus » ne rejoint pas « Airbus Defence and Space ».
69
+
70
+ ## Exemples
71
+
72
+ Dans chaque exemple, le tableau de sortie montre les propriétés ajoutées à chaque annotation.
73
+
74
+ ### 1. Un nom partiel rejoint le nom complet
75
+
76
+ « M. Macron » rejoint « Emmanuel Macron » : même groupe `g1`, même forme canonique. Le prénom seul, « Emmanuel », forme son propre groupe :
77
+
78
+ <!-- generated:example:partial-name -->
79
+
80
+ Texte (français) :
81
+
82
+ > Emmanuel Macron parle. M. Macron sourit. Emmanuel repart.
83
+
84
+ Annotations en entrée :
85
+
86
+ | Texte annoté | Label | Score |
87
+ |---|---|---|
88
+ | Emmanuel Macron | `person` | 1,00 |
89
+ | M. Macron | `person` | 1,00 |
90
+ | Emmanuel | `person` | 1,00 |
91
+
92
+ Paramètres par défaut.
93
+
94
+ Annotations en sortie :
95
+
96
+ | Texte annoté | Label | Score | Propriétés |
97
+ |---|---|---|---|
98
+ | Emmanuel Macron | `person` | 1,00 | `canonical_form` : Emmanuel Macron<br>`entity_group` : g1<br>`mention_type` : name |
99
+ | M. Macron | `person` | 1,00 | `canonical_form` : Emmanuel Macron<br>`entity_group` : g1<br>`mention_type` : name |
100
+ | Emmanuel | `person` | 1,00 | `canonical_form` : Emmanuel<br>`entity_group` : g2<br>`mention_type` : name |
101
+
102
+ <!-- /generated:example:partial-name -->
103
+
104
+ ### 2. Un nom partiel ambigu
105
+
106
+ Deux personnes portent le nom « Macron » : la mention « Macron » forme son propre groupe, et `entity_group_candidates` liste les deux groupes possibles :
107
+
108
+ <!-- generated:example:ambiguous -->
109
+
110
+ Texte (français) :
111
+
112
+ > Emmanuel Macron et Brigitte Macron voyagent. Macron sourit.
113
+
114
+ Annotations en entrée :
115
+
116
+ | Texte annoté | Label | Score |
117
+ |---|---|---|
118
+ | Emmanuel Macron | `person` | 1,00 |
119
+ | Brigitte Macron | `person` | 1,00 |
120
+ | Macron | `person` | 1,00 |
121
+
122
+ Paramètres par défaut.
123
+
124
+ Annotations en sortie :
125
+
126
+ | Texte annoté | Label | Score | Propriétés |
127
+ |---|---|---|---|
128
+ | Emmanuel Macron | `person` | 1,00 | `canonical_form` : Emmanuel Macron<br>`entity_group` : g1<br>`mention_type` : name |
129
+ | Brigitte Macron | `person` | 1,00 | `canonical_form` : Brigitte Macron<br>`entity_group` : g2<br>`mention_type` : name |
130
+ | Macron | `person` | 1,00 | `canonical_form` : Macron<br>`entity_group` : g3<br>`entity_group_candidates` : ["g1", "g2"]<br>`mention_type` : name |
131
+
132
+ <!-- /generated:example:ambiguous -->
133
+
134
+ ### 3. Organisations et lieux
135
+
136
+ Hors personnes, il n'y a pas de nom partiel : « AIRBUS » ne rejoint pas « Airbus Defence and Space ». Et des labels différents restent séparés : la personne « George Washington » et le lieu « Washington » ont chacun leur groupe :
137
+
138
+ <!-- generated:example:other-labels -->
139
+
140
+ Texte (français) :
141
+
142
+ > Airbus Defence and Space recrute. AIRBUS aussi. George Washington est né loin de Washington.
143
+
144
+ Annotations en entrée :
145
+
146
+ | Texte annoté | Label | Score |
147
+ |---|---|---|
148
+ | Airbus Defence and Space | `organisation` | 1,00 |
149
+ | AIRBUS | `organisation` | 1,00 |
150
+ | George Washington | `person` | 1,00 |
151
+ | Washington | `lieu` | 1,00 |
152
+
153
+ Paramètres par défaut.
154
+
155
+ Annotations en sortie :
156
+
157
+ | Texte annoté | Label | Score | Propriétés |
158
+ |---|---|---|---|
159
+ | Airbus Defence and Space | `organisation` | 1,00 | `canonical_form` : Airbus Defence and Space<br>`entity_group` : g1<br>`mention_type` : name |
160
+ | AIRBUS | `organisation` | 1,00 | `canonical_form` : AIRBUS<br>`entity_group` : g2<br>`mention_type` : name |
161
+ | George Washington | `person` | 1,00 | `canonical_form` : George Washington<br>`entity_group` : g3<br>`mention_type` : name |
162
+ | Washington | `lieu` | 1,00 | `canonical_form` : Washington<br>`entity_group` : g4<br>`mention_type` : name |
163
+
164
+ <!-- /generated:example:other-labels -->
165
+
166
+ ### 4. Ne regrouper que certains labels
167
+
168
+ Avec **Labels** = `person`, le lieu « Washington » ne reçoit aucune propriété :
169
+
170
+ <!-- generated:example:labels -->
171
+
172
+ Texte (français) :
173
+
174
+ > Emmanuel Macron à Washington.
175
+
176
+ Annotations en entrée :
177
+
178
+ | Texte annoté | Label | Score |
179
+ |---|---|---|
180
+ | Emmanuel Macron | `person` | 1,00 |
181
+ | Washington | `lieu` | 1,00 |
182
+
183
+ Paramètres : **Labels** = `["person"]`.
184
+
185
+ Annotations en sortie :
186
+
187
+ | Texte annoté | Label | Score | Propriétés |
188
+ |---|---|---|---|
189
+ | Emmanuel Macron | `person` | 1,00 | `canonical_form` : Emmanuel Macron<br>`entity_group` : g1<br>`mention_type` : name |
190
+ | Washington | `lieu` | 1,00 | — |
191
+
192
+ <!-- /generated:example:labels -->
193
+
194
+ ## Limites à connaître
195
+
196
+ - **Un label `PER` n'est pas reconnu comme personne par défaut.** Seuls les labels dont le nom contient « person » le sont. Avec des annotations `PER`, « Macron » ne rejoint pas « Emmanuel Macron » tant que `PER` n'est pas déclaré dans **Person Labels**. Déclaré, il les regroupe :
197
+
198
+ <!-- generated:example:person-labels -->
199
+
200
+ Texte (français) :
201
+
202
+ > Emmanuel Macron parle. Macron sourit.
203
+
204
+ Annotations en entrée :
205
+
206
+ | Texte annoté | Label | Score |
207
+ |---|---|---|
208
+ | Emmanuel Macron | `PER` | 1,00 |
209
+ | Macron | `PER` | 1,00 |
210
+
211
+ Paramètres : **Person Labels** = `["PER"]`.
212
+
213
+ Annotations en sortie :
214
+
215
+ | Texte annoté | Label | Score | Propriétés |
216
+ |---|---|---|---|
217
+ | Emmanuel Macron | `PER` | 1,00 | `canonical_form` : Emmanuel Macron<br>`entity_group` : g1<br>`mention_type` : name |
218
+ | Macron | `PER` | 1,00 | `canonical_form` : Emmanuel Macron<br>`entity_group` : g1<br>`mention_type` : name |
219
+
220
+ <!-- /generated:example:person-labels -->
221
+
222
+ - **La règle du nom partiel ne regarde que la fin du nom.** Deux personnes différentes qui partagent un nom de famille sont regroupées si une seule apparaît en entier : dans un document qui ne cite que « Brigitte Macron », un « Macron » qui désigne son mari la rejoint.
223
+ - **Une civilité absente de la liste fait partie du nom.** « Général Martin » ne se réduit pas à « Martin » ; il est regroupé avec « Martin » par la règle du nom partiel, mais la forme canonique du groupe devient alors « Général Martin ».
224
+ - **Les mentions des labels exclus par Labels ne reçoivent rien**, pas même un groupe à elles seules.
@@ -1,3 +1,3 @@
1
1
  """Processor grouping the mentions of the same entity"""
2
2
 
3
- __version__ = "1.6.8"
3
+ __version__ = "1.6.12"
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "python-archetype",
4
4
  "displayName": "Kairntech python-archetype",
5
- "version": "1.3.2",
5
+ "version": "1.3.6",
6
6
  "description": "Cree et entretient les depots Python de Kairntech qui consomment python-archetype : creation d'un plugin a partir de sa graine, page d'aide Sherpa d'un plugin, et mesure de la derive d'un depot par rapport a elle.",
7
7
  "author": {
8
8
  "name": "Kairntech",
@@ -24,7 +24,9 @@ Le dépôt du plugin rend la fixture visible depuis 'tests/conftest.py' :
24
24
  from help_examples import help_example # noqa: E402, F401
25
25
  """
26
26
 
27
+ import io
27
28
  import json
29
+ import zipfile
28
30
  from pathlib import Path
29
31
 
30
32
  import pytest
@@ -36,14 +38,90 @@ EXAMPLES_DIR = Path("build") / "help-examples"
36
38
  MAX_CONTENT = 100_000
37
39
 
38
40
 
39
- def _content(data: bytes) -> dict:
40
- """Le contenu d'un fichier ou d'une réponse : son texte s'il en est un, sa taille sinon."""
41
- content = {"size": len(data)}
41
+ # Un classeur se montre par ses feuilles : au-delà, la page n'en citerait qu'un extrait.
42
+ MAX_SHEETS = 5
43
+ MAX_SHEET_ROWS = 50
44
+ # Une archive se montre par la liste de ses fichiers, et le texte des premiers.
45
+ MAX_MEMBERS = 20
46
+
47
+
48
+ def _sheets(data: bytes) -> list[dict] | None:
49
+ """Les feuilles d'un classeur Excel, ou None si ce n'en est pas un (ou sans openpyxl)."""
42
50
  try:
43
- text = data.decode("utf-8")
51
+ import openpyxl
52
+
53
+ workbook = openpyxl.load_workbook(io.BytesIO(data), read_only=True, data_only=True)
54
+ except Exception: # noqa: BLE001 -- une archive zip qui n'est pas un classeur, ou pas d'openpyxl
55
+ return None
56
+ sheets = []
57
+ for sheet in workbook.worksheets[:MAX_SHEETS]:
58
+ rows = []
59
+ for row in sheet.iter_rows(values_only=True):
60
+ if len(rows) == MAX_SHEET_ROWS:
61
+ break
62
+ rows.append(["" if value is None else str(value) for value in row])
63
+ # Sans les colonnes vides de droite, qu'openpyxl rend quand une ligne est plus longue.
64
+ width = max((max((i + 1 for i, v in enumerate(row) if v), default=0) for row in rows), default=0)
65
+ sheets.append({"name": sheet.title, "rows": [row[:width] for row in rows]})
66
+ return sheets
67
+
68
+
69
+ def _archive(data: bytes) -> list[dict] | None:
70
+ """Les fichiers d'une archive zip : nom, taille, et texte quand c'en est un."""
71
+ try:
72
+ archive = zipfile.ZipFile(io.BytesIO(data))
73
+ except zipfile.BadZipFile:
74
+ return None
75
+ # Un document Office (docx, pptx) est une archive, mais ses fichiers XML n'apprennent rien au lecteur.
76
+ if "[Content_Types].xml" in archive.namelist():
77
+ return None
78
+ members = []
79
+ for info in archive.infolist()[:MAX_MEMBERS]:
80
+ if info.is_dir():
81
+ continue
82
+ member = {"name": info.filename, "size": info.file_size}
83
+ decoded = _text(archive.read(info))
84
+ if decoded is not None:
85
+ member["text"] = decoded[0][:MAX_CONTENT]
86
+ members.append(member)
87
+ return members
88
+
89
+
90
+ def _text(data: bytes) -> tuple[str, str] | None:
91
+ """Le texte et son encodage : UTF-8, sinon Latin-1 (cp1252) pour un texte sans octet de contrôle."""
92
+ try:
93
+ return data.decode("utf-8"), "utf-8"
44
94
  except UnicodeDecodeError:
95
+ pass
96
+ # Latin-1 décode n'importe quels octets : un octet de contrôle (hors tabulation et fins
97
+ # de ligne) trahit un contenu binaire.
98
+ if any(byte < 0x20 and byte not in b"\t\n\r\f" for byte in data):
99
+ return None
100
+ try:
101
+ return data.decode("cp1252"), "cp1252"
102
+ except UnicodeDecodeError:
103
+ return None
104
+
105
+
106
+ def _content(data: bytes) -> dict:
107
+ """Le contenu d'un fichier ou d'une réponse : son texte, les feuilles d'un classeur, les fichiers d'une archive, ou sa taille."""
108
+ content = {"size": len(data)}
109
+ if data.startswith(b"PK"):
110
+ sheets = _sheets(data)
111
+ if sheets is not None:
112
+ content["sheets"] = sheets
113
+ return content
114
+ members = _archive(data)
115
+ if members is not None:
116
+ content["archive"] = members
117
+ return content
118
+ decoded = _text(data)
119
+ if decoded is None:
45
120
  return content
121
+ text, encoding = decoded
46
122
  content["text"] = text[:MAX_CONTENT]
123
+ if encoding != "utf-8":
124
+ content["encoding"] = encoding
47
125
  if len(text) > MAX_CONTENT:
48
126
  content["truncated"] = True
49
127
  return content