`\nLayout Detection:` produces degenerate output (uniform striping / marching boxes) while `Text/Table Recognition:` works
Thanks for the model and for adding the Usage section. We followed the pipeline
(detect → crop → per-region call) and the recognition tasks work great, but theLayout Detection: task never produces real regions on any of our documents —
it degenerates into uniform bands / marching identical boxes. We've tried to rule out
every runtime/flag variable we could and would love some guidance. Full repro below.
TL;DR
\nText Recognition:and\nTable Recognition:on cropped regions → accurate (Table returns correct OTSL).\nLayout Detection:on a full page → degenerate every time: 88–93 identical boxes that either stripe the page full-width or march in a line, ignoring actual content. Happens under both vLLM and transformers, on 14+ documents including a dense in-distribution academic paper.- Following the card's two "repeated-nonsense" fixes (
enable_thinking=False,skip_special_tokens=False) does not change it — see "Why the two flags don't apply here".
Environment
| Component | Version |
|---|---|
| Model | KDLAI/KDL-Frontier-Parser-nano, main @ a6cb7d2a9ac5fd13f527764f5411c9ec3ad2c4ec (only commit after our 2026-06-24 download is the docs one) |
| Serving | vLLM 0.24.0 |
| torch | 2.11.0+cu130 |
| transformers | 5.12.1 |
| GPU | NVIDIA RTX 5090 (Blackwell, sm_120, 32 GB), driver 591.86 |
| OS | Windows 11 + WSL2 Ubuntu 24.04 |
(sm_120 note: vLLM's FlashInfer JIT sampler fails to build for sm_120 — "SM 12.x requires CUDA >= 12.9" → generic "FlashInfer requires GPUs with sm75 or higher". We disable it with VLLM_USE_FLASHINFER_SAMPLER=0. This is unrelated to the detection issue; the same degeneration reproduces on the pure-transformers path with no vLLM/FlashInfer involved.)
Serve command
VLLM_USE_FLASHINFER_SAMPLER=0 vllm serve KDLAI/KDL-Frontier-Parser-nano \
--served-model-name kdl-frontier-parser-nano \
--max-model-len 8192 --gpu-memory-utilization 0.6 --max-num-seqs 24 \
--trust-remote-code --limit-mm-per-prompt '{"image":1}'
Exact request (OpenAI-compatible chat endpoint)
{
"model": "kdl-frontier-parser-nano",
"messages": [{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": "data:image/png;base64,<PAGE>"}},
{"type": "text", "text": "\nLayout Detection:"}
]}],
"temperature": 0,
"max_tokens": 2048,
"skip_special_tokens": false,
"chat_template_kwargs": {"enable_thinking": false}
}
Fully rendered prompt the model receives (image pad collapsed):
<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
<|vision_start|><|image_pad|>…<|vision_end|>
Layout Detection:<|im_end|>
<|im_start|>assistant
What works — recognition (same endpoint, same flags)
\nText Recognition: on a cropped company-name region →
ACME ROBOTICS INC.
\nTable Recognition: on a cropped line-item table → correct OTSL:
<fcel>Description<fcel>Qty<fcel>Unit Price<fcel>Amount<nl>
<fcel>Vision LLM inference license<fcel>2<fcel>$1,200.00<fcel>$2,400.00<nl>
<fcel>On-prem GPU server (RTX 5090)<fcel>1<fcel>$3,500.00<fcel>$3,500.00<nl>
...
So the endpoint, chat template, image pipeline, flags, and decoding are all correct.
What fails — \nLayout Detection: (degenerate on every document)
The output token format is always well-formed
(<|box_start|>x1 y1 x2 y2<|box_end|><|ref_start|>LABEL<|ref_end|><|rotate_up|>,
coords normalized 0–999), but the content is degenerate. Three failure shapes:
(a) Full-width horizontal striping — e.g. a clean 1000×1300 invoice → 93 identical boxes:
<|box_start|>000 000 999 060<|box_end|><|ref_start|>header<|ref_end|><|rotate_up|>
<|box_start|>000 060 999 100<|box_end|><|ref_start|>header<|ref_end|><|rotate_up|>
<|box_start|>000 100 999 140<|box_end|><|ref_start|>header<|ref_end|><|rotate_up|>
... (marches by ~40, all label "header", loops after y=980 back to y=100)
(b) Marching specks — a dense 2-column academic paper (1224×1584) → 88 tiny boxes crawling across the top running-head, nothing for the title/abstract/columns:
<|box_start|>100 040 118 049<|box_end|><|ref_start|>page_number<|ref_end|><|rotate_up|>
<|box_start|>120 040 138 049<|box_end|><|ref_start|>page_number<|ref_end|><|rotate_up|>
... (marches in x, all "page_number")
(c) Zero parseable boxes — some photos return looped short text (e.g. 06\n06\n06…) instead of box tokens.
Across 14 documents (synthetic invoice, real Turkish invoices, bank cheques, retail receipts/fiş, a filled government form, and a dense academic paper), 0 produced a usable layout. Different images give different degenerate patterns (so the image IS being read — it's not a blank/misattached input), but never real regions.
Everything we tried (none fixed detection)
| Variable | What we tried | Result |
|---|---|---|
| Runtime | vLLM 0.24.0 and pure transformers (Qwen2VLForConditionalGeneration / AutoModelForImageTextToText) |
Byte-identical degenerate output |
| Decoding | greedy temperature=0 / do_sample=False; repetition_penalty 1.05/1.15/1.30; no_repeat_ngram_size |
Greedy loops; penalties just change the stripe pattern; still degenerate |
| Image preprocessing | canonical Qwen2-VL qwen_vl_utils.process_vision_info smart-resize (e.g. 1008×1288, correct 1656 image tokens) |
No change |
skip_special_tokens |
false |
Honored — the `< |
enable_thinking |
false via chat_template_kwargs |
No-op here (see below). Still degenerate |
| System prompt | removed entirely (custom chat template with no system turn, --trust-request-chat-template) |
Still degenerate |
| Prompt text | appended a detailed "output bbox+category+text, allowed categories […]" instruction after \nLayout Detection: |
Worse — collapses to `< |
| Prompt text | that detailed instruction alone (no task token) | Model ignores it and does full-page OCR instead → it is not instruction-following; only the 5 fixed task tokens steer it |
| Document type | 14 docs incl. a dense in-distribution academic paper | All degenerate |
Why the two flags don't apply here
skip_special_tokens=falseis clearly in effect — the special layout tokens are present in the raw output.enable_thinking=falsehas nothing to disable: the repo'schat_template.jinja(both the live HF copy and our local one) contains no thinking block / noenable_thinkingvariable, andtokenizer_config.json(414 bytes) has nochat_templatefield at all. So there is no thinking mode to turn off in this repo.
Minimal reproduction
import base64, requests
URL = "http://localhost:8000/v1/chat/completions"
def call(png_path, prompt):
b64 = base64.b64encode(open(png_path, "rb").read()).decode()
body = {
"model": "kdl-frontier-parser-nano",
"messages": [{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": "data:image/png;base64," + b64}},
{"type": "text", "text": prompt}]}],
"temperature": 0, "max_tokens": 2048, "skip_special_tokens": False,
"chat_template_kwargs": {"enable_thinking": False},
}
return requests.post(URL, json=body, timeout=300).json()["choices"][0]["message"]["content"]
print(call("any_page.png", "\nLayout Detection:")) # -> degenerate striping / marching boxes
# For contrast, crop a single text/table region and:
# print(call("table_crop.png", "\nTable Recognition:")) # -> correct OTSL
Questions for the authors
- Could you share one known-good
Layout Detection:example — the exact input image plus the exact request (or fully rendered prompt string) and the expected box output? That would immediately tell us whether this is our invocation or a checkpoint issue. - Is there an image requirement for detection not in the card (min/max pixels, DPI, aspect ratio, RGB vs grayscale, page vs region)?
- Is the shipped
chat_template.jinjathe one used for detection? (It injectssystem: You are a helpful assistant.and has no thinking block;tokenizer_config.jsonhas no chat_template. Should a different template be present, and is a system prompt expected?) - What exact versions (transformers / vLLM) and decoding params was detection validated with? Is greedy correct for the Layout task, or does it need specific sampling?
- Any known issue with the detection head at sm_120 / bf16 / this transformers version?
Happy to run any specific image + request you provide and report back. Thanks!