Merge pull request #323 from ggozad/fix/docling-serve-local-parity

Fix docling-serve silent failure detection and local chunker table header parity
This commit is contained in:
Yiorgis Gozadinos 2026-03-26 14:10:53 +02:00 committed by GitHub
commit ab80b7366f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 89320 additions and 83678 deletions

View file

@ -9,6 +9,8 @@
### Fixed
- **Generated skill packages**: Include SKILL.md and assets in wheel distributions. Add README to generated packages.
- **Docling-serve chunker**: Detect per-document failure status that was silently returning 0 chunks when the task-level status was "success" but individual documents failed
- **Docling local chunker**: Re-enable `repeat_table_header` for self-contained table chunks, improving retrieval quality and matching docling-serve behavior
## [0.35.1] - 2026-03-24

View file

@ -83,9 +83,6 @@ class DoclingLocalChunker(DocumentChunker):
tokenizer=tokenizer,
merge_peers=config.processing.chunking_merge_peers,
serializer_provider=serializer_provider,
# Context expansion reconstructs full tables, so repeating
# headers in each chunk is redundant and inflates chunk count.
repeat_table_header=False,
)
elif self.chunker_type == "hierarchical":
serializer_provider = _create_markdown_serializer_provider(

View file

@ -114,6 +114,14 @@ class DoclingServeChunker(DocumentChunker):
name="document",
)
# Task-level polling status can be "success" while individual documents
# report "failure" (e.g. schema version mismatch), returning 0 chunks silently.
documents = result.get("documents", [])
for doc_result in documents:
if doc_result.get("status") not in ("success", "partial_success", None):
errors = doc_result.get("errors", [])
raise ValueError(f"Chunking failed: {errors}")
return result.get("chunks", [])
async def chunk(self, document: "DoclingDocument") -> list[Chunk]:

File diff suppressed because one or more lines are too long

View file

@ -1,14 +1,15 @@
interactions:
- request:
body: "--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data; name=\"to_formats\"\r\n\r\njson\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition:
form-data; name=\"do_ocr\"\r\n\r\ntrue\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data; name=\"force_ocr\"\r\n\r\nfalse\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition:
form-data; name=\"do_table_structure\"\r\n\r\ntrue\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data;
name=\"table_mode\"\r\n\r\naccurate\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data; name=\"table_cell_matching\"\r\n\r\ntrue\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition:
form-data; name=\"images_scale\"\r\n\r\n2.0\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data;
name=\"generate_picture_images\"\r\n\r\nfalse\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data;
name=\"do_picture_description\"\r\n\r\nfalse\r\n--bbed9c660a5a644cfb155b3bf58816e1\r\nContent-Disposition: form-data;
name=\"files\"; filename=\"tmpu51qinmn.md\"\r\nContent-Type: text/markdown\r\n\r\n```python\ndef test():\n return
42\n```\r\n--bbed9c660a5a644cfb155b3bf58816e1--\r\n"
body: "--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data; name=\"to_formats\"\r\n\r\njson\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition:
form-data; name=\"do_ocr\"\r\n\r\ntrue\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data; name=\"force_ocr\"\r\n\r\nfalse\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition:
form-data; name=\"ocr_engine\"\r\n\r\nauto\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data;
name=\"do_table_structure\"\r\n\r\ntrue\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data; name=\"table_mode\"\r\n\r\naccurate\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition:
form-data; name=\"table_cell_matching\"\r\n\r\ntrue\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data;
name=\"images_scale\"\r\n\r\n2.0\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data; name=\"image_export_mode\"\r\n\r\nembedded\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition:
form-data; name=\"include_images\"\r\n\r\nfalse\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data;
name=\"do_picture_description\"\r\n\r\nfalse\r\n--b484380e1e52463dfd1441de1832f57a\r\nContent-Disposition: form-data;
name=\"files\"; filename=\"tmpx3posbtx.md\"\r\nContent-Type: text/markdown\r\n\r\n```python\ndef test():\n return
42\n```\r\n--b484380e1e52463dfd1441de1832f57a--\r\n"
headers:
accept:
- '*/*'
@ -17,9 +18,9 @@ interactions:
connection:
- keep-alive
content-length:
- '1119'
- '1311'
content-type:
- multipart/form-data; boundary=bbed9c660a5a644cfb155b3bf58816e1
- multipart/form-data; boundary=b484380e1e52463dfd1441de1832f57a
host:
- localhost:5001
method: POST
@ -27,11 +28,12 @@ interactions:
response:
headers:
content-length:
- '131'
- '152'
content-type:
- application/json
parsed_body:
task_id: 888b2bcb-b29e-475d-ab3d-465128a3e628
error_message: null
task_id: 5e82f2a0-0775-4d1b-b204-7de29b2b4a1a
task_meta: null
task_position: 1
task_status: pending
@ -51,15 +53,16 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/888b2bcb-b29e-475d-ab3d-465128a3e628
uri: http://localhost:5001/v1/status/poll/5e82f2a0-0775-4d1b-b204-7de29b2b4a1a
response:
headers:
content-length:
- '134'
- '155'
content-type:
- application/json
parsed_body:
task_id: 888b2bcb-b29e-475d-ab3d-465128a3e628
error_message: null
task_id: 5e82f2a0-0775-4d1b-b204-7de29b2b4a1a
task_meta: null
task_position: null
task_status: started
@ -79,71 +82,16 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/888b2bcb-b29e-475d-ab3d-465128a3e628
uri: http://localhost:5001/v1/status/poll/5e82f2a0-0775-4d1b-b204-7de29b2b4a1a
response:
headers:
content-length:
- '134'
- '155'
content-type:
- application/json
parsed_body:
task_id: 888b2bcb-b29e-475d-ab3d-465128a3e628
task_meta: null
task_position: null
task_status: started
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/888b2bcb-b29e-475d-ab3d-465128a3e628
response:
headers:
content-length:
- '134'
content-type:
- application/json
parsed_body:
task_id: 888b2bcb-b29e-475d-ab3d-465128a3e628
task_meta: null
task_position: null
task_status: started
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/888b2bcb-b29e-475d-ab3d-465128a3e628
response:
headers:
content-length:
- '134'
content-type:
- application/json
parsed_body:
task_id: 888b2bcb-b29e-475d-ab3d-465128a3e628
error_message: null
task_id: 5e82f2a0-0775-4d1b-b204-7de29b2b4a1a
task_meta: null
task_position: null
task_status: success
@ -163,7 +111,7 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/result/888b2bcb-b29e-475d-ab3d-465128a3e628
uri: http://localhost:5001/v1/result/5e82f2a0-0775-4d1b-b204-7de29b2b4a1a
response:
headers:
content-length:
@ -173,7 +121,7 @@ interactions:
parsed_body:
document:
doctags_content: null
filename: tmpu51qinmn.md
filename: tmpx3posbtx.md
html_content: null
json_content:
body:
@ -196,10 +144,10 @@ interactions:
self_ref: '#/furniture'
groups: []
key_value_items: []
name: tmpu51qinmn
name: tmpx3posbtx
origin:
binary_hash: 9008975733065065710
filename: tmpu51qinmn.md
filename: tmpx3posbtx.md
mimetype: text/markdown
uri: null
pages: {}
@ -228,11 +176,11 @@ interactions:
text: |-
def test():
return 42
version: 1.9.0
version: 1.10.0
md_content: null
text_content: null
errors: []
processing_time: 0.0015285420013242401
processing_time: 0.001853875000961125
status: success
timings: {}
status:

View file

@ -1,13 +1,14 @@
interactions:
- request:
body: "--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data; name=\"to_formats\"\r\n\r\njson\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition:
form-data; name=\"do_ocr\"\r\n\r\ntrue\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data; name=\"force_ocr\"\r\n\r\nfalse\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition:
form-data; name=\"do_table_structure\"\r\n\r\ntrue\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data;
name=\"table_mode\"\r\n\r\naccurate\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data; name=\"table_cell_matching\"\r\n\r\ntrue\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition:
form-data; name=\"images_scale\"\r\n\r\n2.0\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data;
name=\"generate_picture_images\"\r\n\r\nfalse\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data;
name=\"do_picture_description\"\r\n\r\nfalse\r\n--0e26cc5ea1ada7c3725080067973ef53\r\nContent-Disposition: form-data;
name=\"files\"; filename=\"content.md\"\r\nContent-Type: text/markdown\r\n\r\n# Test Document\n\nThis is a test.\r\n--0e26cc5ea1ada7c3725080067973ef53--\r\n"
body: "--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data; name=\"to_formats\"\r\n\r\njson\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition:
form-data; name=\"do_ocr\"\r\n\r\ntrue\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data; name=\"force_ocr\"\r\n\r\nfalse\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition:
form-data; name=\"ocr_engine\"\r\n\r\nauto\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data;
name=\"do_table_structure\"\r\n\r\ntrue\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data; name=\"table_mode\"\r\n\r\naccurate\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition:
form-data; name=\"table_cell_matching\"\r\n\r\ntrue\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data;
name=\"images_scale\"\r\n\r\n2.0\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data; name=\"image_export_mode\"\r\n\r\nembedded\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition:
form-data; name=\"include_images\"\r\n\r\nfalse\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data;
name=\"do_picture_description\"\r\n\r\nfalse\r\n--b76818aa08785867d7feea5e663b3613\r\nContent-Disposition: form-data;
name=\"files\"; filename=\"content.md\"\r\nContent-Type: text/markdown\r\n\r\n# Test Document\n\nThis is a test.\r\n--b76818aa08785867d7feea5e663b3613--\r\n"
headers:
accept:
- '*/*'
@ -16,9 +17,9 @@ interactions:
connection:
- keep-alive
content-length:
- '1108'
- '1300'
content-type:
- multipart/form-data; boundary=0e26cc5ea1ada7c3725080067973ef53
- multipart/form-data; boundary=b76818aa08785867d7feea5e663b3613
host:
- localhost:5001
method: POST
@ -26,11 +27,70 @@ interactions:
response:
headers:
content-length:
- '131'
- '152'
content-type:
- application/json
parsed_body:
task_id: d9b46213-a4a6-4465-bd22-b98a5d2285c0
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 2
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 2
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
@ -50,18 +110,19 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/d9b46213-a4a6-4465-bd22-b98a5d2285c0
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '134'
- '152'
content-type:
- application/json
parsed_body:
task_id: d9b46213-a4a6-4465-bd22-b98a5d2285c0
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: null
task_status: started
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
@ -78,18 +139,19 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/d9b46213-a4a6-4465-bd22-b98a5d2285c0
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '134'
- '152'
content-type:
- application/json
parsed_body:
task_id: d9b46213-a4a6-4465-bd22-b98a5d2285c0
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: null
task_status: started
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
@ -106,18 +168,19 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/d9b46213-a4a6-4465-bd22-b98a5d2285c0
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '134'
- '152'
content-type:
- application/json
parsed_body:
task_id: d9b46213-a4a6-4465-bd22-b98a5d2285c0
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: null
task_status: started
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
@ -134,15 +197,219 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/d9b46213-a4a6-4465-bd22-b98a5d2285c0
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '134'
- '152'
content-type:
- application/json
parsed_body:
task_id: d9b46213-a4a6-4465-bd22-b98a5d2285c0
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '152'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: 1
task_status: pending
task_type: convert
status:
code: 200
message: OK
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate, zstd
connection:
- keep-alive
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/status/poll/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '155'
content-type:
- application/json
parsed_body:
error_message: null
task_id: 1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
task_meta: null
task_position: null
task_status: success
@ -162,11 +429,11 @@ interactions:
host:
- localhost:5001
method: GET
uri: http://localhost:5001/v1/result/d9b46213-a4a6-4465-bd22-b98a5d2285c0
uri: http://localhost:5001/v1/result/1ad62ce1-5260-4fdb-aab7-e17f7adc8ee5
response:
headers:
content-length:
- '1225'
- '1226'
content-type:
- application/json
parsed_body:
@ -231,11 +498,11 @@ interactions:
prov: []
self_ref: '#/texts/1'
text: This is a test.
version: 1.9.0
version: 1.10.0
md_content: null
text_content: null
errors: []
processing_time: 0.003054707995033823
processing_time: 0.003092042010393925
status: success
timings: {}
status:

View file

@ -464,6 +464,62 @@ class TestDoclingServeChunker:
with pytest.raises(ValueError, match="Authentication failed"):
await chunker.chunk(doc)
@pytest.mark.asyncio
@patch("haiku.rag.providers.docling_serve.httpx.AsyncClient")
async def test_chunk_document_failure_status(self, mock_client_class, chunker):
"""Test that document-level failure status raises ValueError."""
result_data = {
"chunks": [],
"documents": [
{
"kind": "ExportResult",
"status": "failure",
"errors": ["Schema version mismatch"],
}
],
}
submit_resp, poll_resp, result_resp = create_async_workflow_mocks(result_data)
mock_client = AsyncMock()
mock_client.post = AsyncMock(return_value=submit_resp)
mock_client.get = AsyncMock(side_effect=[poll_resp, result_resp])
mock_client_class.return_value.__aenter__.return_value = mock_client
converter = get_converter(Config)
doc = await converter.convert_text("# Test", name="test.md")
with pytest.raises(ValueError, match="Chunking failed"):
await chunker.chunk(doc)
@pytest.mark.asyncio
@patch("haiku.rag.providers.docling_serve.httpx.AsyncClient")
async def test_chunk_document_success_empty_chunks(
self, mock_client_class, chunker
):
"""Test that successful status with empty chunks returns empty list."""
result_data = {
"chunks": [],
"documents": [
{
"kind": "ExportResult",
"status": "success",
"errors": [],
}
],
}
submit_resp, poll_resp, result_resp = create_async_workflow_mocks(result_data)
mock_client = AsyncMock()
mock_client.post = AsyncMock(return_value=submit_resp)
mock_client.get = AsyncMock(side_effect=[poll_resp, result_resp])
mock_client_class.return_value.__aenter__.return_value = mock_client
converter = get_converter(Config)
doc = await converter.convert_text("# Test", name="test.md")
chunks = await chunker.chunk(doc)
assert chunks == []
@pytest.mark.asyncio
@patch("haiku.rag.providers.docling_serve.httpx.AsyncClient")
async def test_chunk_metadata_extraction(self, mock_client_class, chunker):