Skip to content

feat(token-classification): expose GLiNER2.5 CLI options, fix leftovers - #2521

Merged
inureyes merged 4 commits into
mainfrom
fix/issue-2468-token-classification
Oct 21, 2026
Merged

inureyes merged 4 commits into
mainfrom
fix/issue-2468-token-classification

Conversation

@inureyes

Copy link
Copy Markdown
Member

Implements #2468: the four token-classification leftovers in one PR.

Item 1: GLiNER2.5 CLI options

mlxcel extract gains --max-len <N> and --word-splitter <whitespace|char>, wired through SpanExtractorLoadOptions and load_span_extractor_with to the loaded Gliner2_5Model. A max_len outside 1..=MAX_SUPPORTED_MAX_LEN produces the existing config error text (GLiNER2.5 max_len must be an integer in 1..=32768, got ...) before any model loads, and an unknown splitter is refused with --word-splitter must be whitespace or char, got '<value>'. Absent flags keep the checkpoint's own values. The char splitter scores every non-space character separately for scripts without word spacing such as CJK.

Tests: extract_gliner_flags_parse and extract_gliner_flags_reject_bad_values go through the real Cli parser (both were written first and failed to compile before the flags existed), and two checkpoint-backed gates run against fastino/gliner2.5-small-v1: --word-splitter char on the fixture's CJK case matching the mlx-vlm char case, and --max-len reaching the loaded model including the out-of-range text. Flags are visible in mlxcel extract --help.

Item 2: Viterbi f64 accumulation

The constrained BIOES lattice sums in f64 as upstream's f64 bias adds do, keeping the public decode(&[Vec<f32>]) -> Vec<usize> signature and tie resolution. f64_accumulation_keeps_an_increment_f32_loses decodes a lattice where at V = 2^24 the f32 ULP is 2 and V + 1 rounds back to V, tying the closing E-x; f64 keeps the increment and closes the entity. All existing Viterbi tests pass unchanged.

Item 3: band mask once per chunk

The Privacy Filter band masks are built once per query chunk per forward (band_windows) and shared by every layer, instead of per chunk per layer. Spans are unchanged on the existing fixtures, and band_masks_are_built_once_per_chunk_per_forward counts BAND_MASKS_BUILT: 40 tokens in 7-query chunks builds 6 masks, not 12.

Item 4: checkpoint shape validation

PrivacyFilterModel::from_weights validates every dense tensor against the config-derived shapes before the model graph is built and returns an error naming the tensor with its expected and actual shape, instead of aborting inside MLX. malformed_shapes_error_with_the_tensor_named covers a missing tensor, a wrong rank and a wrong dimension, and proves a quantized plane (a .scales sibling) is skipped.

Validation

The filtered cargo test --profile test-fast runs for token_classification, extract and openai_privacy_filter with --test-threads=1, extract --help, cargo fmt --all -- --check and cargo clippy --profile test-fast -- -D warnings pass locally with --features cuda on GB10 (test binary under gpu-lock, MLX_CUDA_ARCHITECTURES=121, MLX_ENABLE_TF32=0).

Closes #2468

Expose the GLiNER2.5 load-time options on `mlxcel extract` and fix three token-classification leftovers from #2383/#2387/#2390/#2391.

`--max-len <N>` and `--word-splitter <whitespace|char>` reach the loaded Gliner2_5Model through SpanExtractorLoadOptions and load_span_extractor_with; a max_len outside 1..=32768 produces the existing config error text before any model loads, and default behavior is unchanged when the flags are absent. Both are visible in `mlxcel extract --help`.

The Viterbi lattice accumulates in f64 as upstream's f64 bias adds do, keeping the `decode(&[Vec<f32>]) -> Vec<usize>` signature and the existing tie resolution. The Privacy Filter band mask is built once per query chunk per forward and shared across layers instead of per chunk per layer. A checkpoint whose tensor shapes do not match the config now fails at load naming the tensor with its expected and actual shape instead of aborting inside MLX; quantized planes (a `.scales` sibling) and optional biases and sinks are skipped, missing tensor, wrong rank and wrong dimension covered.

Validation: the filtered cargo test --profile test-fast runs for token_classification, extract and openai_privacy_filter with --test-threads=1, extract --help, fmt --check and clippy -D warnings pass with --features cuda on GB10 (test binary under gpu-lock, MLX_ENABLE_TF32=0). The item 1 CLI tests were written first and failed to compile before the flags existed.

Closes #2468
@inureyes inureyes added area:cli Command-line interface / CLI flags area:models Model architectures, weights, loading, metadata priority:medium Medium priority status:review Under review type:enhancement New features, capabilities, or significant additions priority:low Low priority and removed priority:medium Medium priority labels Oct 21, 2026
--max-len and --word-splitter are now CLI flags on mlxcel extract (PR #2521); document them in the extract options paragraph and replace the stale "no CLI flag yet" statement in the GLiNER2.5 Input paragraph.
Bilingual technical report for the token-classification leftovers PR (#2468): GLiNER2.5 CLI options, Viterbi f64, band mask sharing, shape validation.
@inureyes inureyes added status:done Completed and removed status:review Under review labels Oct 21, 2026
manual_find -> Iterator::find in gliner2_5_small_dir, .err().expect -> expect_err (3 sites), and drop a redundant i32 cast in the band-mask chunk test.
@inureyes
inureyes merged commit a96e11e into main Oct 21, 2026
27 of 28 checks passed
@inureyes
inureyes deleted the fix/issue-2468-token-classification branch October 21, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:cli Command-line interface / CLI flags area:models Model architectures, weights, loading, metadata priority:low Low priority status:done Completed type:enhancement New features, capabilities, or significant additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(token-classification): expose GLiNER2.5 CLI options, fix leftovers

1 participant