Skip to content

Oversized first page produces an empty chunk that reaches the model #467

Description

@jerrl10

When the first page of a document exceeds the per-part token average, page_list_to_group_text() emits an empty string as its first chunk. Both callers pass that chunk to a model call as if it were a slice of the document.

Reproduction

from pageindex.page_index_classic import page_list_to_group_text

print(page_list_to_group_text(["PAGE-A", "PAGE-B", "PAGE-C"],
                              [60000, 500, 500], max_tokens=20000))
# ['', 'PAGE-A', 'PAGE-APAGE-B', 'PAGE-BPAGE-C']
#  ^^ empty

Root cause

page_index_classic.py:530-537 — at i == 0 the accumulator is still empty, so the boundary test reduces to token_lengths[0] > average_tokens_per_part and flushes before anything has been added:

if current_token_count + page_tokens > average_tokens_per_part:
    subsets.append(''.join(current_subset))     # current_subset is [] when i == 0

Only the first page can trigger it — later splits re-seed the accumulator with the overlap page. [60000, 500, 500] reproduces; [100, 60000, 100] does not.

Consequence

  • process_no_toc() (:686)generate_toc_init() seeds the whole table of contents from an empty prompt. The empty chunk has no <physical_index_N> markers, so _validate_chunk_physical_indices() nullifies everything it returns, and that seed is carried into every generate_toc_continue() call (:698). No page content is lost (page 1 arrives via the overlap in chunk 1) and the bogus entries are filtered at :1221 — the cost is a wasted call and a contaminated seed.
  • process_toc_no_page_numbers() (:738) — this loop does not skip chunk 0. add_page_number_to_toc() is asked to assign page numbers from an empty document, and a mismatched entry count raises ValueError at :742, aborting the run.

Scope

average_tokens_per_part is bounded below by max_tokens / 2, so the first page must exceed ~15k tokens (~60 KB) — a wide table, a dense appendix, a badly paginated export. Not the common case, and call site 1 fails silently, which is likely why it has gone unnoticed. Also reachable per-node: process_large_node_recursively() (:1174) re-runs process_no_toc over one oversized node, so "first page" there is the node's.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions