> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eigenpal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Split Document

> Split a parsed document into named sections using an LLM. Consumes ai.parse output; emits per-section page ranges and text ready for downstream ai.extract via control.parallel_map.

`ai.split`, Split a parsed document into named sections using an LLM. Consumes ai.parse output; emits per-section page ranges and text ready for downstream ai.extract via control.parallel\_map.

## Configuration

Configuration goes inside the step's `with:` block.

<ParamField path="input" type="string" required>
  Template expression resolving to ai.parse output, e.g. "\{\{ steps.parse.output }}"
</ParamField>

<ParamField path="sections" type="array<object>" required>
  Named sections to find in the document

  <Expandable title="sections properties">
    <ParamField path="name" type="string" required>
      Stable id, used as the key in output and template references
    </ParamField>

    <ParamField path="description" type="string" required>
      What this section looks like in the document. Use the document's own terminology (e.g. "Príloha 2 / Anlage 2") and visual cues. Multilingual variants belong here.
    </ParamField>

    <ParamField path="required" type="boolean">
      If true, log a warn when this section is not found, and lean the LLM toward expecting it. Default: false (sections may be absent).
    </ParamField>

    <ParamField path="endHints" type="array<string>">
      Natural-language hints fed to the LLM about what marks the END of this section (e.g. \["PRÍPAD PORUŠENIA ZMLUVY", "*Koniec prílohy*"]). The LLM uses these as guidance, not a regex match, so casing variants, missing diacritics, and multilingual phrasings all close the section correctly.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="rules" type="string">
  Optional natural-language rules appended to the system prompt. E.g. "End-of-section markers like *Koniec prílohy 2* close the current section."
</ParamField>

<ParamField path="provider" type="string">
  Provider ID from eigenpal.config.yaml (e.g. "openai-gpt5.4-mini"). Falls back to the tenant default LLM provider when omitted.
</ParamField>

<ParamField path="windowTokenBudget" type="integer">
  Override the per-window token ceiling for this step. Defaults to env SPLIT\_WINDOW\_TOKEN\_BUDGET or 20000. Smaller windows give sharper anchors on contract-style documents (less competing context for the LLM to mis-anchor on); bump to 50k–100k when sections routinely exceed per-window page count.
</ParamField>

## Output

<ResponseField path="splits" type="array<object>" required>
  Sections found in the document, in page order. Absent sections are omitted.

  <Expandable title="splits properties">
    <ResponseField path="name" type="string" required>
      Section name from the config
    </ResponseField>

    <ResponseField path="page_range" type="array<integer>" required>
      \[startIndex, endIndex] inclusive, 0-based page indices
    </ResponseField>

    <ResponseField path="confidence" type="&#x22;low&#x22; | &#x22;medium&#x22; | &#x22;high&#x22;" required>
      LLM confidence at the anchor page. Coarse enum (low | medium | high), numeric scores cluster meaninglessly at 0.85-0.95.
    </ResponseField>

    <ResponseField path="notes" type="string" required>
      LLM's justification for the anchor, useful for debugging
    </ResponseField>

    <ResponseField path="evidence" type="object">
      Structured start-anchor evidence, parse this instead of regexing over `notes`.

      <Expandable title="evidence properties">
        <ResponseField path="start_heading_text" type="string | null">
          Verbatim heading text the LLM cited as the section start
        </ResponseField>

        <ResponseField path="start_page" type="integer | null">
          Page where start\_heading\_text appears
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField path="end_evidence" type="object">
      Set when the LLM detected an end-of-section cue (matched against the section's `endHints` or an explicit closing marker). Absent when the section was closed by continuity-fill alone.

      <Expandable title="end_evidence properties">
        <ResponseField path="end_page" type="integer" required>
          LAST page of the section (inclusive)
        </ResponseField>

        <ResponseField path="confidence" type="&#x22;low&#x22; | &#x22;medium&#x22; | &#x22;high&#x22;" required />

        <ResponseField path="notes" type="string" required>
          LLM's justification for the end. Pair with start `notes` for reconciliation.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField path="text" type="string" required>
      Joined per-page content for this section, ready for downstream ai.extract
    </ResponseField>

    <ResponseField path="pages" type="array<object>" required>
      Raw per-page records covered by this split (in page order). Iterate when downstream needs per-page context (e.g. control.parallel\_map over section pages).

      <Expandable title="pages properties">
        <ResponseField path="pageIndex" type="integer" required />

        <ResponseField path="text" type="string" required />

        <ResponseField path="pageName" type="string" />

        <ResponseField path="source" type="&#x22;anchored&#x22; | &#x22;inferred&#x22;" required>
          Whether this page is direct LLM evidence (anchored) or continuity-filled (inferred)
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField path="pages_anchored" type="array<integer>" required>
      Pages with direct LLM evidence
    </ResponseField>

    <ResponseField path="pages_inferred" type="array<integer>" required>
      Pages assigned by deterministic continuity fill
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField path="sections" type="record<string, object>" required>
  The same sections keyed by config name, so a downstream step can reference one directly: `&#123;&#123; steps.&lt;split>.output.sections.&lt;name>.page_range }}`. Prefer this over filtering `splits`. On a duplicate name the last wins.
</ResponseField>
