Skip to content

Add create_onboarding_experiments utility#1120

Merged
TaperChipmunk32 merged 8 commits into
masterfrom
create_onboarding_experiments
Jul 20, 2026
Merged

Add create_onboarding_experiments utility#1120
TaperChipmunk32 merged 8 commits into
masterfrom
create_onboarding_experiments

Conversation

@davidbaines

@davidbaines davidbaines commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds silnlp/common/create_onboarding_experiments.py, a utility that turns a production Serval onboarding request into ready-to-run NMT experiment folders.

Given a request folder name in MT/experiments/_OnboardingRequests/, it:

  • Parses onboarding.log for the main project and every aligned reference project (both the request's onboarded references and the standard alignment set), collecting verse counts, parallel counts, alignment scores, and scripts.
  • Looks up the target language's name and country from assets/languageFamilies.json and creates <Country>/<Language>/ folders (title case, underscores for spaces/hyphens).
  • Selects references passing configurable thresholds (--min-parallel, default 2000; --min-alignment, default 0.2) and creates one experiment per passing reference plus two-source mixed_src combinations ordered by alignment score (interactive selection when more than 3 pairs qualify).
  • Writes config.yml (NLLB lang codes, with <iso3>_<script> fallback for languages not in NLLB) and translate_config.yml (one translate entry per source project). corpus_books comes from --training-books, either a book selection or complete, which derives the books from verse_counts.csv using a 98% completeness rule.
  • Prints the silnlp.nmt.experiment command for each experiment and offers to run them (--run skips the prompt). Experiments skipped because an identical config.yml already exists are still offered, since they may not have been run yet.
  • Supports --dry-run to report everything without creating files (run commands are not printed — nothing will be run).

Distinct src/trg iso codes

The src and trg isos of a corpus pair must differ (silnlp would otherwise collapse both sides into one NLLB language token). When a passing reference shares the main project's iso — e.g. a closely related language whose translation team uses the major language's code — the tool synthesizes a target code that is neither a real iso nor in NLLB (first letter kept, last two letters mutated) and copies the target extract file in MT/scripture (plus its MT/terms renderings files) to the new stem, keeping the originals. The copy is deferred until an experiment folder is actually created, so a run that creates nothing leaves the shared store untouched, and the copied file on disk is the durable record: later runs reuse the same code even if the clash is no longer visible under different thresholds.

Translated books are excluded from training

Books in --translate-books are always excluded from corpus_books, so none of their text is used for training (a config that trains on the books it translates looks invalid even when the target books are empty). Explicit book lists are kept verbatim with subtraction selections appended (NTNT;-MAT, GEN-DEUGEN-DEU;-EXO, chapter selections subtract exactly the selected chapters); complete mode subtracts before NT/OT compaction. The user is warned with the list of excluded books.

Book-list arguments

--training-books and --translate-books are parsed and validated with machine.scripture.get_chapters: the full silnlp selection syntax is preserved verbatim (ranges like GEN-DEU, chapter selections like "MAT 1-4", subtractions like "NT;-REV"), comma- or space-separated lists are normalised (GEN,RUT works unquoted), input is case-insensitive, and invalid book IDs are rejected at the CLI instead of failing later at preprocess time.

Translation-source checks

Before the configs are written, each chosen source project (the src_project in translate_config.yml) is verified: its folder must exist in M/Paratext/projects and contain a book file for every book being translated, with file names derived from the project's Settings.xml naming convention via FileParatextProjectSettingsParser. A missing project or book produces a warning and an interactive offer to translate from a different project (checked the same way) or keep the original; the replacement only affects translate_config.yml. Dry runs warn without prompting.

Test-set options

--no-test creates train-only experiments (type: train, folder suffix _notest, --test dropped from the run command); --test100 uses a 100-verse test set (test_size: 100, folder suffix _test100). The default remains the standard random 250-verse test set with no suffix.

Corpus-stats input (preferred) with log fallback

corpus-stats.csv written by silnlp.common.analyze is the preferred input — it is the stable machine-readable artifact, whereas the log parser scrapes message wording. The tool probes <folder>/corpus-stats.csv then <folder>/alignments/corpus-stats.csv, falling back to onboarding.log only when neither exists (older request folders). The request argument therefore also accepts any folder relative to MT/experiments containing a stats file — e.g. an alignment folder like PNG/Taupota/Align. The main project is the stem appearing in every row on one side (trg for alignment runs, src for onboarding runs); when a log sits next to the CSV its main-project line orients the direction, so single-reference CSVs are never misread. verse_counts.csv is read from the same folder so --training-books complete works unchanged. A stats folder inside the experiments tree (but not under _OnboardingRequests) creates its experiments next to itself (PNG/Taupota/AlignPNG/Taupota), preserving the existing country/language folder naming.

--target override

--target names the target language by iso code or project name, overriding detection: it may match either corpus-stats.csv column (rows not involving it are ignored), iso codes match across their 2/3-letter forms, and matching two projects that share an iso — or both sides of a row — is an error suggesting the project name. With only an onboarding.log the value is validated against the log's main project. When the override flips the direction away from the analyze run's own target, a note is printed and the derived <Country>/<Language> location is used instead of the stats folder's parent.

Safeguards

The log-derived main-project hint is soft: if it does not fit the CSV the tool warns and falls back to auto-detection, then to parsing the log itself, so a stale or partial CSV never breaks a previously working folder. CSV rows with missing statistics are skipped with a warning. For a synthetic iso the target extract file (and its terms renderings) is copied to the new stem — the originals are kept, since they may be referenced by other experiments — after a y/N confirmation (default No), with a warning when --target overrode the direction and a caution when the target's iso is an NLLB code. If the original is later re-extracted, a warning flags the copy as possibly outdated. The create-next-to-stats location rule is disabled for request folders, folders directly under MT/experiments, and flipped targets.

Experiment selection

Every run displays one numbered list of possible experiments capped at the top 20 (--top adjusts the cap) — all single-source experiments first (ranked by alignment), then two-source pairs in best-source-first order — and asks which to create (1,3, all, or none; duplicates are deduplicated). Nothing is created automatically; --dry-run prints the list without prompting and reports all displayed experiments.

Usage

poetry run python -m silnlp.common.create_onboarding_experiments SFDH_2026_07_13 --translate-books MAT
poetry run python -m silnlp.common.create_onboarding_experiments SFDH_2026_07_13 --translate-books GEN,RUT --training-books "NT;-REV" --test100
poetry run python -m silnlp.common.create_onboarding_experiments PNG/Taupota/Align --translate-books MAT

Test plan

  • 34 tests in tests/test_create_onboarding_experiments.py using a real-format fixture log: log parsing, folder naming, NLLB tag resolution, the 98% completeness rule and NT/OT compaction, book-list parsing/validation, translate-book exclusion (plain lists, ranges, chapter selections, complete mode), synthetic iso derivation, deferred extract/terms renames with dry-run and re-run/prior-rename behaviour, test-set variants, index handling and identical-config skipping, corpus-stats parsing (both directions, --target by iso/project/stem, mixed-direction rows, ambiguity detection), CSV-over-log precedence with the log hint, the stats-folder experiment location, the top-20 selection prompt, end-to-end folder/YAML creation, dry-run, and the run-offer behaviour.
  • Validated with --dry-run against real onboarding request folders (A33_2026_07_02_Request, MKWBe_2026_07_16_Request) and used to create and launch real experiments.
  • black / isort / flake8 / mypy clean.

🤖 Generated with Claude Code


This change is Reviewable

davidbaines and others added 2 commits July 15, 2026 17:12
Parses the onboarding.log from a MT/experiments/_OnboardingRequests
folder, selects reference projects that pass parallel-count and
alignment-score thresholds, and creates Country/Language/experiment
folders containing config.yml and translate_config.yml. Supports
single-source and two-source mixed_src experiments, corpus_books
derivation from verse_counts.csv (98% completeness rule), and a
--dry-run mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After creating experiment folders, print the silnlp.nmt.experiment
command for each one and offer to run them (or run directly with
--run). Experiments skipped because an identical config.yml already
exists are included in the offer, since they may not have been run yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
davidbaines and others added 6 commits July 16, 2026 22:49
- Enforce distinct src/trg iso codes: when a reference shares the target
  project's iso, synthesize a non-real, non-NLLB target code (first letter
  kept, last two letters mutated) and rename the target extract file in
  MT/scripture (and its MT/terms renderings files) to match. The rename is
  deferred until an experiment folder is actually created, and the renamed
  file on disk is reused as the durable record of the code on later runs.
- Always exclude --translate-books from corpus_books so no translated text
  is used for training, warning the user once per distinct set. Explicit
  book lists are kept verbatim with subtraction selections appended
  (e.g. NT -> NT;-MAT); complete-mode lists subtract before compaction.
- Parse and validate book-list arguments with machine.scripture
  get_chapters: full silnlp selection syntax is preserved verbatim
  (ranges, chapter selections, subtractions), comma/space-separated lists
  are normalised, input is case-insensitive, and invalid book IDs are
  rejected at the CLI instead of failing at preprocess time.
- Add mutually exclusive --no-test (type: train, folder suffix _notest,
  --test dropped from the run command) and --test100 (test_size: 100,
  folder suffix _test100) options.
- Rename --books to --training-books.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The request argument now also accepts a folder relative to MT/experiments
containing a corpus-stats.csv written by silnlp.common.analyze (e.g. an
alignment folder like PNG/Taupota/Align), as an alternative to an
onboarding request:

- The main project is detected as the stem appearing in every row on one
  side (trg for alignment runs, src for onboarding analyze runs), with
  reference stems, parallel counts, alignment scores, and scripts taken
  from the CSV columns.
- verse_counts.csv is read from the same folder, so --training-books
  complete works unchanged.
- When the stats folder lies inside the experiments tree, experiments are
  created next to it (PNG/Taupota/Align -> PNG/Taupota), preserving the
  existing country/language folder naming; otherwise the location is
  derived from languageFamilies.json as before.
- onboarding.log takes precedence when a folder contains both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- corpus-stats.csv (probed at the folder top level, then alignments/) is
  now the preferred input, with onboarding.log kept as the fallback for
  older request folders. When the log sits next to the CSV, its
  main-project line orients the CSV direction, so single-reference
  onboarding CSVs are never misread; without a hint an ambiguous CSV
  errors and suggests --target. Request folders keep the derived
  <Country>/<Language> location (the create-next-to-stats rule now
  explicitly excludes _OnboardingRequests).
- New --target argument: an iso code or project name naming the target
  language regardless of which corpus-stats.csv column it appears in
  (rows not involving it are ignored; matching two projects that share
  an iso is an error suggesting the project name). With only a log the
  value is validated against the log's main project.
- Experiment selection: instead of creating all singles automatically
  and prompting only for >3 mixed pairs, always display one numbered
  list capped at the top 20 (singles first by alignment, then pairs in
  best-source-first order) and ask which to create; dry runs list
  without prompting and report all displayed experiments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Confirm before renaming: the iso-clash rename of the target extract
  (and terms renderings) in the shared MT/scripture store now always asks
  y/N first (defaulting to abort), with a strong warning when --target
  overrode the analyze run's own target - guarding against renaming a
  shared reference Bible's extract - and a caution when the target's iso
  is an NLLB language code.
- Soft log hint with a fallback chain: if the log-derived main-project
  hint does not fit the CSV, warn and fall back to auto-detection, then
  to parsing the log itself, so a stale or partial corpus-stats.csv never
  breaks a folder that previously worked. An explicit --target remains
  authoritative. parse_log_main_name streams the log and tolerates
  non-UTF-8 bytes.
- Skip CSV rows with missing statistics (analyze writes empty counts
  when original extracts are absent) instead of crashing on NaN.
- Location rules: the create-next-to-stats rule no longer applies to
  folders directly under MT/experiments (was writing to the tree root),
  to request folders identified by their onboarding.log (not just the
  _OnboardingRequests name), or when --target flipped the target
  language (a note is printed and the derived location used).
- --target matching: 2- and 3-letter iso forms are equivalent (fra
  matches fr- stems), and a target matching both sides of a row is an
  explicit ambiguity error instead of silently picking src.
- Selection: duplicate numbers are deduplicated (a repeat was submitted
  twice), an empty selection prints "No experiments selected.", and a
  new --top flag (default 20) lifts the hard display ceiling. The tool
  stays deliberately interactive - no non-interactive escape flags.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The synthetic-iso handling now copies the target extract file (and its
  terms renderings) to the new stem, keeping the originals - they may be
  referenced by other experiments and tools, and clashes are rare enough
  that the duplicate file is not a concern. The y/N confirmation remains;
  its warnings are reframed for the copy. The copy on disk stays the
  durable record of the chosen code, and if the original is re-extracted
  after the copy was made, a warning says the copy may be outdated
  (replacing the old both-files-exist error).
- --dry-run no longer prints the silnlp.nmt.experiment run commands:
  nothing will be run, so it only lists the experiments that would be
  created.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each chosen source project doubles as the src_project in
translate_config.yml, so before the configs are written the tool now
verifies that the project folder exists in M/Paratext/projects and that
it contains a book file for every book being translated, deriving the
file names from the project's Settings.xml naming convention via
FileParatextProjectSettingsParser (the same mechanism paratext.py uses).

A missing project or missing books produces a warning and an interactive
offer to translate from a different project - the replacement is checked
the same way - or to keep the original and proceed. The replacement only
affects src_project in translate_config.yml; the training config keeps
the original extract stems. Dry runs print the warnings without
prompting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TaperChipmunk32
TaperChipmunk32 self-requested a review July 20, 2026 16:05

@TaperChipmunk32 TaperChipmunk32 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be a good foundation for me to add automatic model building to the automated onboarding requests sometime later this year.
:lgtm:

@TaperChipmunk32 made 1 comment.
Reviewable status: 0 of 3 files reviewed, all discussions resolved.

@TaperChipmunk32
TaperChipmunk32 merged commit f70a642 into master Jul 20, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants