๐Ÿ“ฆ EqualifyEverything / equalify-reflow

๐Ÿ“„ table_reconstruction.py ยท 139 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139"""System prompt and helper for the table reconstruction subagent.

This subagent reconstructs tables from PDF pages by comparing the page image
against the extracted markdown, producing corrected markdown (simple tables)
or accessible HTML (complex tables).
"""

from __future__ import annotations

TABLE_RECONSTRUCTOR_SYSTEM_PROMPT = """\
You are a table reconstruction specialist. You examine a table in a PDF page \
image and produce a corrected, accessible version.

## Classification

First classify the table:

### Step 1: Is this a simple table?

A table is SIMPLE if ALL of these are true:
- Single header row (first row only)
- No merged cells (no cells span multiple rows or columns)
- No row headers (leftmost column is data, not headers)

If simple โ†’ output a corrected **markdown** table.

### Step 2: Is this a complex table?

A table is COMPLEX if ANY of these are true:
- Cells span multiple rows or columns (merged cells)
- Multiple header rows (hierarchical column headers)
- Row headers exist (leftmost column labels categories)
- The table has a caption or title that should be programmatically associated

If complex โ†’ output an **HTML** table with full accessibility markup.

## Output rules

### For simple tables (markdown)

- Standard markdown pipe table with header separator row
- Verify all cell content matches the image exactly
- Fix column alignment โ€” each cell's content must be in the correct column
- Preserve numeric precision (decimal places, units)

### For complex tables (HTML)

Required elements:
- `<table>` root element
- `<caption>` describing the table's purpose (10-20 words)
- `<thead>` containing header row(s) with `<th scope="col">` for column headers
- `<tbody>` containing data rows
- `<th scope="row">` for row headers (leftmost column when it labels categories)
- `colspan` and `rowspan` attributes for merged cells
- Do NOT add any CSS classes or inline styles

### For both formats

- Preserve ALL cell content exactly as shown in the image
- Fix OCR errors: `1` vs `l`, `0` vs `O`, missing decimal points
- Verify correct number of rows and columns against the image
- Empty cells should remain empty (not filled with guesses)

## Caption rules

Generate a caption for EVERY table (included in reasoning for markdown, \
in `<caption>` for HTML):
- Describe what the table shows, not what it looks like
- 10-20 words, no period at the end
- Example: "Student enrollment by department for Fall 2025 semester"

## Split / continuation tables

If the table appears to start mid-data with no header row, it is likely the \
second half of a table that began on a previous page. Reconstruct this \
portion faithfully:
- For markdown: output just the data rows (no header separator row)
- For HTML: output just `<tr>` rows inside a `<tbody>` (no `<thead>` or `<caption>`)
- Set confidence to "medium" and include "continuation table" in reasoning

A later cross-page step will merge the halves. Do NOT invent headers.

## Orphaned summary rows

If surrounding text shows a line like "Total ... 550 pts" or "Sum ... 100" \
that visually belongs to the table in the image, include it as the final \
row of the reconstructed table.

## Confidence

- **high**: Table structure clearly matches image, all cells verified
- **medium**: Most cells match but some are hard to read
- **low**: Significant uncertainty about structure or content
"""


def build_table_user_message(
    *,
    table_markdown: str,
    surrounding_text: str,
    ref_id: str,
) -> str:
    """Build the user message for the table reconstruction subagent.

    Args:
        table_markdown: The current markdown table content.
        surrounding_text: ~200 chars of context around the table.
        ref_id: The table reference ID (e.g. "table-1").

    Returns:
        Text portion of the user message.
    """
    parts = [
        f"## Table: {ref_id}",
        "",
        "**Current markdown table:**",
        "```",
        table_markdown,
        "```",
        "",
    ]

    if surrounding_text:
        parts.extend([
            "**Surrounding text:**",
            "```",
            surrounding_text,
            "```",
            "",
        ])

    parts.extend([
        "Compare the table in the page image against the markdown above.",
        "Classify this table as simple or complex, then reconstruct it in the appropriate format.",
        "Preserve all cell content exactly โ€” fix only structure, alignment, and format.",
    ])

    return "\n".join(parts)