Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,20 @@ import knowhere

client = knowhere.Knowhere(api_key="sk_...")

result = client.parse(url="https://example.com/report.pdf")
result = client.parse(
url="https://example.com/report.pdf",
)

print(result.statistics.total_chunks)
print(result.full_markdown[:200])

for chunk in result.text_chunks:
print(chunk.content[:80])

for page in result.page_chunks:
print(page.content_source) # "summary"
print(page.content[:120]) # page-level summary
print(page.metadata.page_nums) # [4, 5, 6]
```

## Retrieval and document lifecycle
Expand Down Expand Up @@ -59,6 +66,7 @@ After the job is done and published, query the canonical document content:
response = client.retrieval.query(
namespace="support-center",
query="How do I reset Bluetooth pairing?",
chunk_types=["page"],
top_k=5,
channels=["path", "term"],
filter_mode="keep",
Expand All @@ -72,9 +80,11 @@ print(response.stop_reason)
print(response.failure_reason)

for reference in response.referenced_chunks:
print(reference.chunk_id, reference.document_id, reference.asset_url)
print(reference.chunk_id, reference.chunk_type, reference.content_source)
print(reference.metadata, reference.asset_url)

for result in response.results:
print(result.chunk_id, result.chunk_type, result.content_source)
print(result.content)
print(result.score)
print(result.source.source_file_name, result.source.section_path)
Expand All @@ -96,7 +106,7 @@ chunks = client.documents.list_chunks(
document_id,
page=1,
page_size=50,
chunk_type="image",
chunk_type="page",
include_asset_urls=True,
)
print(chunks.pagination.total)
Expand All @@ -107,7 +117,8 @@ if chunks.chunks:
include_asset_urls=True,
)
print(chunk.chunk.content)
print(chunk.chunk.asset_url) # Requested 7-day URL for image/table chunks.
print(chunk.chunk.metadata.get("page_nums")) # Page citations.
print(chunk.chunk.asset_url) # Requested 7-day URL when available.

client.documents.archive(document_id)
```
Expand Down Expand Up @@ -164,8 +175,15 @@ result = client.parse(url="https://example.com/report.pdf")

# Text chunks
for chunk in result.text_chunks:
print(chunk.keywords)
print(chunk.summary)
print(chunk.metadata.keywords)
print(chunk.metadata.summary)

# Page chunks (v2 page-memory results)
for chunk in result.page_chunks:
print(chunk.content_source) # "summary"
print(chunk.content[:120])
print(chunk.metadata.page_nums) # citation pages
print(chunk.metadata.entities)

# Image chunks (raw bytes loaded from ZIP)
for chunk in result.image_chunks:
Expand Down
54 changes: 46 additions & 8 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ for chunk in result.text_chunks:
print(chunk.content[:80])
```

Page-memory chunks are returned by the v2 API used by this SDK:

```python
result = client.parse(url="https://example.com/report.pdf")

for page in result.page_chunks:
print(page.content_source) # "summary"
print(page.content[:120]) # page-level summary
print(page.metadata.page_nums) # [4, 5, 6]
```

---

## Parsing Documents
Expand Down Expand Up @@ -207,6 +218,7 @@ result.statistics.total_chunks # 152
result.statistics.text_chunks # 120
result.statistics.image_chunks # 22
result.statistics.table_chunks # 10
result.statistics.page_chunks # 48
result.statistics.total_pages # 48

# Full markdown — the entire document as markdown
Expand All @@ -219,6 +231,7 @@ print(len(result.chunks)) # 152
result.text_chunks # List[TextChunk]
result.image_chunks # List[ImageChunk]
result.table_chunks # List[TableChunk]
result.page_chunks # List[PageChunk]

# Lookup by ID
chunk = result.getChunk("chunk_42")
Expand Down Expand Up @@ -248,28 +261,29 @@ result.save("./output/report/")

## Chunk Types

Every chunk shares a base set of fields (`chunk_id`, `type`, `content`, `path`,
`metadata`). Worker metadata is kept in the `metadata` dict — it is **not**
Every chunk shares a base set of fields (`chunk_id`, `type`, `content_source`,
`content`, `path`, `metadata`). Worker metadata is kept in the `metadata` model — it is **not**
flattened to top-level chunk properties.

### Base fields (all chunk types)

| Field | Type | Description |
|-------|------|-------------|
| `chunk_id` | `str` | Unique identifier |
| `type` | `str` | `"text"`, `"image"`, or `"table"` |
| `type` | `str` | `"text"`, `"image"`, `"table"`, or `"page"` |
| `content_source` | `str \| None` | What `content` represents. Page chunks usually use `"summary"`; text chunks default to `"content"`. |
| `content` | `str` | Text content or placeholder |
| `path` | `str \| None` | Document structure path |
| `metadata` | `dict` | Raw worker metadata (tokens, keywords, summary, length, page_nums, etc.) |
| `metadata` | `ChunkMetadata` | Raw worker metadata (tokens, keywords, summary, length, page_nums, entities, etc.) |

### TextChunk

```python
for chunk in result.text_chunks:
print(f"[{chunk.chunk_id}] {chunk.content[:60]}...")
# Metadata is in chunk.metadata, not flattened:
keywords = chunk.metadata.get("keywords", [])
summary = chunk.metadata.get("summary")
keywords = chunk.metadata.keywords or []
summary = chunk.metadata.summary
if keywords:
print(f" Keywords: {', '.join(keywords)}")
if summary:
Expand Down Expand Up @@ -306,6 +320,20 @@ for tbl in result.table_chunks:
tbl.save("./output/tables/") # writes HTML file to disk
```

### PageChunk

Page chunks are returned by the v2 page-memory API. Their `content` is the
page-level summary, and `metadata.page_nums` gives the citation pages.

```python
for page in result.page_chunks:
pages = page.metadata.page_nums or []
print(page.content_source) # "summary"
print(page.content)
print(pages)
print(page.metadata.entities)
```

---

## Step-by-Step Control (Jobs API)
Expand Down Expand Up @@ -412,6 +440,8 @@ The poller uses adaptive backoff: it starts at `poll_interval` and gradually inc
result = client.jobs.load(job_result)
# or pass a URL directly:
result = client.jobs.load("https://storage.example.com/result.zip")
# or resolve a job id before loading:
result = client.jobs.load("job_123")
```

---
Expand Down Expand Up @@ -470,6 +500,7 @@ retryable `ConflictError` using the server error code `ABORTED`.
response = client.retrieval.query(
namespace="support-center",
query="How do I pair a Bluetooth headset?",
chunk_types=["page"],
top_k=5,
)

Expand All @@ -486,12 +517,14 @@ print(response.evidence_text) # rendered evidence context, when returned
print(response.stop_reason) # agentic termination reason, when returned
print(response.failure_reason) # no-answer reason, when returned
for ref in response.referenced_chunks:
print(ref.chunk_id, ref.document_id, ref.chunk_type)
print(ref.section_path, ref.file_path, ref.job_id, ref.asset_url)
print(ref.chunk_id, ref.document_id, ref.chunk_type, ref.content_source)
print(ref.section_path, ref.file_path, ref.job_id, ref.asset_url, ref.metadata)

# Legacy results are always available
for result in response.results:
print(result.chunk_id)
print(result.content)
print(result.content_source)
print(result.score)
print(result.source.document_id)
print(result.source.source_file_name)
Expand All @@ -501,6 +534,8 @@ for result in response.results:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `use_agentic` | `bool \| None` | `None` | Force agentic (`True`) or legacy (`False`) retrieval. `None` uses server default. |
| `chunk_types` | `list["text" \| "image" \| "table" \| "page"] \| None` | `None` | Restrict retrieval to the selected chunk types. |
| `data_type` | `int \| None` | `None` | Deprecated server selector; `7` maps to page chunks and `8` maps to text+image+table. Prefer `chunk_types`. |

Retrieval results expose `content`, not the older parse-result `text` field.
Media results may include `asset_url` when the server can sign the referenced
Expand All @@ -510,9 +545,12 @@ Each retrieval result uses one canonical source reference shape:

```python
result.content
result.chunk_id # Optional[str]
result.chunk_type
result.content_source
result.score
result.asset_url # Optional[str]
result.metadata # Optional[dict]
result.source.document_id
result.source.source_file_name
result.source.section_path
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "knowhere-python-sdk"
version = "0.6.0"
version = "2.0.0"
description = "Official Python SDK for the Knowhere document parsing API"
readme = "README.md"
license = "MIT"
Expand Down Expand Up @@ -32,7 +32,7 @@ dependencies = [

[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest>=7.0.0,<9.0.0",
"pytest-asyncio>=0.23.0",
"respx>=0.21.0",
"ruff>=0.1.0",
Expand Down
4 changes: 4 additions & 0 deletions src/knowhere/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from knowhere.types.params import ParsingParams, WebhookConfig
from knowhere.types.retrieval import (
RetrievalChannel,
RetrievalChunkType,
RetrievalFilterMode,
RetrievalReferencedChunk,
RetrievalSectionExclusion,
Expand All @@ -64,6 +65,7 @@
ImageChunk,
ImageFileInfo,
Manifest,
PageChunk,
ParseResult,
ProcessingCost,
ProcessingMetadata,
Expand Down Expand Up @@ -117,6 +119,7 @@
"DocumentListResponse",
# Retrieval types
"RetrievalChannel",
"RetrievalChunkType",
"RetrievalFilterMode",
"RetrievalReferencedChunk",
"RetrievalSectionExclusion",
Expand All @@ -126,6 +129,7 @@
# Result types
"ParseResult",
"Manifest",
"PageChunk",
"Statistics",
"Checksum",
"FileIndex",
Expand Down
Loading
Loading