VectifyAI/PageIndex · warning · DeprecationWarning

optimize_expand is deprecated: pass optimize='full', 'merge'

Error message

optimize_expand is deprecated: pass optimize='full', 'merge', or False. When optimize is not passed it maps onto it (False -> 'merge', True -> 'full'), so the optimize pass now runs where the old optimize=False default ran nothing.

What it means

Deprecation warning emitted by page_index_flash when the legacy optimize_expand keyword is passed. The option still works (False -> 'merge', True -> 'full') but only when the newer optimize argument is not passed, and the optimize pass now runs where the old optimize=False default did nothing.

Source

Thrown at pageindex/flash/api.py:105

             for page_text in page_texts]
    outcome = asyncio.run(optimize(structure, page_texts, lines, model=model,
                                   do_expand=do_expand,
                                   page_count=len(page_texts)))
    return {"merges": outcome["merges"], "expands": outcome["expands"],
            "same_page_merges": outcome["same_page_merges"],
            "same_page_dropped": outcome["same_page_dropped"],
            "kept_collapsed": outcome["kept_collapsed"],
            "before": outcome["before"], "after": outcome["after"]}


def page_index_flash(pdf, summary=True, summary_model=None,
                     optimize: str | bool | None = None, optimize_expand=None,
                     optimize_model=None, summary_concurrency=None,
                     use_embedded_toc=True) -> dict:
    """Build a PageIndex tree structure from a PDF using layout statistics. The tree extraction itself uses no LLM; by default an LLM writes node summaries and expands the tree (``summary=False, optimize=False`` runs fully LLM-free). Args: pdf: path to a PDF file (``str`` or ``pathlib.Path``) or an in-memory binary stream (``io.BytesIO``). summary: if True, generate LLM summaries for each node (requires ``summary_model``). summary_model: the LLM model identifier to use for summary generation. optimize: ``"full"`` for merge + LLM expand (a model unreachable after the retry ladder — a missing credential included — fails the run loudly from expand itself; a per-prompt rejection leaves just that node collapsed), ``"merge"`` for deterministic merge only, ``False`` to disable. ``True`` is accepted as ``"full"`` for backward compatibility; defaults to ``"full"``. Expand needs readable page text, so a bookmark-only or scanned PDF runs the merge half only (``expands`` reports 0). optimize_expand: deprecated — use ``optimize``. Honored only when ``optimize`` is not passed (or is the legacy ``True``): ``False`` maps to ``"merge"``, ``True`` to ``"full"``. optimize_model: the LLM model for expand (defaults to the summary model). summary_concurrency: maximum simultaneous summary model calls; None uses the library default. use_embedded_toc: if True, consume the PDF's embedded bookmarks when trustworthy: deep bookmarks become the frame and the detected sections they lack are grafted back in after noise filtering, coarse ones become the chapter frame with detected nodes re-hung under them (deeper sparse entries are filled in when the page text confirms them, and garbled extracted titles are repaired from the bookmark strings), garbage ones are ignored; adds a ``toc_source`` key to the result. On by default; pass False for the pure detected structure. Returns: dict with keys ``doc_name``, ``doc_title``, ``structure`` (a list of nested ``{"title", "start_index", "end_index", "nodes"}`` dicts; page indexes are 1-based) and ``has_abstract_or_references_section`` (True when a top-level entry is an abstract or references heading). With ``optimize`` an ``optimize`` key reports merge/expand counts and before/after search-cost metrics. """
    if optimize_expand is not None:
        import warnings
        warnings.warn(
            "optimize_expand is deprecated: pass optimize='full', 'merge', "
            "or False. When optimize is not passed it maps onto it (False "
            "-> 'merge', True -> 'full'), so the optimize pass now runs "
            "where the old optimize=False default ran nothing.",
            DeprecationWarning, stacklevel=2)
    if optimize is None or optimize is True:
        # legacy spellings only — an explicit 'full'/'merge' wins
        optimize = "merge" if optimize_expand is False else "full"
    if not optimize:
        optimize = False
    elif optimize not in ("full", "merge"):
        raise ValueError(
            f"optimize must be 'full', 'merge', or False, got {optimize!r}")
    result = extract_toc(_validate_pdf(pdf), use_embedded_toc=use_embedded_toc)
    structure = result.get("structure", [])
    if optimize and structure:
        # bookmark-only extractions carry no page_texts and scanned ones
        # only empty strings; expand needs text

View on GitHub (pinned to afb5e11976)

Solutions

  1. Replace optimize_expand=True with optimize='full' and optimize_expand=False with optimize='merge'
  2. Pass optimize=False to fully disable the optimize pass (the old default behavior)
  3. Silence during migration with warnings.filterwarnings('ignore', category=DeprecationWarning) — but migration is better

Example fix

# before
page_index_flash(pdf, optimize_expand=True)
# after
page_index_flash(pdf, optimize='full')
Defensive patterns

Strategy: validation

Validate before calling

import inspect
kwargs = dict(model_kwargs)
if 'optimize_expand' in kwargs:
    kwargs['optimize'] = 'full' if kwargs.pop('optimize_expand') else 'merge'
result = page_index_flash(pdf, **kwargs)

Type guard

def uses_current_optimize_api(kwargs: dict) -> bool:
    return 'optimize' in kwargs and 'optimize_expand' not in kwargs

Try / catch

import warnings
with warnings.catch_warnings():n    warnings.simplefilter('ignore', DeprecationWarning)
    result = page_index_flash(pdf, optimize_expand=True)  # legacy call, silenced

Prevention

When it happens

Trigger: Calling page_index_flash(optimize_expand=True/False) — old scripts written before the optimize='full'|'merge'|False API was introduced.

Common situations: Upgrading the library and running existing pipelines that pass optimize_expand; seeing behavior change because optimize now defaults to 'full' and runs LLM expand where it previously did nothing.

Related errors


AI-assisted analysis of VectifyAI/PageIndex@afb5e11976 (2026-08-27). Data as JSON: /api/errors/b8f9b6195dc87fb9. Report an issue: GitHub.