Fix Serve API contract: send to_formats as repeated form fields

Docling Serve expects array fields (to_formats) as repeated multipart
keys (to_formats=md&to_formats=html&to_formats=json), not a JSON
string. Changed _build_form_data to return list[tuple] so httpx sends
repeated keys correctly. Fixes 422 Unprocessable Entity on convert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Pier-Jean Malandrino 2026-03-31 15:27:21 +02:00
parent 001cf6807c
commit 1b8cfe0a6b
2 changed files with 8 additions and 8 deletions

View file

@ -108,14 +108,14 @@ class ServeConverter:
return False
def _build_form_data(options: ConversionOptions) -> dict[str, str]:
"""Build individual form fields matching Docling Serve's FormDepends pattern.
def _build_form_data(options: ConversionOptions) -> dict[str, str | list[str]]:
"""Build form fields matching Docling Serve's multipart form contract.
Docling Serve uses FormDepends to flatten ConvertDocumentsRequestOptions
into individual form fields (not a JSON blob).
Array fields (to_formats) are sent as lists httpx encodes them as
repeated form keys (to_formats=md&to_formats=html&to_formats=json).
"""
return {
"to_formats": '["md","html","json"]',
"to_formats": ["md", "html", "json"],
"do_ocr": str(options.do_ocr).lower(),
"do_table_structure": str(options.do_table_structure).lower(),
"table_mode": options.table_mode,

View file

@ -32,7 +32,7 @@ class TestBuildFormData:
assert data["do_picture_description"] == "false"
assert data["include_images"] == "false"
assert data["images_scale"] == "1.0"
assert '"json"' in data["to_formats"]
assert set(data["to_formats"]) == {"md", "html", "json"}
def test_custom_options(self):
opts = ConversionOptions(
@ -299,11 +299,11 @@ class TestServeConverterConvert:
assert len(result.pages[0].elements) == 1
assert result.pages[0].elements[0].type == "title"
# Verify form fields sent individually (not as JSON blob)
# Verify form fields sent as dict with list for repeated keys
call_kwargs = mock_client.post.call_args
sent_data = call_kwargs.kwargs.get("data", {})
assert "do_ocr" in sent_data
assert sent_data["do_ocr"] == "true"
assert set(sent_data["to_formats"]) == {"md", "html", "json"}
@pytest.mark.asyncio
async def test_http_error_raises(self, tmp_path):