feat: add sentence_pair_classification modality#329
Merged
Conversation
Enable the NLP sentence-pair classification use case end-to-end on the ingestor side (Phase-4 cross-repo wiring for engine PR #301). The on-disk contract is one .txt per sample under texts/, each a tab-separated text_a<TAB>text_b sentence pair, plus a class label in the labels CSV. sentence_pair is SUPERVISED text classification — the label travels in the CSV exactly like text_classification (is_classification=True, is_self_supervised=False, staged from texts/) — but its .txt has a STRUCTURED shape (the text_classification layout with a tab separating the pair). Wiring (mirrors text_classification; structural check mirrors embeddings): - utils/constants.py: TaskCategory.SENTENCE_PAIR_CLASSIFICATION. - schema/ingest.v1.json: enum value, texts requirement + description, and the "requires label" rule (supervised — NOT the self-supervised no-label guard). - modalities/registry.py: ModalitySpec (file-bearing, classification, NLP, texts/) — is_nlp gates the #805 data-derived text profile (the ingestor side of the federated tokenizer-alignment check). - modalities/validators.py + transfer.py + cli/conventions.py: same validator set + raw-text staging from texts/ as text_classification. What's distinct from text_classification: the on-disk shape is STRUCTURED, so beyond the shared TextContentValidator (UTF-8/binary hygiene) it adds a structural check that rejects any .txt that isn't exactly 2 non-empty tab fields (no plain prose, no empty side, one record per file). Rather than clone ContrastivePairsValidator, extract the shared scaffold into a new TabSeparatedRecordValidator base (centralized validators, #317); ContrastivePairsValidator (2-or-3 fields) subclasses it with byte-identical messages (its tests pass unchanged) and SentencePairValidator (exactly-2) is a thin subclass. Template fails loudly via run_ingestion (no exit-0-on-failure). Adds templates/sentence_pair_classification (script + README + sample pairs), examples/yaml/sentence_pair_classification.yaml, and full test coverage (new + updated enumerating tests, e2e params). Unit suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
divyasinghds
previously approved these changes
Jul 1, 2026
Ships the sentence_pair_classification modality; v0.5.5 will be the first image to contain it. Bumping in this PR so the release tag can be cut straight from develop after merge (no separate bump PR needed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
divyasinghds
approved these changes
Jul 1, 2026
shujaatTracebloc
added a commit
that referenced
this pull request
Jul 3, 2026
…mmary flow The embeddings (#327) and sentence_pair (#329) suites branched before the #325 global-meta refactor, which removed the per-batch send_batch publish in favour of one send_ingest_summary call after commit. Their `*_api_send_failure_counts_every_record` tests still asserted the removed per-record `api_send_failed` accounting (send_batch is never called now), so they failed `assert 0 == 2`. Rewrite both to the post-refactor contract, mirroring the passing sibling test_ingest_summary_failure_raises_out_of_ingest: a failing send_ingest_summary raises out of ingest() so the run still exits non-zero. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Enable the NLP sentence-pair classification use case end-to-end on the ingestor side — Phase-4 cross-repo wiring for engine tracebloc-engine#301.
The on-disk contract: one
.txtper sample undertexts/, each a tab-separatedtext_a<TAB>text_bsentence pair, plus a classlabelin the labels CSV.sentence_pair_classificationis supervised text classification — the label travels in the CSV exactly liketext_classification(is_classification=True,is_self_supervised=False, staged fromtexts/) — but the.txthas a structured shape (thetext_classificationlayout with a tab separating the pair).Wiring (mirrors
text_classification; structural check mirrorsembeddings)utils/constants.py—TaskCategory.SENTENCE_PAIR_CLASSIFICATION.schema/ingest.v1.json— enum value,textsrequirement + description, and added to the "requireslabel" rule (supervised — deliberately not the self-supervised no-label guard).modalities/registry.py—ModalitySpec(file-bearing, classification, NLP,texts/).is_nlpgates the #805 data-derived text profile (the ingestor side of the federated tokenizer-alignment check — there is nocheck_same_tokenizerin this repo; that lives in backend/SDK).modalities/validators.py+transfer.py+cli/conventions.py— same validator set + raw-text staging fromtexts/astext_classification.What's distinct from
text_classificationThe on-disk shape is structured, so beyond the shared
TextContentValidator(UTF-8/binary hygiene) it adds a structural check that rejects any.txtthat isn't exactly 2 non-empty tab fields (no plain prose, no empty side, one record per file).Rather than clone
ContrastivePairsValidator, this extracts the shared scaffold into a newTabSeparatedRecordValidatorbase (centralized validators, #317).ContrastivePairsValidator(2-or-3 fields) subclasses it with byte-identical messages — its existing tests pass unchanged — andSentencePairValidator(exactly-2) is a thin subclass.Template fails loudly via the shared
run_ingestionhelper (noexit 0on hard failure);zip(ids, batch)send path untouched.Tests
templates/sentence_pair_classification/(script + README + labels CSV + 5 sample pairs),examples/yaml/sentence_pair_classification.yaml.tests/test_sentence_pair_validator.py+tests/test_sentence_pair_classification.py(supervised — incl. rejecting a config that omitslabel).1353 passed, 1 xfailed); black-clean. e2e runs on CI (MySQL service).🤖 Generated with Claude Code
Note
Medium Risk
New modality touches schema, registry, validators, and ingestion paths used by all YAML submissions; behavior is well-tested but expands the supported category surface customers rely on.
Overview
Adds supervised
sentence_pair_classificationend-to-end: CSV manifest +texts/sidecars where each.txtis exactlytext_a<TAB>text_b, with class labels in the CSV liketext_classification.Wires the new category through
TaskCategory,ingest.v1.json(enum, requiredtexts+label), modality registry (NLP, file-bearing, classification), conventions, transfer (text_transferfromtexts/), and a validator stack that addsSentencePairValidatoron top of the usual text hygiene and label checks.Introduces shared
TabSeparatedRecordValidatorand refactorsContrastivePairsValidatorto subclass it (embeddings pair/triplet vs sentence-pair exactly-2 fields). Ships template, example YAML, docs, e2e/characterization cases, and broad test coverage; bumps package version to 0.5.5.Reviewed by Cursor Bugbot for commit dc356b1. Bugbot is set up for automated code reviews on this repo. Configure here.