Fix GLiREL relation extraction: pass token-index ner, not character offsets#152
Open
samarthbanodia wants to merge 1 commit into
Open
Fix GLiREL relation extraction: pass token-index ner, not character offsets#152samarthbanodia wants to merge 1 commit into
samarthbanodia wants to merge 1 commit into
Conversation
…ffsets predict_relations expects ner as [[start_token, end_token, label], ...] aligned to the token list, and returns head_text/tail_text as token-list slices. The previous conversion passed GLiNER character offsets in a 4-tuple and read the slices as strings, yielding 0 relations at the default threshold and a pydantic ValidationError at lower ones. Map char spans to token indices, join the slice output, drop the hard spaCy-tokenization dependency, and add a from_pretrained compat shim for newer huggingface_hub.
|
@samarthbanodia is attempting to deploy a commit to the lyonwj's projects Team on Vercel. A member of the Team first needs to authorize it. |
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.
Fix GLiREL relation extraction: pass token-index
ner, not character offsets (silent 0 relations on glirel ≥ 1.x)Fixes #151.
Summary
GLiRELExtractor(and thereforeGLiNERWithRelationsExtractor) produces zero relations when usedexactly as documented, and raises a
pydantic.ValidationErrorat non-default thresholds. The causeis the entity→GLiREL format conversion: it passes GLiNER character offsets in a 4-tuple where GLiREL
expects token indices in a 3-tuple, and it reads GLiREL's token-list
head_text/tail_textas ifthey were strings. As a side effect, any pipeline that falls back to an LLM when local relation
extraction yields nothing silently routes all relation extraction to the LLM.
This PR makes the conversion correct, joins the token-slice output, and adds a small
huggingface_hubcompatibility shim soGLiREL.from_pretrainedloads on currentglirel/hubversions. Verified end-to-end (see Verification).
Environment where this reproduces
neo4j-agent-memoryadvertises the GLiREL feature but declares noglirelversion pin (onlygliner), so there is no "supportedglirelversion" that avoids this — current resolutions get 1.2.1.Symptoms
GLiNERWithRelationsExtractor.for_poleo().extract(text)→ entities found,relations == []at thedefault
threshold=0.5. No error is raised — it just looks like "no relations in this text."head_text/tail_textas token-listslices (lists), which are passed straight into
ExtractedRelation(source=..., target=...)whosefields are typed
str→pydantic.ValidationError.glirel≤ 1.2.1 + recenthuggingface_hub, the model won't even load:TypeError: GLiREL._from_pretrained() missing 2 required keyword-only arguments: 'proxies' and 'resume_download'.Root cause
GLiRELExtractor._entities_to_glirel_formatbuilds entries as:but
glirel.GLiREL.predict_relations(text, labels, ..., ner=...)(signature(self, text, labels, flat_ner=True, threshold=0.5, ner=None, ground_truth_relations=None, top_k=-1))expects
neras[[start_token, end_token, label], ...]— TOKEN indices aligned to the token listpassed as
text. Character offsets interpreted as token indices land out of range / span the wholesentence, so predictions are empty (silently dropped at
0.5) or degenerate. And the returnedhead_text/tail_textare token-list slices, not strings, so buildingExtractedRelationfrom themeither validates oddly or raises.
The fix
src/neo4j_agent_memory/extraction/gliner_extractor.py:_entities_to_glirel_format(entities, token_spans)now maps each entity's character span to therange of tokens it overlaps and emits the correct 3-tuple
[start_token, end_token, label].Entities whose span can't be resolved are skipped (fewer relations, never a crash).
_extract_relations_synctokenizes with the same regex GLiREL uses(
\w+(?:[-_]\w+)*|\S) so token indices line up, passestop_k=1(best label per pair), andjoins the token-slice
head_text/tail_textinto strings before constructingExtractedRelation. Empty/self-pair predictions are dropped and duplicates de-duplicated.Bonus: this removes the hard dependency on a loaded spaCy model (
en_core_web_sm) for GLiRELtokenization — a common silent failure point. (
_tokenize/nlpare retained, just unused here.)_apply_glirel_hub_compat_shim()is called in the lazymodelproperty: it injects safedefaults for the
proxies/resume_downloadkwargs that newerhuggingface_hubno longer passes,so
from_pretrainedworks onglirel≤ 1.2.1. Idempotent; no-op ifglirelisn't importable.The only signature change is to the private
_entities_to_glirel_format(now takestoken_spans).Reproduction
The runnable repro is in #151. On the unpatched package it prints
relations: 0for everysentence; with this PR applied it prints real relations. Run:
Verification (controlled, with an independent oracle)
A controlled before/after harness loads the model once and compares the shipped vs patched
GLiRELExtractor.extract_relationson the same GLiNER entities at the same thresholds, against anindependent reference implementation of the correct GLiREL contract. Results
(
jackboyla/glirel-large-v0, CPU):John Smith —WORKS_AT→ Acme Corporation,Questar —LOCATED_IN→ Salt Lake City,Sarah Lee —CEO_OF→ EnronThe patched output matches the independent reference implementation set-for-set on every sentence,
and the model loads via the in-property shim with no external patching. Same model, same entities, same
thresholds — the only variable was the conversion code.
Notes
mislabel); that's a model-quality matter, separate from this correctness fix. This PR is strictly
about "the pipeline now returns relations at all" rather than "always to the LLM."
huggingface_hubkwarg change; the loadshim is needed only on newer
huggingface_hub+glirel≤ 1.2.1.