docforge-sdk 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ # ====== Code Summary ======
2
+ # Response model for the blobs resource. A blob endpoint streams raw bytes with a registered media
3
+ # type rather than a JSON body, so BlobContent is an SDK-only wrapper (no matching OpenAPI schema)
4
+ # pairing the fetched bytes with the server-declared mime type.
5
+
6
+ # ====== Third-Party Library Imports ======
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class BlobContent(BaseModel):
11
+ """
12
+ A fetched blob's raw bytes plus its server-registered media type.
13
+
14
+ Attributes:
15
+ content (bytes): The raw blob bytes, verbatim.
16
+ mime_type (str): The media type the server registered for the blob.
17
+ """
18
+
19
+ model_config = {"arbitrary_types_allowed": True}
20
+
21
+ content: bytes = Field(description="The raw blob bytes, verbatim.")
22
+ mime_type: str = Field(description="The media type the server registered for the blob.")
23
+
24
+
25
+ __all__ = ["BlobContent"]
@@ -0,0 +1,142 @@
1
+ # ====== Code Summary ======
2
+ # Request/response models for the collections resource, mirrored field-for-field from the DocForge
3
+ # backend router models. The pipeline (ingest graph) and search blobs are opaque server-shaped JSON,
4
+ # so they are typed as plain dicts rather than the engine's structured blob models.
5
+
6
+ # ====== Standard Library Imports ======
7
+ from datetime import datetime
8
+ from typing import Any
9
+
10
+ # ====== Third-Party Library Imports ======
11
+ from pydantic import BaseModel, Field
12
+
13
+ # ====== Local Project Imports ======
14
+ from ._shared import FieldOrigin, FieldScope, FieldType
15
+
16
+
17
+ class FieldSpec(BaseModel):
18
+ """
19
+ One metadata field of the collection's contract (declared OR generated).
20
+
21
+ Attributes:
22
+ field_name (str): Unique field name within the collection.
23
+ field_type (FieldType): Value type — drives validation and storage.
24
+ required (bool): Upload refused without it (user fields).
25
+ filterable (bool): Present in the Qdrant payload (lean vector).
26
+ lexical (bool): Gets a sparse BM25 named vector.
27
+ semantic (bool): Gets a dense named vector.
28
+ enum_values (list[str] | None): Allowed values when ``field_type`` is enum.
29
+ origin (FieldOrigin): user (declared at upload) or generated (metagen).
30
+ scope (FieldScope): document or chunk level value.
31
+ """
32
+
33
+ field_name: str = Field(description="Unique field name within the collection.")
34
+ field_type: FieldType = Field(description="Value type — drives validation and storage.")
35
+ required: bool = Field(default=False, description="Upload refused without it (user fields).")
36
+ filterable: bool = Field(default=False, description="Present in the Qdrant payload (lean).")
37
+ lexical: bool = Field(default=False, description="Gets a sparse BM25 named vector.")
38
+ semantic: bool = Field(default=False, description="Gets a dense named vector.")
39
+ enum_values: list[str] | None = Field(
40
+ default=None, description="Allowed values when field_type is enum."
41
+ )
42
+ origin: FieldOrigin = Field(
43
+ default=FieldOrigin.USER, description="user (declared at upload) or generated (metagen)."
44
+ )
45
+ scope: FieldScope = Field(
46
+ default=FieldScope.DOCUMENT, description="document or chunk level value."
47
+ )
48
+
49
+
50
+ class CollectionModel(BaseModel):
51
+ """
52
+ One collection — the full contract the UI displays and edits.
53
+
54
+ Attributes:
55
+ id (str): The collection's UUID.
56
+ name (str): Unique human name.
57
+ supported_formats (list[str]): Accepted upload extensions (e.g. pdf).
58
+ max_file_size_bytes (int): Upload size ceiling, bytes.
59
+ needs_reindex (bool): True when a config change requires reindexing.
60
+ created_at (datetime | None): Creation timestamp.
61
+ pipeline (dict[str, Any]): The ingestion pipeline blob (the graph).
62
+ search (dict[str, Any]): The search pipeline graph blob ({} = the stock default).
63
+ fields (list[FieldSpec]): The metadata schema.
64
+ """
65
+
66
+ id: str = Field(description="The collection's UUID.")
67
+ name: str = Field(description="Unique human name.")
68
+ supported_formats: list[str] = Field(description="Accepted upload extensions (e.g. pdf).")
69
+ max_file_size_bytes: int = Field(description="Upload size ceiling, bytes.")
70
+ needs_reindex: bool = Field(description="True when a config change requires reindexing.")
71
+ created_at: datetime | None = Field(default=None, description="Creation timestamp.")
72
+ pipeline: dict[str, Any] = Field(description="The ingestion pipeline blob (the graph).")
73
+ search: dict[str, Any] = Field(
74
+ description="The search pipeline graph blob ({} = use the stock default)."
75
+ )
76
+ fields: list[FieldSpec] = Field(default_factory=list, description="The metadata schema.")
77
+
78
+
79
+ class CreateCollectionRequest(BaseModel):
80
+ """
81
+ Create a collection from A to Z — contract, schema and (optionally) its pipeline.
82
+
83
+ Attributes:
84
+ name (str): Unique human name.
85
+ supported_formats (list[str]): Accepted upload extensions (e.g. pdf).
86
+ max_file_size_bytes (int): Upload size ceiling, bytes.
87
+ fields (list[FieldSpec]): The FULL schema, declared up front (vector space is fixed).
88
+ pipeline (dict[str, Any] | None): The pipeline blob; omitted → the product default.
89
+ """
90
+
91
+ name: str = Field(description="Unique human name.")
92
+ supported_formats: list[str] = Field(description="Accepted upload extensions (e.g. pdf).")
93
+ max_file_size_bytes: int = Field(description="Upload size ceiling, bytes.")
94
+ fields: list[FieldSpec] = Field(
95
+ default_factory=list,
96
+ description="The FULL schema, declared up front (vector space is fixed at creation).",
97
+ )
98
+ pipeline: dict[str, Any] | None = Field(
99
+ default=None,
100
+ description="The pipeline blob; omitted → the product default (all stages wired).",
101
+ )
102
+
103
+
104
+ class UpdateCollectionRequest(BaseModel):
105
+ """
106
+ Patch any part of the collection — identity/limits, metadata schema, config blobs.
107
+
108
+ Attributes:
109
+ name (str | None): New unique name.
110
+ supported_formats (list[str] | None): New accepted upload extensions.
111
+ max_file_size_bytes (int | None): New size ceiling, bytes.
112
+ fields (list[FieldSpec] | None): The TARGET schema (diffed by field name).
113
+ pipeline (dict[str, Any] | None): New pipeline blob (validated before storage).
114
+ search (dict[str, Any] | None): New search graph blob ({} = stock default).
115
+ note (str | None): Version note shown in the history.
116
+ """
117
+
118
+ name: str | None = Field(default=None, description="New unique name.")
119
+ supported_formats: list[str] | None = Field(
120
+ default=None, description="New accepted upload extensions."
121
+ )
122
+ max_file_size_bytes: int | None = Field(default=None, description="New size ceiling, bytes.")
123
+ fields: list[FieldSpec] | None = Field(
124
+ default=None,
125
+ description="The TARGET schema (diffed by field name; omitted fields are removed).",
126
+ )
127
+ pipeline: dict[str, Any] | None = Field(
128
+ default=None, description="New pipeline blob (validated before being stored)."
129
+ )
130
+ search: dict[str, Any] | None = Field(
131
+ default=None,
132
+ description="New search pipeline graph blob ({} = stock default; validated before storage).",
133
+ )
134
+ note: str | None = Field(default=None, description="Version note shown in the history.")
135
+
136
+
137
+ __all__ = [
138
+ "FieldSpec",
139
+ "CollectionModel",
140
+ "CreateCollectionRequest",
141
+ "UpdateCollectionRequest",
142
+ ]
@@ -0,0 +1,49 @@
1
+ # ====== Code Summary ======
2
+ # Request/response models for the documents resource, mirrored field-for-field from the DocForge
3
+ # backend router models: the async-admission response and the searchability toggle contract.
4
+
5
+ # ====== Third-Party Library Imports ======
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class UploadAccepted(BaseModel):
10
+ """
11
+ Response for an accepted upload — the ingestion runs asynchronously.
12
+
13
+ Attributes:
14
+ document_id (str): The admitted document (poll its ingestion via the job).
15
+ job_id (str): The ingestion job driving status/progress ('' when duplicate).
16
+ duplicate (bool): True when this exact content+pipeline was already ingested — the EXISTING
17
+ document is returned and nothing is re-run.
18
+ """
19
+
20
+ document_id: str = Field(description="The admitted document's UUID.")
21
+ job_id: str = Field(description="The ingestion job's UUID ('' when duplicate).")
22
+ duplicate: bool = Field(default=False, description="Already ingested — nothing re-run.")
23
+
24
+
25
+ class EnabledPatch(BaseModel):
26
+ """
27
+ The desired searchability state for a document (the reversible toggle).
28
+
29
+ Attributes:
30
+ enabled (bool): True to make it searchable, False to hide it from search.
31
+ """
32
+
33
+ enabled: bool = Field(description="True to make it searchable, False to hide it from search.")
34
+
35
+
36
+ class DocumentEnabledResponse(BaseModel):
37
+ """
38
+ The state of a document after toggling its searchability.
39
+
40
+ Attributes:
41
+ document_id (str): The toggled document.
42
+ enabled (bool): Its new searchability state.
43
+ """
44
+
45
+ document_id: str = Field(description="The toggled document's UUID.")
46
+ enabled: bool = Field(description="The new searchability state.")
47
+
48
+
49
+ __all__ = ["UploadAccepted", "EnabledPatch", "DocumentEnabledResponse"]
@@ -0,0 +1,238 @@
1
+ # ====== Code Summary ======
2
+ # Response/request models for the document explorer resource, mirrored field-for-field from the
3
+ # DocForge backend router models: the catalogue list item, the per-document detail + resolved
4
+ # metadata, a page's render reference, a chunk with its composition, and the chunk-toggle contract.
5
+
6
+ # ====== Standard Library Imports ======
7
+ from datetime import datetime
8
+ from typing import Any
9
+
10
+ # ====== Third-Party Library Imports ======
11
+ from pydantic import BaseModel, Field
12
+
13
+ # ====== Local Project Imports ======
14
+ from ._shared import DocumentStatus, FieldOrigin, SourceKind
15
+
16
+
17
+ class MetadataValue(BaseModel):
18
+ """
19
+ One resolved metadata value — the field name, the stored value and the origin that filled it.
20
+
21
+ Attributes:
22
+ field_name (str): The schema field this value fills.
23
+ value (Any): The stored JSON value (typed by the field).
24
+ origin (FieldOrigin): Who filled it: user / system / generated.
25
+ """
26
+
27
+ field_name: str = Field(description="The schema field this value fills.")
28
+ value: Any = Field(description="The stored JSON value (typed by the field).")
29
+ origin: FieldOrigin = Field(description="Who filled it: user / system / generated.")
30
+
31
+
32
+ class DocumentListItem(BaseModel):
33
+ """
34
+ One row of a collection's document catalogue (the browse list).
35
+
36
+ Attributes:
37
+ id (str): The document's UUID.
38
+ filename (str): The uploaded file name.
39
+ format (str): The file extension (pdf, docx, …).
40
+ status (DocumentStatus): Ingestion lifecycle state.
41
+ page_count (int | None): Pages once parsed (None before).
42
+ file_size (int): Original size in bytes.
43
+ created_at (datetime | None): Admission timestamp.
44
+ title (str): Learned title ('' before parse).
45
+ language (str | None): Detected language (None before parse).
46
+ enabled (bool): Document-level searchability toggle.
47
+ """
48
+
49
+ id: str = Field(description="The document's UUID.")
50
+ filename: str = Field(description="The uploaded file name.")
51
+ format: str = Field(description="The file extension (pdf, docx, …).")
52
+ status: DocumentStatus = Field(description="Ingestion lifecycle state.")
53
+ page_count: int | None = Field(default=None, description="Pages once parsed (None before).")
54
+ file_size: int = Field(description="Original size in bytes.")
55
+ created_at: datetime | None = Field(default=None, description="Admission timestamp.")
56
+ title: str = Field(description="Learned title ('' before parse).")
57
+ language: str | None = Field(default=None, description="Detected language (None before parse).")
58
+ enabled: bool = Field(description="Document-level searchability toggle.")
59
+
60
+
61
+ class DocumentDetail(BaseModel):
62
+ """
63
+ A document's full facts plus its resolved document-level metadata.
64
+
65
+ Attributes:
66
+ id (str): The document's UUID.
67
+ collection_id (str): Owning collection.
68
+ filename (str): The uploaded file name.
69
+ format (str): The file extension.
70
+ mime_type (str): The upload's declared content type.
71
+ file_size (int): Original size in bytes.
72
+ page_count (int | None): Pages once parsed.
73
+ language (str | None): Detected document language.
74
+ title (str): Learned title ('' before parse).
75
+ source_kind (SourceKind): Acquisition routing.
76
+ status (DocumentStatus): Ingestion lifecycle state.
77
+ source_hash (str): Content address of the original bytes (blob key).
78
+ pdf_blob_hash (str | None): Canonical PDF view blob (or None).
79
+ simhash (str | None): Near-duplicate signature (or None).
80
+ pipeline_version (str): Pipeline config identity the run used.
81
+ created_at (datetime | None): Admission timestamp.
82
+ enabled (bool): Document-level searchability toggle.
83
+ metadata (list[MetadataValue]): Document-level values (declared and generated).
84
+ """
85
+
86
+ id: str = Field(description="The document's UUID.")
87
+ collection_id: str = Field(description="Owning collection.")
88
+ filename: str = Field(description="The uploaded file name.")
89
+ format: str = Field(description="The file extension.")
90
+ mime_type: str = Field(description="The upload's declared content type.")
91
+ file_size: int = Field(description="Original size in bytes.")
92
+ page_count: int | None = Field(default=None, description="Pages once parsed.")
93
+ language: str | None = Field(default=None, description="Detected document language.")
94
+ title: str = Field(description="Learned title ('' before parse).")
95
+ source_kind: SourceKind = Field(description="Acquisition routing.")
96
+ status: DocumentStatus = Field(description="Ingestion lifecycle state.")
97
+ source_hash: str = Field(description="Content address of the original bytes (blob key).")
98
+ pdf_blob_hash: str | None = Field(
99
+ default=None, description="Canonical PDF view blob (or None)."
100
+ )
101
+ simhash: str | None = Field(default=None, description="Near-duplicate signature (or None).")
102
+ pipeline_version: str = Field(description="Pipeline config identity the run used.")
103
+ created_at: datetime | None = Field(default=None, description="Admission timestamp.")
104
+ enabled: bool = Field(description="Document-level searchability toggle.")
105
+ metadata: list[MetadataValue] = Field(
106
+ default_factory=list, description="Document-level values (declared and generated)."
107
+ )
108
+
109
+
110
+ class PageInfo(BaseModel):
111
+ """
112
+ One page's geometry, routing and its render blob reference.
113
+
114
+ Attributes:
115
+ page_number (int): 0-based page index (first page = 0).
116
+ width (float | None): Page width in points (None if unknown).
117
+ height (float | None): Page height in points (None if unknown).
118
+ is_scanned (bool): Whether this page was routed as image-only (OCR/VLM).
119
+ language (str | None): Per-page detected language.
120
+ render_blob_hash (str | None): Blob of the rasterized page (None when render was off).
121
+ """
122
+
123
+ page_number: int = Field(description="0-based page index (first page = 0).")
124
+ width: float | None = Field(default=None, description="Page width in points (None if unknown).")
125
+ height: float | None = Field(
126
+ default=None, description="Page height in points (None if unknown)."
127
+ )
128
+ is_scanned: bool = Field(description="Whether this page was routed as image-only (OCR/VLM).")
129
+ language: str | None = Field(default=None, description="Per-page detected language.")
130
+ render_blob_hash: str | None = Field(
131
+ default=None, description="Blob of the rasterized page (None when render was off)."
132
+ )
133
+
134
+
135
+ class ChunkInfo(BaseModel):
136
+ """
137
+ One retrieval chunk — enriched text, composition (block ids) and generated metadata.
138
+
139
+ Attributes:
140
+ id (str): The chunk UUID (doubles as the Qdrant point id).
141
+ chunk_index (int): Position of the chunk within the document.
142
+ text (str): The enriched, embedded text.
143
+ token_count (int): Token length of the enriched text.
144
+ is_indexed (bool): Whether the chunk is upserted into Qdrant.
145
+ role (str): Structural classification set by the pipeline.
146
+ enabled (bool): Effective searchability (override when set, else the role default).
147
+ strategy (str): The chunking strategy that produced it.
148
+ parent_id (str | None): Parent chunk (hierarchical chunking).
149
+ block_ids (list[str]): Composing IR block ids, in assembly order.
150
+ metadata (list[MetadataValue]): Per-chunk generated metadata values.
151
+ """
152
+
153
+ id: str = Field(description="The chunk UUID (doubles as the Qdrant point id).")
154
+ chunk_index: int = Field(description="Position of the chunk within the document.")
155
+ text: str = Field(description="The enriched, embedded text.")
156
+ token_count: int = Field(description="Token length of the enriched text.")
157
+ is_indexed: bool = Field(description="Whether the chunk is upserted into Qdrant.")
158
+ role: str = Field(description="Structural classification set by the pipeline.")
159
+ enabled: bool = Field(description="Effective searchability (override or role default).")
160
+ strategy: str = Field(description="The chunking strategy that produced it.")
161
+ parent_id: str | None = Field(default=None, description="Parent chunk (hierarchical chunking).")
162
+ block_ids: list[str] = Field(
163
+ default_factory=list, description="Composing IR block ids, in assembly (position) order."
164
+ )
165
+ metadata: list[MetadataValue] = Field(
166
+ default_factory=list, description="Per-chunk generated metadata values."
167
+ )
168
+
169
+
170
+ class ChunkEnabledPatch(BaseModel):
171
+ """
172
+ The desired searchability state for one chunk (its enabled_override).
173
+
174
+ Attributes:
175
+ enabled (bool): True to make the chunk searchable, False to hide it.
176
+ """
177
+
178
+ enabled: bool = Field(description="True to make the chunk searchable, False to hide it.")
179
+
180
+
181
+ class BulkChunkEnabledPatch(BaseModel):
182
+ """
183
+ Toggle several chunks' searchability to the same state in one call.
184
+
185
+ Attributes:
186
+ chunk_ids (list[str]): The chunks to toggle (at least one).
187
+ enabled (bool): The state to apply to every listed chunk.
188
+ """
189
+
190
+ chunk_ids: list[str] = Field(min_length=1, description="The chunks to toggle (at least one).")
191
+ enabled: bool = Field(description="The state to apply to every listed chunk.")
192
+
193
+
194
+ class ChunkEnabledResult(BaseModel):
195
+ """
196
+ The outcome of toggling one chunk's searchability.
197
+
198
+ Attributes:
199
+ chunk_id (str): The toggled chunk.
200
+ enabled (bool): The recomputed EFFECTIVE state (override ?? role default).
201
+ reindex_required (bool): True when a never-embedded chunk was enabled — needs a re-embed.
202
+ """
203
+
204
+ chunk_id: str = Field(description="The toggled chunk's UUID.")
205
+ enabled: bool = Field(description="The recomputed effective searchability state.")
206
+ reindex_required: bool = Field(
207
+ description="True when a never-embedded chunk was enabled — needs a deferred re-embed."
208
+ )
209
+
210
+
211
+ class BulkChunkEnabledResponse(BaseModel):
212
+ """
213
+ The per-chunk outcomes of a bulk toggle plus the ids that did not resolve to a chunk.
214
+
215
+ Attributes:
216
+ results (list[ChunkEnabledResult]): One outcome per KNOWN chunk.
217
+ not_found (list[str]): Requested ids with no matching chunk (skipped, not an error).
218
+ """
219
+
220
+ results: list[ChunkEnabledResult] = Field(
221
+ default_factory=list, description="One outcome per known chunk."
222
+ )
223
+ not_found: list[str] = Field(
224
+ default_factory=list, description="Requested ids with no matching chunk."
225
+ )
226
+
227
+
228
+ __all__ = [
229
+ "MetadataValue",
230
+ "DocumentListItem",
231
+ "DocumentDetail",
232
+ "PageInfo",
233
+ "ChunkInfo",
234
+ "ChunkEnabledPatch",
235
+ "BulkChunkEnabledPatch",
236
+ "ChunkEnabledResult",
237
+ "BulkChunkEnabledResponse",
238
+ ]
@@ -0,0 +1,20 @@
1
+ # ====== Code Summary ======
2
+ # Response model for the health resource — the minimal liveness payload the bare-root /health probe
3
+ # returns, mirrored field-for-field from the DocForge backend router model.
4
+
5
+ # ====== Third-Party Library Imports ======
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class HealthStatus(BaseModel):
10
+ """
11
+ The public health probe payload.
12
+
13
+ Attributes:
14
+ status (str): Liveness marker — always ``"ok"`` when the app is serving.
15
+ """
16
+
17
+ status: str = Field(description="Liveness marker — always 'ok' when the app is serving.")
18
+
19
+
20
+ __all__ = ["HealthStatus"]
@@ -0,0 +1,139 @@
1
+ # ====== Code Summary ======
2
+ # Response models for the document IR view (GET /documents/{id}/ir), mirrored field-for-field from the
3
+ # DocForge backend router models. Table cells and enrichment data are opaque server-shaped JSON, so
4
+ # they are typed as ``Any`` rather than a structured grid model.
5
+
6
+ # ====== Standard Library Imports ======
7
+ from typing import Any
8
+
9
+ # ====== Third-Party Library Imports ======
10
+ from pydantic import BaseModel, Field
11
+
12
+ # ====== Local Project Imports ======
13
+ from ._shared import EnrichmentKind, EnrichmentStatus
14
+
15
+
16
+ class IRBlock(BaseModel):
17
+ """
18
+ One RAW IR block — structure, position in reading order and native text.
19
+
20
+ Attributes:
21
+ id (str): Document-namespaced block id.
22
+ block_type (str): text / heading / list / table / figure / …
23
+ page (int): 0-based page the block sits on (first page = 0).
24
+ bbox (list[float]): Bounding box [x0, y0, x1, y1] in page coordinates.
25
+ reading_order (int): Global reading-order rank within the document.
26
+ column_index (int): Column the block belongs to (multi-column layouts).
27
+ parent_id (str | None): Parent block in the heading tree.
28
+ level (int | None): Heading level (None for non-headings).
29
+ text (str | None): Native extracted text (None for figures).
30
+ is_boilerplate (bool): Flagged repeated header/footer/watermark.
31
+ language (str | None): Per-block detected language.
32
+ confidence (float | None): Parser confidence (None if n/a).
33
+ """
34
+
35
+ id: str = Field(description="Document-namespaced block id.")
36
+ block_type: str = Field(description="text / heading / list / table / figure / …")
37
+ page: int = Field(description="0-based page the block sits on (first page = 0).")
38
+ bbox: list[float] = Field(description="Bounding box [x0, y0, x1, y1] in page coordinates.")
39
+ reading_order: int = Field(description="Global reading-order rank within the document.")
40
+ column_index: int = Field(description="Column the block belongs to (multi-column layouts).")
41
+ parent_id: str | None = Field(default=None, description="Parent block in the heading tree.")
42
+ level: int | None = Field(default=None, description="Heading level (None for non-headings).")
43
+ text: str | None = Field(default=None, description="Native extracted text (None for figures).")
44
+ is_boilerplate: bool = Field(description="Flagged repeated header/footer/watermark.")
45
+ language: str | None = Field(default=None, description="Per-block detected language.")
46
+ confidence: float | None = Field(default=None, description="Parser confidence (None if n/a).")
47
+
48
+
49
+ class IRTable(BaseModel):
50
+ """
51
+ A TABLE block's parsed structure (1:1 with its block).
52
+
53
+ Attributes:
54
+ block_id (str): The TABLE block this detail belongs to.
55
+ n_rows (int): Row count of the grid.
56
+ n_cols (int): Column count of the grid.
57
+ has_header (bool): Whether the first row is a header.
58
+ cells (Any): Row-major 2D cell grid (opaque JSON).
59
+ linearized_md (str | None): Deterministic markdown rendering.
60
+ """
61
+
62
+ block_id: str = Field(description="The TABLE block this detail belongs to.")
63
+ n_rows: int = Field(description="Row count of the grid.")
64
+ n_cols: int = Field(description="Column count of the grid.")
65
+ has_header: bool = Field(description="Whether the first row is a header.")
66
+ cells: Any = Field(description="Row-major 2D cell grid (opaque JSON).")
67
+ linearized_md: str | None = Field(default=None, description="Deterministic markdown rendering.")
68
+
69
+
70
+ class IRFigure(BaseModel):
71
+ """
72
+ A FIGURE block's parse-time facts (1:1 with its block).
73
+
74
+ Attributes:
75
+ block_id (str): The FIGURE block this detail belongs to.
76
+ crop_blob_hash (str | None): Blob of the cropped figure image (None if not stored).
77
+ caption_block_id (str | None): The associated caption block id.
78
+ """
79
+
80
+ block_id: str = Field(description="The FIGURE block this detail belongs to.")
81
+ crop_blob_hash: str | None = Field(
82
+ default=None, description="Blob of the cropped figure image (None if not stored)."
83
+ )
84
+ caption_block_id: str | None = Field(
85
+ default=None, description="The associated caption block id (proximity + numbering)."
86
+ )
87
+
88
+
89
+ class IREnrichment(BaseModel):
90
+ """
91
+ One enrichment applied to a block (OCR/VLM/classify/chart_to_data/table_summary).
92
+
93
+ Attributes:
94
+ id (str): The enrichment UUID (use it to fetch the model-chain trace).
95
+ block_id (str): The enriched block.
96
+ kind (EnrichmentKind): What the enrichment produced.
97
+ text (str | None): OCR text / VLM description (None if data-only).
98
+ data (Any | None): Structured result (chart cells, classification).
99
+ status (EnrichmentStatus): ok / failed / skipped.
100
+ """
101
+
102
+ id: str = Field(description="The enrichment UUID (use it to fetch the model-chain trace).")
103
+ block_id: str = Field(description="The enriched block.")
104
+ kind: EnrichmentKind = Field(description="What the enrichment produced.")
105
+ text: str | None = Field(
106
+ default=None, description="OCR text / VLM description (None if data-only)."
107
+ )
108
+ data: Any | None = Field(
109
+ default=None, description="Structured result (chart cells, classification)."
110
+ )
111
+ status: EnrichmentStatus = Field(description="ok / failed / skipped.")
112
+
113
+
114
+ class DocumentIRModel(BaseModel):
115
+ """
116
+ The full IR of a document — raw blocks, table/figure details and every enrichment.
117
+
118
+ Attributes:
119
+ blocks (list[IRBlock]): Raw blocks, reading order.
120
+ tables (list[IRTable]): Table detail rows.
121
+ figures (list[IRFigure]): Figure detail rows.
122
+ enrichments (list[IREnrichment]): Every block enrichment (OCR/VLM/…).
123
+ """
124
+
125
+ blocks: list[IRBlock] = Field(default_factory=list, description="Raw blocks, reading order.")
126
+ tables: list[IRTable] = Field(default_factory=list, description="Table detail rows.")
127
+ figures: list[IRFigure] = Field(default_factory=list, description="Figure detail rows.")
128
+ enrichments: list[IREnrichment] = Field(
129
+ default_factory=list, description="Every block enrichment (OCR/VLM/…)."
130
+ )
131
+
132
+
133
+ __all__ = [
134
+ "IRBlock",
135
+ "IRTable",
136
+ "IRFigure",
137
+ "IREnrichment",
138
+ "DocumentIRModel",
139
+ ]