Skip to content
Open
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
13 changes: 9 additions & 4 deletions src/landingai_ade/_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import os
import json
import importlib.metadata
from typing import TYPE_CHECKING, Any, Dict, Union, Mapping, Iterable, Optional, cast
from pathlib import Path
Expand Down Expand Up @@ -796,7 +797,7 @@ def section(
def split(
self,
*,
split_class: Iterable[client_split_params.SplitClass],
split_class: Union[str, Iterable[client_split_params.SplitClass]],
markdown: Union[FileTypes, str, None] | Omit = omit,
markdown_url: Optional[str] | Omit = omit,
model: Optional[str] | Omit = omit,
Expand Down Expand Up @@ -846,7 +847,9 @@ def split(
original_markdown_url = markdown_url
body = deepcopy_with_paths(
{
"split_class": split_class,
# The API expects split_class as a single JSON string form field,
# not flattened multipart fields (https://github.com/landing-ai/ade-python/issues/76).
"split_class": split_class if isinstance(split_class, str) else json.dumps(list(split_class)),
"markdown": markdown,
"markdown_url": markdown_url,
"model": model,
Expand Down Expand Up @@ -1513,7 +1516,7 @@ async def section(
async def split(
self,
*,
split_class: Iterable[client_split_params.SplitClass],
split_class: Union[str, Iterable[client_split_params.SplitClass]],
markdown: Union[FileTypes, str, None] | Omit = omit,
markdown_url: Optional[str] | Omit = omit,
model: Optional[str] | Omit = omit,
Expand Down Expand Up @@ -1563,7 +1566,9 @@ async def split(
original_markdown_url = markdown_url
body = deepcopy_with_paths(
{
"split_class": split_class,
# The API expects split_class as a single JSON string form field,
# not flattened multipart fields (https://github.com/landing-ai/ade-python/issues/76).
"split_class": split_class if isinstance(split_class, str) else json.dumps(list(split_class)),
"markdown": markdown,
"markdown_url": markdown_url,
"model": model,
Expand Down
2 changes: 1 addition & 1 deletion src/landingai_ade/types/client_split_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


class ClientSplitParams(TypedDict, total=False):
split_class: Required[Iterable[SplitClass]]
split_class: Required[Union[str, Iterable[SplitClass]]]
"""List of split classification options/configuration.

Can be provided as JSON string in form data.
Expand Down
57 changes: 57 additions & 0 deletions tests/test_split_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Regression tests for https://github.com/landing-ai/ade-python/issues/76:
# split_class must be sent as a single JSON string form field, not flattened
# into split_class[0][name]-style multipart fields.
from __future__ import annotations

import json
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from landingai_ade import LandingAIADE, AsyncLandingAIADE

SPLIT_CLASS = [
{"name": "Bank Statement", "description": "Bank account activity summary."},
{"name": "Pay Stub", "description": "Employee earnings detail.", "identifier": "Pay Stub Date"},
]


def test_sync_split_class_sent_as_json_string() -> None:
with LandingAIADE(apikey="test-key", base_url="http://localhost") as client:
with patch.object(client, "post", return_value=MagicMock()) as post:
client.split(split_class=SPLIT_CLASS, markdown="some markdown")

body = post.call_args.kwargs["body"]
assert body["split_class"] == json.dumps(SPLIT_CLASS)
assert json.loads(body["split_class"]) == SPLIT_CLASS


def test_sync_split_class_json_string_passed_through() -> None:
# An already JSON-encoded split_class (documented in the README) must not be re-serialized.
encoded = json.dumps(SPLIT_CLASS)
with LandingAIADE(apikey="test-key", base_url="http://localhost") as client:
with patch.object(client, "post", return_value=MagicMock()) as post:
client.split(split_class=encoded, markdown="some markdown")

assert post.call_args.kwargs["body"]["split_class"] == encoded


@pytest.mark.asyncio
async def test_async_split_class_json_string_passed_through() -> None:
encoded = json.dumps(SPLIT_CLASS)
async with AsyncLandingAIADE(apikey="test-key", base_url="http://localhost") as client:
with patch.object(client, "post", new_callable=AsyncMock, return_value=MagicMock()) as post:
await client.split(split_class=encoded, markdown="some markdown")

assert post.call_args.kwargs["body"]["split_class"] == encoded


@pytest.mark.asyncio
async def test_async_split_class_sent_as_json_string() -> None:
async with AsyncLandingAIADE(apikey="test-key", base_url="http://localhost") as client:
with patch.object(client, "post", new_callable=AsyncMock, return_value=MagicMock()) as post:
await client.split(split_class=SPLIT_CLASS, markdown="some markdown")

body = post.call_args.kwargs["body"]
assert body["split_class"] == json.dumps(SPLIT_CLASS)
assert json.loads(body["split_class"]) == SPLIT_CLASS