Skip to content

Commit 430b067

Browse files
authored
Merge pull request #9 from Ontos-AI/fix/wangbinqi/python-parse-result-payload
fix: sync parse result payload with current API schema
2 parents 837ac96 + c8fc035 commit 430b067

7 files changed

Lines changed: 421 additions & 5 deletions

File tree

src/knowhere/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
ImageFileInfo,
4747
Manifest,
4848
ParseResult,
49+
ProcessingCost,
50+
ProcessingMetadata,
51+
ProcessingTiming,
52+
SlimChunk,
4953
Statistics,
5054
TableChunk,
5155
TableFileInfo,
@@ -91,6 +95,10 @@
9195
"FileIndex",
9296
"ImageFileInfo",
9397
"TableFileInfo",
98+
"ProcessingCost",
99+
"ProcessingMetadata",
100+
"ProcessingTiming",
101+
"SlimChunk",
94102
"BaseChunk",
95103
"TextChunk",
96104
"ImageChunk",

src/knowhere/_exceptions.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,29 @@ def makeStatusError(
387387
response=response,
388388
)
389389

390-
if exception_class in (RateLimitError, ServiceUnavailableError, GatewayTimeoutError):
391-
return exception_class(
390+
if exception_class is RateLimitError:
391+
return RateLimitError(
392392
status_code,
393393
**common_kwargs,
394-
retry_after=retry_after, # type: ignore[call-arg]
394+
retry_after=retry_after,
395+
limit=limit,
396+
period=period,
397+
)
398+
399+
if exception_class is ServiceUnavailableError:
400+
return ServiceUnavailableError(
401+
status_code,
402+
**common_kwargs,
403+
retry_after=retry_after,
404+
limit=limit,
405+
period=period,
406+
)
407+
408+
if exception_class is GatewayTimeoutError:
409+
return GatewayTimeoutError(
410+
status_code,
411+
**common_kwargs,
412+
retry_after=retry_after,
395413
limit=limit,
396414
period=period,
397415
)

src/knowhere/lib/result_parser.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
ImageChunk,
1717
Manifest,
1818
ParseResult,
19+
SlimChunk,
1920
TableChunk,
2021
TextChunk,
2122
TextChunkTokens,
@@ -134,6 +135,7 @@ def _buildChunks(
134135
type="image",
135136
content=raw.get("content", ""),
136137
path=raw.get("path"),
138+
page_nums=metadata.get("page_nums", raw.get("page_nums")),
137139
length=metadata.get("length", raw.get("length", 0)),
138140
file_path=file_path,
139141
original_name=metadata.get("original_name", raw.get("original_name")),
@@ -151,6 +153,7 @@ def _buildChunks(
151153
type="table",
152154
content=raw.get("content", ""),
153155
path=raw.get("path"),
156+
page_nums=metadata.get("page_nums", raw.get("page_nums")),
154157
length=metadata.get("length", raw.get("length", 0)),
155158
file_path=file_path,
156159
original_name=metadata.get("original_name", raw.get("original_name")),
@@ -167,10 +170,12 @@ def _buildChunks(
167170
type="text",
168171
content=raw.get("content", ""),
169172
path=raw.get("path"),
173+
page_nums=metadata.get("page_nums", raw.get("page_nums")),
170174
length=metadata.get("length", raw.get("length", 0)),
171175
tokens=_parseTextChunkTokens(raw_tokens, chunk_id=chunk_id),
172176
keywords=metadata.get("keywords", raw.get("keywords")),
173177
summary=metadata.get("summary", raw.get("summary")),
178+
connect_to=metadata.get("connect_to", raw.get("connect_to")),
174179
relationships=metadata.get("relationships", raw.get("relationships")),
175180
)
176181

@@ -230,12 +235,39 @@ def parseResultZip(
230235
json.loads(hierarchy_text) if hierarchy_text else None
231236
)
232237

238+
# -- Optimized sidecar files --
239+
chunks_slim_text: Optional[str] = _readZipText(zf, "chunks_slim.json")
240+
parsed_chunks_slim: Any = json.loads(chunks_slim_text) if chunks_slim_text else None
241+
if isinstance(parsed_chunks_slim, dict) and "chunks" in parsed_chunks_slim:
242+
raw_chunks_slim: List[Dict[str, Any]] = parsed_chunks_slim["chunks"]
243+
elif isinstance(parsed_chunks_slim, list):
244+
raw_chunks_slim = parsed_chunks_slim
245+
else:
246+
raw_chunks_slim = []
247+
chunks_slim: Optional[List[SlimChunk]] = (
248+
[SlimChunk.model_validate(chunk) for chunk in raw_chunks_slim]
249+
if chunks_slim_text is not None
250+
else None
251+
)
252+
253+
toc_hierarchies_text: Optional[str] = _readZipText(zf, "toc_hierarchies.json")
254+
toc_hierarchies: Optional[Any] = (
255+
json.loads(toc_hierarchies_text) if toc_hierarchies_text else None
256+
)
257+
258+
kb_csv: Optional[str] = _readZipText(zf, "kb.csv")
259+
hierarchy_view_html: Optional[str] = _readZipText(zf, "hierarchy_view.html")
260+
233261
zf.close()
234262

235263
return ParseResult(
236264
manifest=manifest,
237265
chunks=chunks,
266+
chunks_slim=chunks_slim,
238267
full_markdown=full_markdown,
239268
hierarchy=hierarchy,
269+
toc_hierarchies=toc_hierarchies,
270+
kb_csv=kb_csv,
271+
hierarchy_view_html=hierarchy_view_html,
240272
raw_zip=zip_bytes,
241273
)

src/knowhere/types/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
ImageFileInfo,
1414
Manifest,
1515
ParseResult,
16+
ProcessingCost,
17+
ProcessingMetadata,
18+
ProcessingTiming,
19+
SlimChunk,
1620
Statistics,
1721
TableChunk,
1822
TableFileInfo,
@@ -36,6 +40,10 @@
3640
"ImageFileInfo",
3741
"Manifest",
3842
"ParseResult",
43+
"ProcessingCost",
44+
"ProcessingMetadata",
45+
"ProcessingTiming",
46+
"SlimChunk",
3947
"Statistics",
4048
"TableChunk",
4149
"TableFileInfo",

src/knowhere/types/result.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import os
6+
import json
67
import re
78
from pathlib import Path
89
from typing import Any, Dict, List, Optional, Union
@@ -92,12 +93,39 @@ class FileIndex(BaseModel):
9293

9394
chunks: Optional[str] = None
9495
markdown: Optional[str] = None
96+
chunks_slim: Optional[str] = None
9597
kb_csv: Optional[str] = None
9698
hierarchy: Optional[str] = None
99+
toc_hierarchies: Optional[str] = None
100+
hierarchy_view_html: Optional[str] = None
97101
images: List[ImageFileInfo] = Field(default_factory=list)
98102
tables: List[TableFileInfo] = Field(default_factory=list)
99103

100104

105+
class ProcessingCost(BaseModel):
106+
"""Billing details emitted by manifest v2."""
107+
108+
micro_dollars: Optional[int] = None
109+
credits: Optional[float] = None
110+
111+
112+
class ProcessingTiming(BaseModel):
113+
"""Timing details emitted by manifest v2."""
114+
115+
started_at: Optional[str] = None
116+
completed_at: Optional[str] = None
117+
duration_ms: Optional[int] = None
118+
119+
120+
class ProcessingMetadata(BaseModel):
121+
"""Worker-side processing metadata emitted by manifest v2."""
122+
123+
page_count: Optional[int] = None
124+
billing_status: Optional[str] = None
125+
cost: Optional[ProcessingCost] = None
126+
timing: Optional[ProcessingTiming] = None
127+
128+
101129
class Manifest(BaseModel):
102130
"""Top-level manifest describing the result ZIP contents."""
103131

@@ -106,6 +134,7 @@ class Manifest(BaseModel):
106134
data_id: Optional[str] = None
107135
source_file_name: Optional[str] = None
108136
processing_date: Optional[str] = None
137+
processing: Optional[ProcessingMetadata] = None
109138
checksum: Optional[Checksum] = None
110139
statistics: Optional[Statistics] = None
111140
files: Optional[FileIndex] = None
@@ -123,6 +152,7 @@ class BaseChunk(BaseModel):
123152
type: str
124153
content: str = ""
125154
path: Optional[str] = None
155+
page_nums: Optional[List[int]] = None
126156

127157

128158
TextChunkTokens: TypeAlias = List[str]
@@ -136,6 +166,7 @@ class TextChunk(BaseChunk):
136166
tokens: Optional[TextChunkTokens] = None
137167
keywords: Optional[List[str]] = None
138168
summary: Optional[str] = None
169+
connect_to: Optional[List[Dict[str, Any]]] = None
139170
relationships: Optional[List[Union[Dict[str, Any], str]]] = None
140171

141172

@@ -210,6 +241,15 @@ def save(self, directory: Union[str, Path]) -> Path:
210241
Chunk = Union[TextChunk, ImageChunk, TableChunk]
211242

212243

244+
class SlimChunk(BaseModel):
245+
"""Minimal chunk entry emitted in chunks_slim.json."""
246+
247+
type: str
248+
path: Optional[str] = None
249+
content: str = ""
250+
summary: Optional[str] = None
251+
252+
213253
# ---------------------------------------------------------------------------
214254
# ParseResult — the top-level object returned to the user
215255
# ---------------------------------------------------------------------------
@@ -225,23 +265,35 @@ class ParseResult:
225265

226266
manifest: Manifest
227267
chunks: List[Chunk]
268+
chunks_slim: Optional[List[SlimChunk]]
228269
full_markdown: str
229270
hierarchy: Optional[Any]
271+
toc_hierarchies: Optional[Any]
272+
kb_csv: Optional[str]
273+
hierarchy_view_html: Optional[str]
230274
raw_zip: bytes
231275

232276
def __init__(
233277
self,
234278
*,
235279
manifest: Manifest,
236280
chunks: List[Chunk],
281+
chunks_slim: Optional[List[SlimChunk]],
237282
full_markdown: str,
238283
hierarchy: Optional[Any],
284+
toc_hierarchies: Optional[Any],
285+
kb_csv: Optional[str],
286+
hierarchy_view_html: Optional[str],
239287
raw_zip: bytes,
240288
) -> None:
241289
self.manifest = manifest
242290
self.chunks = chunks
291+
self.chunks_slim = chunks_slim
243292
self.full_markdown = full_markdown
244293
self.hierarchy = hierarchy
294+
self.toc_hierarchies = toc_hierarchies
295+
self.kb_csv = kb_csv
296+
self.hierarchy_view_html = hierarchy_view_html
245297
self.raw_zip = raw_zip
246298

247299
# -- convenience properties --
@@ -296,10 +348,58 @@ def save(self, directory: Union[str, Path]) -> Path:
296348
dir_path: Path = Path(directory)
297349
dir_path.mkdir(parents=True, exist_ok=True)
298350

351+
# Manifest / chunks
352+
manifest_path: Path = dir_path / "manifest.json"
353+
manifest_path.write_text(
354+
self.manifest.model_dump_json(indent=2),
355+
encoding="utf-8",
356+
)
357+
358+
chunks_path: Path = dir_path / "chunks.json"
359+
chunks_path.write_text(
360+
json.dumps([chunk.model_dump() for chunk in self.chunks], indent=2),
361+
encoding="utf-8",
362+
)
363+
364+
if self.chunks_slim is not None:
365+
chunks_slim_path: Path = dir_path / "chunks_slim.json"
366+
chunks_slim_path.write_text(
367+
json.dumps(
368+
{"chunks": [chunk.model_dump() for chunk in self.chunks_slim]},
369+
indent=2,
370+
),
371+
encoding="utf-8",
372+
)
373+
299374
# Full markdown
300375
md_path: Path = dir_path / "full.md"
301376
md_path.write_text(self.full_markdown, encoding="utf-8")
302377

378+
if self.hierarchy is not None:
379+
hierarchy_path: Path = dir_path / "hierarchy.json"
380+
hierarchy_path.write_text(
381+
json.dumps(self.hierarchy, indent=2),
382+
encoding="utf-8",
383+
)
384+
385+
if self.toc_hierarchies is not None:
386+
toc_hierarchies_path: Path = dir_path / "toc_hierarchies.json"
387+
toc_hierarchies_path.write_text(
388+
json.dumps(self.toc_hierarchies, indent=2),
389+
encoding="utf-8",
390+
)
391+
392+
if self.kb_csv is not None:
393+
kb_csv_path: Path = dir_path / "kb.csv"
394+
kb_csv_path.write_text(self.kb_csv, encoding="utf-8")
395+
396+
if self.hierarchy_view_html is not None:
397+
hierarchy_view_path: Path = dir_path / "hierarchy_view.html"
398+
hierarchy_view_path.write_text(
399+
self.hierarchy_view_html,
400+
encoding="utf-8",
401+
)
402+
303403
# Images
304404
if self.image_chunks:
305405
images_dir: Path = dir_path / "images"

0 commit comments

Comments
 (0)