Skip to content

feat: add sentence_pair_classification modality#329

Merged
shujaatTracebloc merged 2 commits into
developfrom
feat/sentence-pair-classification
Jul 1, 2026
Merged

feat: add sentence_pair_classification modality#329
shujaatTracebloc merged 2 commits into
developfrom
feat/sentence-pair-classification

Conversation

@shujaatTracebloc

@shujaatTracebloc shujaatTracebloc commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

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 .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_classification 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 the .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.pyTaskCategory.SENTENCE_PAIR_CLASSIFICATION.
  • schema/ingest.v1.json — enum value, texts requirement + description, and added to the "requires label" rule (supervised — deliberately not the self-supervised no-label guard).
  • modalities/registry.pyModalitySpec (file-bearing, classification, NLP, texts/). is_nlp gates the #805 data-derived text profile (the ingestor side of the federated tokenizer-alignment check — there is no check_same_tokenizer in this repo; that lives in backend/SDK).
  • 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, this extracts the shared scaffold into a new TabSeparatedRecordValidator base (centralized validators, #317). ContrastivePairsValidator (2-or-3 fields) subclasses it with byte-identical messages — its existing tests pass unchanged — and SentencePairValidator (exactly-2) is a thin subclass.

Template fails loudly via the shared run_ingestion helper (no exit 0 on hard failure); zip(ids, batch) send path untouched.

Tests

  • New templates/sentence_pair_classification/ (script + README + labels CSV + 5 sample pairs), examples/yaml/sentence_pair_classification.yaml.
  • New tests/test_sentence_pair_validator.py + tests/test_sentence_pair_classification.py (supervised — incl. rejecting a config that omits label).
  • Updated the enumerating/contract tests (registry, validators_mapping, schema_validation, template_equivalence, modality_failure_accounting) + e2e params.
  • Unit suite green locally (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_classification end-to-end: CSV manifest + texts/ sidecars where each .txt is exactly text_a<TAB>text_b, with class labels in the CSV like text_classification.

Wires the new category through TaskCategory, ingest.v1.json (enum, required texts + label), modality registry (NLP, file-bearing, classification), conventions, transfer (text_transfer from texts/), and a validator stack that adds SentencePairValidator on top of the usual text hygiene and label checks.

Introduces shared TabSeparatedRecordValidator and refactors ContrastivePairsValidator to 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.

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>
@shujaatTracebloc shujaatTracebloc self-assigned this Jul 1, 2026
divyasinghds
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>
@shujaatTracebloc shujaatTracebloc merged commit b9b6387 into develop Jul 1, 2026
6 checks passed
@shujaatTracebloc shujaatTracebloc deleted the feat/sentence-pair-classification branch July 1, 2026 09:53
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>
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.

3 participants