Skip to content

fix(server): limit the label prompt of GLiNER-family extract requests - #381

Merged
svonava merged 1 commit into
mainfrom
fix/gliner-prompt-token-limit
Sep 26, 2026
Merged

svonava merged 1 commit into
mainfrom
fix/gliner-prompt-token-limit

Conversation

@svonava

@svonava svonava commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

GLiNER, GLiNER2 and GLiREL encode a request's labels, relation types, class
labels and schema fields (the task prompt) with every document, and bill only
the document's tokens. Apart from the count of labels (at most 1000), the
prompt had no documented size limit. GLiFormer and GLiNER2.5-Decide already
limit theirs (512 prompt tokens).

Change

A shared module, sie_server/adapters/_prompt_limit.py, validates the
prompt before any model work. A request is rejected with HTTP 400
INVALID_INPUT when either of these holds:

  • A label is too long. A label, relation type, class label, task name,
    output_schema property name or choice has more than 128 characters (the
    limit GLiFormer applies). This is checked first, before anything is
    tokenized.
  • The prompt takes too many tokens. The count uses the model's own
    tokenizer, and the whole prompt's characters are bounded before counting.
    The limit is:
    • GLiNER and GLiREL: 1024 tokens. GLiNER builds the prompt with its own
      processor (entity types and, for relex models, relation types). GLiREL
      counts [REL] type ... [SEP].
    • GLiNER2: 2048 tokens, since its schemas carry field descriptions. This
      covers entity, relation and classification labels, the task name, and
      output_schema fields and choices. It includes the classifier route used
      by GLiGuard (GLiNER2ClassificationAdapter, gliner2 2.x).

The limit is a max_prompt_tokens load-time option, so a deployment can
change it per model. The size of each distinct prompt is cached, so repeated
requests with the same labels do not tokenize them again.

The defaults sit well above real label sets. Measured with the models'
tokenizers:

Prompt Tokens
Largest label set in this repository's examples (12 labels) 36–51
A 60-type PII label list 210–232
100 two-word entity types 401–502
200 entity types (GLiNER2) 805
A 50-field output_schema with descriptions (GLiNER2) about 1,260 (estimated 1,310)

GLiREL also:

  • Limits supplied entities. An item may carry at most 256 entities in
    metadata.entities. GLiREL scores every pair of them.
  • Reports bad input as INVALID_INPUT. This covers missing labels, text
    or entities, a non-list entities, and invalid entity offsets. Before, most
    of these were reported as an inference error. Every item is checked before
    any is run.

GLiNER2 row estimates for batch planning (from #380) now come from the same
prompt count.

Compatibility

No API or wire change. Requests over the documented limits, which the models
could not use well in any case, now get a 400 with a message naming the limit
instead of running. The limits are documented in the adapters' docstrings.

The classic GLiNER limit of 1024 prompt tokens admits about 340 one-word or
200–250 two-word labels, fewer than the 1000 labels a request may carry;
deployments that need more can raise it with the max_prompt_tokens option.

Tests

tests/adapters/test_prompt_limit.py covers the following:

  • Character check. A label over 128 characters is rejected before
    anything is tokenized (the test asserts the counter is never called),
    including a 4 MiB label. It is enforced for GLiNER relation types, GLiNER2
    labels, the task name, field names and choices, and GLiREL relation types.
  • Token limit. Enforced for GLiNER (counted with gliner's real processor),
    GLiNER2 (labels, long descriptions, many choices, classification) and
    GLiREL. Rejection happens before any inference, and the limit is
    configurable.
  • Ordinary label sets and schemas pass. This includes the GLiGuard
    classifier route.
  • GLiREL input checks. The entity limit, and bad or missing input reported
    as INVALID_INPUT before any model work.

On main, the adapter-level rejection tests fail because no 400 is raised.
The GLiNER-family adapter tests pass with gliner2 1.3.2 and 2.0.0.

Validation

  • mise run lint, mise run typecheck: pass.
  • GLiNER-family adapter tests: pass.
  • The release Docker images (Dockerfile.cuda12, default and
    transformers5 bundles), served on an L4 and probed over HTTP for
    gliner2-large, gliner_multi-v2.1, NuNER Zero, gliner-relex-large, GLiREL
    large and GLiGuard:
    • Ordinary label sets and a 60-type PII list are accepted.
    • Labels over 128 characters, 1 MiB labels, over-limit label sets and
      schemas, 257 GLiREL entities, and bad offsets get 400 INVALID_INPUT in
      about 5–20 ms.
    • Every model answers an ordinary request afterwards.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Requests to GLiNER-family extraction models now reject prompts that exceed configurable token limits or contain labels and schema values longer than 128 characters.
    • GLiREL rejects invalid or incomplete inputs and requests with more than 256 entities before model processing.
    • GLiNER2 includes schema choices in prompt-size checks and uses prompt estimates when sizing extraction rows.
  • New Features
    • Added configurable prompt-token limits for GLiNER, GLiNER2, and GLiREL, with defaults tailored to each model.

GLiNER, GLiNER2 and GLiREL encode a request's labels, relation types, class
labels and schema fields with every document, and bill only the document.
Validate that prompt before any model work, as GLiFormer and
GLiNER2.5-Decide do, and reject a request with INVALID_INPUT when:

- a label, relation type, class label, task name, field name or choice has
  more than 128 characters (checked before anything is tokenized), or
- the prompt takes more than max_prompt_tokens tokens, counted with the
  model's tokenizer: 1024 for GLiNER and GLiREL, 2048 for GLiNER2, whose
  schemas carry field descriptions. The option is configurable per model.

The defaults sit well above real label sets: a 60-type PII list takes about
230 tokens and a 50-field described schema about 1,300.

GLiREL also limits an item to 256 supplied entities, since it scores every
pair of them, and reports missing or malformed input (labels, text,
entities, entity offsets) as INVALID_INPUT, checked for every item before
any is run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@svonava
svonava requested a review from a team as a code owner September 26, 2026 14:26
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 55681c77-a5fc-44d8-9f0a-bbe89e1091e8

📥 Commits

Reviewing files that changed from the base of the PR and between 894a017 and 867b2b2.

📒 Files selected for processing (6)
  • packages/sie_server/src/sie_server/adapters/_prompt_limit.py
  • packages/sie_server/src/sie_server/adapters/gliner/__init__.py
  • packages/sie_server/src/sie_server/adapters/gliner2/adapter.py
  • packages/sie_server/src/sie_server/adapters/glirel/__init__.py
  • packages/sie_server/tests/adapters/test_gliner2_long_text.py
  • packages/sie_server/tests/adapters/test_prompt_limit.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The adapters now validate label lengths and prompt-token counts. GLiNER2 also uses prompt estimates in row sizing. GLiREL rejects malformed inputs and items with more than 256 entities. Tests cover prompt limits, input validation, and long-text row estimates.

Changes

Prompt Limits Across GLiNER Adapters

Layer / File(s) Summary
Shared prompt-limit checks
packages/sie_server/src/sie_server/adapters/_prompt_limit.py, packages/sie_server/tests/adapters/test_prompt_limit.py
Adds shared character and token limit validation, a bounded recent-count cache, and a GLiNER prompt counter. Tests cover utility behavior and character limits across adapters.
GLiNER prompt enforcement
packages/sie_server/src/sie_server/adapters/gliner/__init__.py, packages/sie_server/tests/adapters/test_prompt_limit.py
GLiNER checks entity and relation labels against character and combined token limits before inference. It removes duplicate entity labels before counting.
GLiNER2 prompt sizing and row estimates
packages/sie_server/src/sie_server/adapters/gliner2/adapter.py, packages/sie_server/tests/adapters/test_prompt_limit.py, packages/sie_server/tests/adapters/test_gliner2_long_text.py
GLiNER2 checks prompts for structured, relation, classification, and entity extraction. Prompt estimates feed row sizing, and tests cover prompt validation and long-text estimates.
GLiREL prompt and request validation
packages/sie_server/src/sie_server/adapters/glirel/__init__.py, packages/sie_server/tests/adapters/test_prompt_limit.py
GLiREL checks relation-label prompts and entity counts. It rejects malformed or missing request data with InvalidInputError before inference.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant GLiNERAdapter
  participant PromptLimit
  participant GLiNERProcessor
  Caller->>GLiNERAdapter: extract with labels and relation labels
  GLiNERAdapter->>PromptLimit: check prompt and cache key
  PromptLimit->>GLiNERProcessor: count prepared prompt when uncached
  GLiNERProcessor-->>PromptLimit: return token count
  PromptLimit-->>GLiNERAdapter: return count or reject with InvalidInputError
Loading

Suggested reviewers: dragosboca

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 867b2

The prompt-limit change has no established functional failure, but the GLiNER package layout still needs to meet the contributor requirement. This is a bounded issue for the owner to address before merge or explicitly accept.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 60 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: prompt-size limits for GLiNER-family extraction requests. It is concise and specific, although the implementation also covers broader input validation …
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@svonava
svonava merged commit 5aa0287 into main Sep 26, 2026
21 checks passed
@svonava
svonava deleted the fix/gliner-prompt-token-limit branch September 26, 2026 14:41
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.

1 participant