sinonym 0.2.7__tar.gz → 0.2.8__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.
- {sinonym-0.2.7 → sinonym-0.2.8}/.gitignore +2 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/PKG-INFO +54 -12
- {sinonym-0.2.7 → sinonym-0.2.8}/README.md +53 -11
- {sinonym-0.2.7 → sinonym-0.2.8}/pyproject.toml +6 -2
- {sinonym-0.2.7 → sinonym-0.2.8}/scripts/README.md +14 -1
- sinonym-0.2.8/scripts/check_test_status.py +511 -0
- sinonym-0.2.8/scripts/name_order_routing_rules.py +163 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/chinese_names_data.py +7 -2
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/coretypes/__init__.py +2 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/coretypes/results.py +58 -5
- sinonym-0.2.8/sinonym/data/name_order_routing/README.md +29 -0
- sinonym-0.2.8/sinonym/data/name_order_routing/pp_abstain_labels.jsonl +750 -0
- sinonym-0.2.8/sinonym/data/name_order_routing/pp_vys_abstain_labels.jsonl +1000 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/detector.py +489 -87
- sinonym-0.2.8/sinonym/pipeline/__init__.py +1 -0
- sinonym-0.2.8/sinonym/pipeline/name_order_routing.py +1334 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/__init__.py +6 -1
- sinonym-0.2.8/sinonym/services/batch_analysis.py +1453 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/ethnicity.py +116 -26
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/formatting.py +14 -7
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/initialization.py +7 -10
- sinonym-0.2.8/sinonym/services/non_person.py +134 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/normalization.py +123 -5
- sinonym-0.2.8/sinonym/services/order_metadata.py +116 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/parsing.py +136 -34
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/process_pool.py +28 -11
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/text_processing/text_normalizer.py +26 -3
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/text_processing/text_preprocessor.py +60 -28
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/timo/integration_test.py +8 -9
- sinonym-0.2.8/sinonym/timo/interface.py +347 -0
- sinonym-0.2.8/tests/_case_assertions.py +46 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_acl.py +23 -82
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_all_chinese_inputs.py +7 -45
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_basic_chinese_names.py +5 -37
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_batch.py +289 -60
- sinonym-0.2.8/tests/test_check_test_status.py +237 -0
- sinonym-0.2.8/tests/test_compound_names.py +40 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_compound_surname_formats.py +6 -49
- sinonym-0.2.8/tests/test_middle_names.py +298 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_misc.py +4 -80
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_mixed_production_cases.py +5 -45
- sinonym-0.2.8/tests/test_mixed_scripts.py +43 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_ml_japanese_detection.py +13 -62
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_multiprocess_pool.py +28 -4
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_name_formatting.py +5 -36
- sinonym-0.2.8/tests/test_name_order_routing_label_fixtures.py +74 -0
- sinonym-0.2.8/tests/test_name_order_routing_rules.py +879 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_non_chinese_rejection.py +7 -30
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_regional_variants.py +5 -36
- sinonym-0.2.8/tests/test_regression_proposals.py +1215 -0
- sinonym-0.2.8/tests/test_timo_interface.py +223 -0
- sinonym-0.2.7/sinonym/services/batch_analysis.py +0 -755
- sinonym-0.2.7/sinonym/timo/interface.py +0 -244
- sinonym-0.2.7/tests/_fail_log.py +0 -24
- sinonym-0.2.7/tests/test_compound_names.py +0 -71
- sinonym-0.2.7/tests/test_middle_names.py +0 -378
- sinonym-0.2.7/tests/test_mixed_scripts.py +0 -74
- sinonym-0.2.7/tests/test_regression_proposals.py +0 -561
- sinonym-0.2.7/tests/test_timo_interface.py +0 -87
- {sinonym-0.2.7 → sinonym-0.2.8}/LICENSE +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/__init__.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/coretypes/config.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/README.md +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/acl_2025_authors.txt +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/chinese_japanese_classifier.joblib +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/chinese_japanese_classifier.skops +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/familyname_orcid.csv +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/givenname_orcid.csv +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/data/model_features.json +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/ml_model_components.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/patterns/__init__.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/patterns/compiled_patterns.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/patterns/regex_builders.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/resources.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/services/cache.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/text_processing/__init__.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/text_processing/compound_detector.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/timo/__init__.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/timo/config.yaml +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/utils/__init__.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/utils/string_manipulation.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/sinonym/utils/thread_cache.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/conftest.py +0 -0
- {sinonym-0.2.7 → sinonym-0.2.8}/tests/test_performance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sinonym
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Chinese Name Detection and Normalization Module
|
|
5
5
|
Project-URL: Homepage, https://github.com/allenai/sinonym
|
|
6
6
|
Project-URL: Repository, https://github.com/allenai/sinonym
|
|
@@ -246,7 +246,11 @@ author_list = ["Zhang Wei", "Li Ming", "Wang Xiaoli", "Liu Jiaming", "Feng Cha"]
|
|
|
246
246
|
batch_result = detector.analyze_name_batch(author_list)
|
|
247
247
|
print(f"Format detected: {batch_result.format_pattern.dominant_format}")
|
|
248
248
|
print(f"Confidence: {batch_result.format_pattern.confidence:.1%}")
|
|
249
|
-
|
|
249
|
+
print(f"Decision confidence: {batch_result.format_pattern.decision_confidence:.1%}")
|
|
250
|
+
# Expected Output:
|
|
251
|
+
# Format detected: NameFormat.SURNAME_FIRST
|
|
252
|
+
# Confidence: 80.0%
|
|
253
|
+
# Decision confidence: 80.0%
|
|
250
254
|
|
|
251
255
|
for i, result in enumerate(batch_result.results):
|
|
252
256
|
if result.success:
|
|
@@ -258,7 +262,7 @@ unknown_format_list = ["Wei Zhang", "Ming Li", "Xiaoli Wang"]
|
|
|
258
262
|
pattern = detector.detect_batch_format(unknown_format_list)
|
|
259
263
|
if pattern.threshold_met:
|
|
260
264
|
print(f"Consistent {pattern.dominant_format} formatting detected")
|
|
261
|
-
print(f"Safe to process as batch with {pattern.
|
|
265
|
+
print(f"Safe to process as batch with {pattern.decision_confidence:.1%} decision confidence")
|
|
262
266
|
else:
|
|
263
267
|
print("Mixed formatting detected - process individually")
|
|
264
268
|
|
|
@@ -282,7 +286,9 @@ When you call `normalize_name`, you get a `ParseResult` with helpful structured
|
|
|
282
286
|
- `surname_tokens`, `given_tokens`: normalized, capitalized tokens used to form components
|
|
283
287
|
- `middle_tokens`: trailing single-letter initials extracted from given name, if present
|
|
284
288
|
- `order`: component order descriptor, typically `["given", "middle", "surname"]`
|
|
285
|
-
- `parsed_original_order`: A `ParsedName`
|
|
289
|
+
- `parsed_original_order`: A `ParsedName` with the same semantic `surname` and
|
|
290
|
+
`given_name` labels as `parsed`, plus an `order` list that records how those
|
|
291
|
+
components appeared in the input.
|
|
286
292
|
|
|
287
293
|
Notes:
|
|
288
294
|
- The tokens in `parsed` and `parsed_original_order` are the same normalized tokens; only the conceptual ordering differs via the `order` list.
|
|
@@ -295,8 +301,8 @@ res = detector.normalize_name("Li Wei")
|
|
|
295
301
|
# res.result == "Wei Li"
|
|
296
302
|
# res.parsed.order == ["given", "middle", "surname"]
|
|
297
303
|
# res.parsed_original_order.order == ["surname", "given"]
|
|
298
|
-
# res.parsed_original_order.given_name == "
|
|
299
|
-
# res.parsed_original_order.surname == "
|
|
304
|
+
# res.parsed_original_order.given_name == "Wei"
|
|
305
|
+
# res.parsed_original_order.surname == "Li"
|
|
300
306
|
|
|
301
307
|
res = detector.normalize_name("Chi-Ying F. Huang")
|
|
302
308
|
# res.result == "Chi-Ying F Huang"
|
|
@@ -318,7 +324,7 @@ When processing multiple names together, Sinonym:
|
|
|
318
324
|
|
|
319
325
|
1. **Detects Format Patterns**: Analyzes the entire batch to identify whether names follow a surname-first (e.g., "Zhang Wei") or given-first (e.g., "Wei Zhang") pattern
|
|
320
326
|
2. **Aggregates Evidence**: Uses frequency statistics across all names to build confidence in the detected pattern
|
|
321
|
-
3. **Applies Consistent Formatting**: When
|
|
327
|
+
3. **Applies Consistent Formatting**: When `decision_confidence` exceeds the configured threshold, applies the detected pattern to improve parsing of ambiguous individual names
|
|
322
328
|
4. **Tracks Improvements**: Identifies which names benefit from batch context vs. individual processing
|
|
323
329
|
|
|
324
330
|
### Key Benefits
|
|
@@ -341,7 +347,10 @@ result = detector.analyze_name_batch([
|
|
|
341
347
|
])
|
|
342
348
|
print(f"Format detected: {result.format_pattern.dominant_format}")
|
|
343
349
|
print(f"Confidence: {result.format_pattern.confidence:.1%}")
|
|
350
|
+
print(f"Decision confidence: {result.format_pattern.decision_confidence:.1%}")
|
|
351
|
+
print(f"Vote margin: {result.format_pattern.vote_margin:.1%}")
|
|
344
352
|
print(f"Improved names: {len(result.improvements)}")
|
|
353
|
+
print(result.name_order_evidence[0].selected_surname_position)
|
|
345
354
|
|
|
346
355
|
# Quick format detection without full processing
|
|
347
356
|
pattern = detector.detect_batch_format([
|
|
@@ -358,6 +367,37 @@ for result in results:
|
|
|
358
367
|
print(f"Processed: {result.result}")
|
|
359
368
|
```
|
|
360
369
|
|
|
370
|
+
Use `analyze_name_batch()` when you need to choose between two batch contexts
|
|
371
|
+
such as paper-level and source/venue/year-level runs. `process_name_batch()`
|
|
372
|
+
returns only the final `ParseResult` list and intentionally drops the routing
|
|
373
|
+
evidence.
|
|
374
|
+
|
|
375
|
+
`BatchFormatPattern` exposes batch-level convention evidence:
|
|
376
|
+
|
|
377
|
+
- `dominant_format`, `confidence`, and `threshold_met`
|
|
378
|
+
- `decision_confidence`, the score used by the batch-application gate
|
|
379
|
+
- `surname_first_count`, `given_first_count`, and `total_count`
|
|
380
|
+
- `voting_count`, `vote_margin_count`, and `vote_margin`
|
|
381
|
+
|
|
382
|
+
`BatchParseResult.name_order_evidence` is aligned with `names` and `results`.
|
|
383
|
+
Each `NameOrderEvidence` contains stable evidence for external context-routing
|
|
384
|
+
rules:
|
|
385
|
+
|
|
386
|
+
- token shape: `raw_tokens`, `raw_token_count`, `has_all_caps_token`,
|
|
387
|
+
`all_caps_tokens`
|
|
388
|
+
- batch behavior: `script_representation`, `batch_participant`,
|
|
389
|
+
`batch_applied`, `batch_changed_format`
|
|
390
|
+
- order choices: `individual_format`, `selected_format`,
|
|
391
|
+
`selected_surname_position`
|
|
392
|
+
- endpoint frequency evidence: `first_token_surname_frequency`,
|
|
393
|
+
`last_token_surname_frequency`, `selected_surname_frequency`,
|
|
394
|
+
`alternate_endpoint_surname_frequency`,
|
|
395
|
+
`selected_over_alternate_surname_frequency_ratio`
|
|
396
|
+
|
|
397
|
+
Caller-owned metadata such as source, venue, and year is not inferred by
|
|
398
|
+
sinonym. Keep that metadata beside the PP/VYS batch calls and combine it with
|
|
399
|
+
the emitted evidence in the external router.
|
|
400
|
+
|
|
361
401
|
### Persistent Multi-Process Processing
|
|
362
402
|
|
|
363
403
|
For high-throughput workloads, you can keep a persistent process pool alive and
|
|
@@ -377,7 +417,7 @@ def main():
|
|
|
377
417
|
results_a = pool.normalize_names(names_a)
|
|
378
418
|
results_b = pool.normalize_names(names_b)
|
|
379
419
|
|
|
380
|
-
# One-off
|
|
420
|
+
# One-off compatibility wrapper with full batch-context semantics
|
|
381
421
|
single_batch = detector.process_name_batch_multiprocess(names_a, max_workers=6, chunk_size=64)
|
|
382
422
|
return results_a, results_b, single_batch
|
|
383
423
|
|
|
@@ -400,7 +440,9 @@ Batch processing requires a minimum of 2 names and works best with 5+ names for
|
|
|
400
440
|
|
|
401
441
|
**Unambiguous Names**: Some names have only one possible parsing format (e.g., compound given names like "Wei‑Qi Wang"). Batch processing never forces such names into the detected pattern and never raises. These names keep their best individual parse while other Chinese names benefit from the jointly detected order.
|
|
402
442
|
|
|
403
|
-
**
|
|
443
|
+
**Batch Application Threshold**: Batch detection keeps count-based evidence (`confidence`, counts, and vote margin) separate from the application decision (`decision_confidence`). Batch formatting is applied only when the direction is confident, at least two vote-eligible Latin-only Chinese names participate, and `decision_confidence` clears the configured threshold. Latin rows with all-caps source-token cues are exposed in `name_order_evidence` but do not vote in, or receive, Latin batch formatting.
|
|
444
|
+
|
|
445
|
+
**Script Cohorts**: Vote-eligible Latin-only names vote in and receive Latin batch formatting. Han-only, explicitly aligned Han/Roman, and other mixed-script names are parsed from their own script evidence so a Latin batch convention does not flip their order.
|
|
404
446
|
|
|
405
447
|
### Batch Processing with Mixed Name Types
|
|
406
448
|
|
|
@@ -423,7 +465,7 @@ mixed_names = [
|
|
|
423
465
|
|
|
424
466
|
result = detector.analyze_name_batch(mixed_names)
|
|
425
467
|
|
|
426
|
-
# Format detection uses only the 8 Chinese names
|
|
468
|
+
# Format detection uses only the 8 Latin-only Chinese names
|
|
427
469
|
# If 7 prefer GIVEN_FIRST vs 1 SURNAME_FIRST = 87.5% confidence
|
|
428
470
|
# GIVEN_FIRST pattern is applied to Chinese names; non‑Chinese names return clear failures
|
|
429
471
|
|
|
@@ -449,8 +491,8 @@ for i, (name, result_obj) in enumerate(zip(mixed_names, result.results)):
|
|
|
449
491
|
|
|
450
492
|
**Key Benefits:**
|
|
451
493
|
- **Maintains input-output correspondence**: Results array matches input array length and order
|
|
452
|
-
- **Robust format detection**: Only valid Chinese names contribute to pattern detection
|
|
453
|
-
- **Consistent formatting**:
|
|
494
|
+
- **Robust format detection**: Only valid Latin-only Chinese names contribute to Latin batch pattern detection
|
|
495
|
+
- **Consistent formatting**: Latin-only Chinese names get the detected format applied when the batch signal is strong enough
|
|
454
496
|
- **Clear failure reporting**: Non-Chinese names are clearly marked as failed with error messages
|
|
455
497
|
|
|
456
498
|
## Development
|
|
@@ -208,7 +208,11 @@ author_list = ["Zhang Wei", "Li Ming", "Wang Xiaoli", "Liu Jiaming", "Feng Cha"]
|
|
|
208
208
|
batch_result = detector.analyze_name_batch(author_list)
|
|
209
209
|
print(f"Format detected: {batch_result.format_pattern.dominant_format}")
|
|
210
210
|
print(f"Confidence: {batch_result.format_pattern.confidence:.1%}")
|
|
211
|
-
|
|
211
|
+
print(f"Decision confidence: {batch_result.format_pattern.decision_confidence:.1%}")
|
|
212
|
+
# Expected Output:
|
|
213
|
+
# Format detected: NameFormat.SURNAME_FIRST
|
|
214
|
+
# Confidence: 80.0%
|
|
215
|
+
# Decision confidence: 80.0%
|
|
212
216
|
|
|
213
217
|
for i, result in enumerate(batch_result.results):
|
|
214
218
|
if result.success:
|
|
@@ -220,7 +224,7 @@ unknown_format_list = ["Wei Zhang", "Ming Li", "Xiaoli Wang"]
|
|
|
220
224
|
pattern = detector.detect_batch_format(unknown_format_list)
|
|
221
225
|
if pattern.threshold_met:
|
|
222
226
|
print(f"Consistent {pattern.dominant_format} formatting detected")
|
|
223
|
-
print(f"Safe to process as batch with {pattern.
|
|
227
|
+
print(f"Safe to process as batch with {pattern.decision_confidence:.1%} decision confidence")
|
|
224
228
|
else:
|
|
225
229
|
print("Mixed formatting detected - process individually")
|
|
226
230
|
|
|
@@ -244,7 +248,9 @@ When you call `normalize_name`, you get a `ParseResult` with helpful structured
|
|
|
244
248
|
- `surname_tokens`, `given_tokens`: normalized, capitalized tokens used to form components
|
|
245
249
|
- `middle_tokens`: trailing single-letter initials extracted from given name, if present
|
|
246
250
|
- `order`: component order descriptor, typically `["given", "middle", "surname"]`
|
|
247
|
-
- `parsed_original_order`: A `ParsedName`
|
|
251
|
+
- `parsed_original_order`: A `ParsedName` with the same semantic `surname` and
|
|
252
|
+
`given_name` labels as `parsed`, plus an `order` list that records how those
|
|
253
|
+
components appeared in the input.
|
|
248
254
|
|
|
249
255
|
Notes:
|
|
250
256
|
- The tokens in `parsed` and `parsed_original_order` are the same normalized tokens; only the conceptual ordering differs via the `order` list.
|
|
@@ -257,8 +263,8 @@ res = detector.normalize_name("Li Wei")
|
|
|
257
263
|
# res.result == "Wei Li"
|
|
258
264
|
# res.parsed.order == ["given", "middle", "surname"]
|
|
259
265
|
# res.parsed_original_order.order == ["surname", "given"]
|
|
260
|
-
# res.parsed_original_order.given_name == "
|
|
261
|
-
# res.parsed_original_order.surname == "
|
|
266
|
+
# res.parsed_original_order.given_name == "Wei"
|
|
267
|
+
# res.parsed_original_order.surname == "Li"
|
|
262
268
|
|
|
263
269
|
res = detector.normalize_name("Chi-Ying F. Huang")
|
|
264
270
|
# res.result == "Chi-Ying F Huang"
|
|
@@ -280,7 +286,7 @@ When processing multiple names together, Sinonym:
|
|
|
280
286
|
|
|
281
287
|
1. **Detects Format Patterns**: Analyzes the entire batch to identify whether names follow a surname-first (e.g., "Zhang Wei") or given-first (e.g., "Wei Zhang") pattern
|
|
282
288
|
2. **Aggregates Evidence**: Uses frequency statistics across all names to build confidence in the detected pattern
|
|
283
|
-
3. **Applies Consistent Formatting**: When
|
|
289
|
+
3. **Applies Consistent Formatting**: When `decision_confidence` exceeds the configured threshold, applies the detected pattern to improve parsing of ambiguous individual names
|
|
284
290
|
4. **Tracks Improvements**: Identifies which names benefit from batch context vs. individual processing
|
|
285
291
|
|
|
286
292
|
### Key Benefits
|
|
@@ -303,7 +309,10 @@ result = detector.analyze_name_batch([
|
|
|
303
309
|
])
|
|
304
310
|
print(f"Format detected: {result.format_pattern.dominant_format}")
|
|
305
311
|
print(f"Confidence: {result.format_pattern.confidence:.1%}")
|
|
312
|
+
print(f"Decision confidence: {result.format_pattern.decision_confidence:.1%}")
|
|
313
|
+
print(f"Vote margin: {result.format_pattern.vote_margin:.1%}")
|
|
306
314
|
print(f"Improved names: {len(result.improvements)}")
|
|
315
|
+
print(result.name_order_evidence[0].selected_surname_position)
|
|
307
316
|
|
|
308
317
|
# Quick format detection without full processing
|
|
309
318
|
pattern = detector.detect_batch_format([
|
|
@@ -320,6 +329,37 @@ for result in results:
|
|
|
320
329
|
print(f"Processed: {result.result}")
|
|
321
330
|
```
|
|
322
331
|
|
|
332
|
+
Use `analyze_name_batch()` when you need to choose between two batch contexts
|
|
333
|
+
such as paper-level and source/venue/year-level runs. `process_name_batch()`
|
|
334
|
+
returns only the final `ParseResult` list and intentionally drops the routing
|
|
335
|
+
evidence.
|
|
336
|
+
|
|
337
|
+
`BatchFormatPattern` exposes batch-level convention evidence:
|
|
338
|
+
|
|
339
|
+
- `dominant_format`, `confidence`, and `threshold_met`
|
|
340
|
+
- `decision_confidence`, the score used by the batch-application gate
|
|
341
|
+
- `surname_first_count`, `given_first_count`, and `total_count`
|
|
342
|
+
- `voting_count`, `vote_margin_count`, and `vote_margin`
|
|
343
|
+
|
|
344
|
+
`BatchParseResult.name_order_evidence` is aligned with `names` and `results`.
|
|
345
|
+
Each `NameOrderEvidence` contains stable evidence for external context-routing
|
|
346
|
+
rules:
|
|
347
|
+
|
|
348
|
+
- token shape: `raw_tokens`, `raw_token_count`, `has_all_caps_token`,
|
|
349
|
+
`all_caps_tokens`
|
|
350
|
+
- batch behavior: `script_representation`, `batch_participant`,
|
|
351
|
+
`batch_applied`, `batch_changed_format`
|
|
352
|
+
- order choices: `individual_format`, `selected_format`,
|
|
353
|
+
`selected_surname_position`
|
|
354
|
+
- endpoint frequency evidence: `first_token_surname_frequency`,
|
|
355
|
+
`last_token_surname_frequency`, `selected_surname_frequency`,
|
|
356
|
+
`alternate_endpoint_surname_frequency`,
|
|
357
|
+
`selected_over_alternate_surname_frequency_ratio`
|
|
358
|
+
|
|
359
|
+
Caller-owned metadata such as source, venue, and year is not inferred by
|
|
360
|
+
sinonym. Keep that metadata beside the PP/VYS batch calls and combine it with
|
|
361
|
+
the emitted evidence in the external router.
|
|
362
|
+
|
|
323
363
|
### Persistent Multi-Process Processing
|
|
324
364
|
|
|
325
365
|
For high-throughput workloads, you can keep a persistent process pool alive and
|
|
@@ -339,7 +379,7 @@ def main():
|
|
|
339
379
|
results_a = pool.normalize_names(names_a)
|
|
340
380
|
results_b = pool.normalize_names(names_b)
|
|
341
381
|
|
|
342
|
-
# One-off
|
|
382
|
+
# One-off compatibility wrapper with full batch-context semantics
|
|
343
383
|
single_batch = detector.process_name_batch_multiprocess(names_a, max_workers=6, chunk_size=64)
|
|
344
384
|
return results_a, results_b, single_batch
|
|
345
385
|
|
|
@@ -362,7 +402,9 @@ Batch processing requires a minimum of 2 names and works best with 5+ names for
|
|
|
362
402
|
|
|
363
403
|
**Unambiguous Names**: Some names have only one possible parsing format (e.g., compound given names like "Wei‑Qi Wang"). Batch processing never forces such names into the detected pattern and never raises. These names keep their best individual parse while other Chinese names benefit from the jointly detected order.
|
|
364
404
|
|
|
365
|
-
**
|
|
405
|
+
**Batch Application Threshold**: Batch detection keeps count-based evidence (`confidence`, counts, and vote margin) separate from the application decision (`decision_confidence`). Batch formatting is applied only when the direction is confident, at least two vote-eligible Latin-only Chinese names participate, and `decision_confidence` clears the configured threshold. Latin rows with all-caps source-token cues are exposed in `name_order_evidence` but do not vote in, or receive, Latin batch formatting.
|
|
406
|
+
|
|
407
|
+
**Script Cohorts**: Vote-eligible Latin-only names vote in and receive Latin batch formatting. Han-only, explicitly aligned Han/Roman, and other mixed-script names are parsed from their own script evidence so a Latin batch convention does not flip their order.
|
|
366
408
|
|
|
367
409
|
### Batch Processing with Mixed Name Types
|
|
368
410
|
|
|
@@ -385,7 +427,7 @@ mixed_names = [
|
|
|
385
427
|
|
|
386
428
|
result = detector.analyze_name_batch(mixed_names)
|
|
387
429
|
|
|
388
|
-
# Format detection uses only the 8 Chinese names
|
|
430
|
+
# Format detection uses only the 8 Latin-only Chinese names
|
|
389
431
|
# If 7 prefer GIVEN_FIRST vs 1 SURNAME_FIRST = 87.5% confidence
|
|
390
432
|
# GIVEN_FIRST pattern is applied to Chinese names; non‑Chinese names return clear failures
|
|
391
433
|
|
|
@@ -411,8 +453,8 @@ for i, (name, result_obj) in enumerate(zip(mixed_names, result.results)):
|
|
|
411
453
|
|
|
412
454
|
**Key Benefits:**
|
|
413
455
|
- **Maintains input-output correspondence**: Results array matches input array length and order
|
|
414
|
-
- **Robust format detection**: Only valid Chinese names contribute to pattern detection
|
|
415
|
-
- **Consistent formatting**:
|
|
456
|
+
- **Robust format detection**: Only valid Latin-only Chinese names contribute to Latin batch pattern detection
|
|
457
|
+
- **Consistent formatting**: Latin-only Chinese names get the detected format applied when the batch signal is strong enough
|
|
416
458
|
- **Clear failure reporting**: Non-Chinese names are clearly marked as failed with error messages
|
|
417
459
|
|
|
418
460
|
## Development
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sinonym"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.8"
|
|
8
8
|
description = "Chinese Name Detection and Normalization Module"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -61,6 +61,8 @@ include = [
|
|
|
61
61
|
include = [
|
|
62
62
|
"sinonym",
|
|
63
63
|
"tests",
|
|
64
|
+
"scripts/check_test_status.py",
|
|
65
|
+
"scripts/name_order_routing_rules.py",
|
|
64
66
|
"README.md",
|
|
65
67
|
"pyproject.toml",
|
|
66
68
|
]
|
|
@@ -80,7 +82,9 @@ select = ["E", "F", "W", "C90", "I", "N", "UP", "YTT", "S", "BLE", "FBT", "B", "
|
|
|
80
82
|
ignore = ["S101", "T201"] # Allow assert statements and print statements
|
|
81
83
|
|
|
82
84
|
[tool.ruff.lint.per-file-ignores]
|
|
83
|
-
"tests/*" = ["S101", "T201", "ARG", "FBT"]
|
|
85
|
+
"tests/*" = ["S101", "T201", "ARG", "FBT", "INP001"]
|
|
86
|
+
"tests/test_mixed_scripts.py" = ["RUF001"]
|
|
87
|
+
"tests/test_non_chinese_rejection.py" = ["RUF001"]
|
|
84
88
|
|
|
85
89
|
[tool.mypy]
|
|
86
90
|
python_version = "3.10"
|
|
@@ -5,7 +5,7 @@ Utility scripts for benchmarking, profiling, testing, and model training.
|
|
|
5
5
|
## Active Scripts
|
|
6
6
|
|
|
7
7
|
### `check_test_status.py`
|
|
8
|
-
Runs the full test suite and reports individual test case failures with detailed diagnostics. Runs performance tests separately, then exits 0/1 based on whether the failure count
|
|
8
|
+
Runs the full test suite and reports individual test case failures with detailed diagnostics. Runs performance tests separately, then exits 0/1 based on whether the failure count and failure signatures match the expected baseline configured in `scripts/check_test_status.py`. Improvements (fewer failures) also pass when the remaining failures are all from the known baseline; regressions or unexpected failure signatures fail.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
uv run python scripts/check_test_status.py
|
|
@@ -54,6 +54,19 @@ Trains the Chinese-vs-Japanese name classifier used in production. Downloads Chi
|
|
|
54
54
|
uv run python scripts/train_ml_classifier_for_chinese_vs_japanese.py
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
### `name_order_routing_rules.py`
|
|
58
|
+
Applies the external routing rules for context runs that compare paper-level PP, VYS, input-order abstain, and terminal non-person outputs. The routing policy lives in `sinonym.pipeline.name_order_routing`; this script is the file-format CLI wrapper for already-expanded routing rows and adds `router_prediction` plus `router_reason`.
|
|
59
|
+
`router_prediction` can be `pp`, `vys`, `abstain`, or `not_person`; `not_person` means no person parse should be emitted.
|
|
60
|
+
For PP/VYS/abstain, input rows contain parser evidence from aligned PP and VYS runs. The router derives `old_prediction`, `old_reason`, `new_prediction`, and `new_reason` as audit output; those fields are not policy inputs.
|
|
61
|
+
For direct PP/VYS `BatchParseResult` usage, call `build_pp_vys_abstain_rows` or `route_pp_vys_abstain_batches`; those functions use the same evidence-row policy path as the file CLI.
|
|
62
|
+
The PP/VYS/abstain row regime requires raw `name` so the narrow validated name-prior guards can run.
|
|
63
|
+
CSV and JSONL use only the standard library; Parquet inputs/outputs require pandas plus a Parquet engine in the runtime environment.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv run python scripts/name_order_routing_rules.py pp-vys-abstain --input pp_vys_features.parquet --output routed.parquet
|
|
67
|
+
uv run python scripts/name_order_routing_rules.py pp-abstain --input pp_only_features.parquet --output routed.parquet
|
|
68
|
+
```
|
|
69
|
+
|
|
57
70
|
## Abandoned Scripts
|
|
58
71
|
|
|
59
72
|
These remain for historical reference but are not used by the library. The rule-based parser in `sinonym.services.parsing` replaced the ML approach.
|