Commit ·
717452f
1
Parent(s): 6856ea8
[NOTICKET] feat(knowledge-parsing): add 1-based page_no (S6b)
Browse filesA computed `page_no` (= page_idx + 1) on Chunk, so the human/review-UI page
number lives in the artifact instead of each consumer converting it.
- Derived, never stored: cannot drift from page_idx, and neither normaliser has
to set it. page_idx stays 0-based and raw.
- Additive, schema 0.3.2 -> 0.3.3; older artifacts and consumers are unaffected.
- Closes S6b (open since 2026-08-19); reverses the earlier "convert in the UI"
note now that a real reviewer is about to read these.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
src/knowledge_parsing/contracts.py
CHANGED
|
@@ -39,15 +39,16 @@ from __future__ import annotations
|
|
| 39 |
|
| 40 |
from typing import Literal
|
| 41 |
|
| 42 |
-
from pydantic import BaseModel, Field
|
| 43 |
|
| 44 |
ChunkKind = Literal["text", "table", "chart", "equation", "image"]
|
| 45 |
|
|
|
|
| 46 |
# 0.3.2 — adds the optional `source_title`.
|
| 47 |
# 0.3.1 — adds the optional `normaliser_version`.
|
| 48 |
-
#
|
| 49 |
# and the new fields are null on anything written earlier.
|
| 50 |
-
SCHEMA_VERSION = "0.3.
|
| 51 |
|
| 52 |
|
| 53 |
class Chunk(BaseModel):
|
|
@@ -70,15 +71,26 @@ class Chunk(BaseModel):
|
|
| 70 |
|
| 71 |
# Location in the source document.
|
| 72 |
#
|
| 73 |
-
# Named `page_idx` deliberately: these are 0-BASED, exactly as
|
| 74 |
-
# them, with no conversion
|
| 75 |
-
#
|
| 76 |
-
#
|
| 77 |
-
#
|
| 78 |
-
#
|
| 79 |
page_idx: int
|
| 80 |
page_idxs: list[int] = Field(default_factory=list)
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
# Structural context. All Optional — many documents carry no headings.
|
| 83 |
section_no: str | None = None # e.g. "2.1.3", when the document is numbered
|
| 84 |
|
|
|
|
| 39 |
|
| 40 |
from typing import Literal
|
| 41 |
|
| 42 |
+
from pydantic import BaseModel, Field, computed_field
|
| 43 |
|
| 44 |
ChunkKind = Literal["text", "table", "chart", "equation", "image"]
|
| 45 |
|
| 46 |
+
# 0.3.3 — adds the computed 1-based `page_no` (S6b). Derived from `page_idx`.
|
| 47 |
# 0.3.2 — adds the optional `source_title`.
|
| 48 |
# 0.3.1 — adds the optional `normaliser_version`.
|
| 49 |
+
# All patch bumps: purely additive, so every 0.3.0 consumer reads them unchanged
|
| 50 |
# and the new fields are null on anything written earlier.
|
| 51 |
+
SCHEMA_VERSION = "0.3.3"
|
| 52 |
|
| 53 |
|
| 54 |
class Chunk(BaseModel):
|
|
|
|
| 71 |
|
| 72 |
# Location in the source document.
|
| 73 |
#
|
| 74 |
+
# Named `page_idx` deliberately: these are 0-BASED, exactly as the parser
|
| 75 |
+
# reports them, with no conversion — so the artifact always matches the raw
|
| 76 |
+
# parser output kept alongside it. The 1-based number a human reads is
|
| 77 |
+
# `page_no` below: a review queue is about to see these, and an off-by-one
|
| 78 |
+
# there is invisible until an expert opens the wrong page, so the conversion
|
| 79 |
+
# is done once, here, rather than left to each consumer (S6b, 2026-09-02).
|
| 80 |
page_idx: int
|
| 81 |
page_idxs: list[int] = Field(default_factory=list)
|
| 82 |
|
| 83 |
+
@computed_field # type: ignore[prop-decorator]
|
| 84 |
+
@property
|
| 85 |
+
def page_no(self) -> int:
|
| 86 |
+
"""1-based page number, for humans and the review UI.
|
| 87 |
+
|
| 88 |
+
Derived from `page_idx`, never stored, so it cannot drift from it — and
|
| 89 |
+
neither normaliser has to remember to set it. `page_idx` stays 0-based
|
| 90 |
+
and raw; this is the single number the reviewer sees.
|
| 91 |
+
"""
|
| 92 |
+
return self.page_idx + 1
|
| 93 |
+
|
| 94 |
# Structural context. All Optional — many documents carry no headings.
|
| 95 |
section_no: str | None = None # e.g. "2.1.3", when the document is numbered
|
| 96 |
|