Skip to content

GH-1342: Escape separators in dependency parser combined features - #1520

Open
henriquejsza wants to merge 1 commit into
apache:mainfrom
henriquejsza:fix-depparse-feature-collision
Open

henriquejsza wants to merge 1 commit into
apache:mainfrom
henriquejsza:fix-depparse-feature-collision

Conversation

@henriquejsza

Copy link
Copy Markdown

Thank you for contributing to Apache OpenNLP.

For all changes:

  • Is there a GitHub issue associated with this PR? Is it referenced in the PR description (e.g. "Fixes Regenerated NOTICE file after dependency changes #1234")?
  • Has your PR been rebased against the latest commit within the target branch (typically main)?
  • Is your initial contribution a single, squashed commit?

For code changes:

  • Have you ensured that the full suite of tests is executed via mvn clean install at the root opennlp folder?
  • Have you written or updated unit tests to verify your changes?
  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0? (no new dependencies)
  • If applicable, have you updated the LICENSE file, including the main LICENSE file in opennlp folder? (not applicable)
  • If applicable, have you updated the NOTICE file, including the main NOTICE file found in opennlp folder? (not applicable)

Fixes #1342

DependencyContextGenerator joined the word-tag and position values of a
combined feature with / and | without escaping them. A value holding one
of these characters could make a combined feature of one configuration read
the same as that of another, for example s1t=X|Y, s0t=Z and s1t=X, s0t=Y|Z
both produced s1t,s0t=X|Y|Z.

This escapes the escape character \, /, and | in every value that is
joined into a combined feature, so each combined feature is read back
unambiguously. Single-position features carry one value, so a separator there
cannot split the feature and the value is left as is. Values without any of
these characters are returned unchanged.

The dependency parser is new in 3.0 and no models have shipped yet, so there is
no released model to keep compatible. Universal POS tags never contain / or
|, so the trained-model feature strings only change for tokens that contain
one of these characters; the pinned UniversalDependencyParserEval scores
should be re-run on a newly trained model.

@rzo1
rzo1 requested a review from krickert October 30, 2026 15:02
@krickert

Copy link
Copy Markdown
Contributor

@henriquejsza thank you for this PR. I have time to look it over this weekend (within 48 hours). Thank you for your contribution and appreciate the time you put into this!

I'll also get you an answer about the model training.

@krickert krickert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just a few minor nits

I got around to reviewing this - it's a clean fix and the tests read well.

Two small nits:

  • escape could be a plain private method like the other helpers in the class, no need for static.
  • The Javadoc says the feature "is read back unambiguously", but nothing reads it back. Maybe "stays unambiguous".

@krickert

krickert commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

On the model question: the dependency parser is new in 3.0 and we haven't released a model for it, so nothing published needs retraining.

The only thing affected is UniversalDependencyParserEval, which trains the parser from the UD treebanks during the run and checks UAS and LAS against fixed numbers. Those corpora only live on our Jenkins node, so you can't run it locally.

@mawiesne - once we get someone a second review I'll start the eval job change and post the numbers here. If they moved, I'll share the numbers for @henriquejsza to update the expected scores.

@mawiesne
mawiesne requested a review from rzo1 November 1, 2026 21:03

@rzo1 rzo1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM once krickert's nits are in. Separate issue: a token that is literally *NULL* or *ROOT* still collides with the marker values.

/**
* Prefixes every {@link #ESCAPE}, {@link #WORD_TAG_SEPARATOR}, and
* {@link #POSITION_SEPARATOR} in a value with {@link #ESCAPE}, so that a combined
* feature of several positions is read back unambiguously and cannot collide with the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing reads this back. "stays unambiguous"?

* @param value The value to escape. Must not be {@code null}.
* @return The escaped value, or {@code value} if it has nothing to escape.
*/
private static String escape(String value) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Doesn't need to be static, the other helpers aren't.

DependencyContextGenerator joined position values with the word-tag
separator '/' and the position separator '|' without escaping them.
A value containing one of these characters could make a combined
feature of one configuration read the same as that of another, for
example s1t=X|Y, s0t=Z and s1t=X, s0t=Y|Z both produced
s1t,s0t=X|Y|Z.

Escape the escape character '\', '/', and '|' in each value that is
joined into a combined feature, so every combined feature is read back
unambiguously. Single-position features carry one value and are left as
is. Values without these characters are returned unchanged.

Signed-off-by: henriquejsza <henriquejsza@gmail.com>
@rzo1
rzo1 force-pushed the fix-depparse-feature-collision branch from c9bd776 to 6e4bf84 Compare November 2, 2026 10:17
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.

Dependency parser feature strings can collide on tags containing | or /

3 participants