diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2a8f4ff..3e9af1b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.0" + ".": "1.4.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index f861e68..07391e1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/landingai%2Fade-a5f82ed3c3a3d3b2ea6a370df423e103e5987fd841320f88d2c1614849e30f58.yml -openapi_spec_hash: 000ad390d642c1cf8cb38754fdf8b459 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/landingai%2Fade-1dceb36dc610007b96837b83f328773f4b13de111a870c838c31d63c9c3c4e98.yml +openapi_spec_hash: c3e0e1244fa711102a91734ce99e1ff6 config_hash: 43f45ffa1cb556d90dcef6d742ed239f diff --git a/CHANGELOG.md b/CHANGELOG.md index c0737c2..a0bf520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 1.4.0 (2026-01-06) + +Full Changelog: [v1.3.0...v1.4.0](https://github.com/landing-ai/ade-python/compare/v1.3.0...v1.4.0) + +### Features + +* **api:** api update ([7fda941](https://github.com/landing-ai/ade-python/commit/7fda941b4cbbbe00e583877013e59148c8fbb5e4)) +* **files:** add support for string alternative to file upload type ([57ae8fb](https://github.com/landing-ai/ade-python/commit/57ae8fb081febb893ee7801836ccd3a725185559)) + + +### Bug Fixes + +* use async_to_httpx_files in patch method ([977143a](https://github.com/landing-ai/ade-python/commit/977143a0435f66a04314b92eab54a2145f1776ad)) + + +### Chores + +* **internal:** add `--fix` argument to lint script ([caa1bc4](https://github.com/landing-ai/ade-python/commit/caa1bc4030bc88621f898815f3c24060bbc32bf2)) +* **internal:** codegen related update ([9a57490](https://github.com/landing-ai/ade-python/commit/9a57490dad6d9bb91e30cb6b476ead94aafce9d0)) +* speedup initial import ([8e2a9f1](https://github.com/landing-ai/ade-python/commit/8e2a9f1b75dc7fca85fab1fb4968ccdd4da204ac)) +* speedup initial import ([0b024ed](https://github.com/landing-ai/ade-python/commit/0b024ed6f2ff7fd27cb08ab1e1afeee3b3f842bf)) + + +### Documentation + +* prominently feature MCP server setup in root SDK readmes ([bb75fea](https://github.com/landing-ai/ade-python/commit/bb75fea795f468e8a215f83626ca660e1ec28d54)) + ## 1.3.0 (2025-12-16) Full Changelog: [v1.2.0...v1.3.0](https://github.com/landing-ai/ade-python/compare/v1.2.0...v1.3.0) diff --git a/LICENSE b/LICENSE index c2602a3..82f6e89 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 LandingAI ADE + Copyright 2026 LandingAI ADE Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index d0f87b2..376b2f2 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,15 @@ A Python library for interacting with the **LandingAI Agentic Document Extractio * 🧩 Schema-based data extraction * 🔌 Pluggable HTTP backends (`httpx` or `aiohttp`) +## MCP Server + +Use the LandingAI ADE MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. + +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=landingai-ade-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImxhbmRpbmdhaS1hZGUtbWNwIl19) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22landingai-ade-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22landingai-ade-mcp%22%5D%7D) + +> Note: You may need to set environment variables in your MCP client. + ## Documentation The REST API documentation can be found on [docs.landing.ai](https://docs.landing.ai/). The full API of this library can be found in [api.md](api.md). diff --git a/pyproject.toml b/pyproject.toml index c9bf0fc..0d165d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "landingai-ade" -version = "1.3.0" +version = "1.4.0" description = "The official Python library for the landingai-ade API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/scripts/lint b/scripts/lint index 89971db..b06ff83 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,8 +4,13 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running lints" -rye run lint +if [ "$1" = "--fix" ]; then + echo "==> Running lints with --fix" + rye run fix:ruff +else + echo "==> Running lints" + rye run lint +fi echo "==> Making sure it imports" rye run python -c 'import landingai_ade' diff --git a/src/landingai_ade/_base_client.py b/src/landingai_ade/_base_client.py index c60dd90..485e121 100644 --- a/src/landingai_ade/_base_client.py +++ b/src/landingai_ade/_base_client.py @@ -1774,7 +1774,7 @@ async def patch( options: RequestOptions = {}, ) -> ResponseT: opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) diff --git a/src/landingai_ade/_client.py b/src/landingai_ade/_client.py index fcb365f..0c701e0 100644 --- a/src/landingai_ade/_client.py +++ b/src/landingai_ade/_client.py @@ -4,7 +4,7 @@ import os import importlib.metadata -from typing import Any, Dict, Mapping, Iterable, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, Union, Mapping, Iterable, Optional, cast from typing_extensions import Self, Literal, override import httpx @@ -34,6 +34,7 @@ get_async_library, async_maybe_transform, ) +from ._compat import cached_property from ._version import __version__ from ._response import ( to_raw_response_wrapper, @@ -41,7 +42,6 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .resources import parse_jobs from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError, LandingAiadeError from ._base_client import ( @@ -55,6 +55,9 @@ from .types.split_response import SplitResponse from .types.extract_response import ExtractResponse +if TYPE_CHECKING: + from .resources import parse_jobs + from .resources.parse_jobs import ParseJobsResource, AsyncParseJobsResource _LIB_VERSION = importlib.metadata.version("landingai-ade") __all__ = [ @@ -76,10 +79,6 @@ class LandingAIADE(SyncAPIClient): - parse_jobs: parse_jobs.ParseJobsResource - with_raw_response: LandingAIADEWithRawResponse - with_streaming_response: LandingAIADEWithStreamedResponse - # client options apikey: str @@ -158,9 +157,19 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.parse_jobs = parse_jobs.ParseJobsResource(self) - self.with_raw_response = LandingAIADEWithRawResponse(self) - self.with_streaming_response = LandingAIADEWithStreamedResponse(self) + @cached_property + def parse_jobs(self) -> ParseJobsResource: + from .resources.parse_jobs import ParseJobsResource + + return ParseJobsResource(self) + + @cached_property + def with_raw_response(self) -> LandingAIADEWithRawResponse: + return LandingAIADEWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LandingAIADEWithStreamedResponse: + return LandingAIADEWithStreamedResponse(self) @property @override @@ -239,7 +248,7 @@ def extract( self, *, schema: str, - markdown: Optional[FileTypes] | Omit = omit, + markdown: Union[FileTypes, str, None] | Omit = omit, markdown_url: Optional[str] | Omit = omit, model: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -396,7 +405,7 @@ def split( self, *, split_class: Iterable[client_split_params.SplitClass], - markdown: Optional[FileTypes] | Omit = omit, + markdown: Union[FileTypes, str, None] | Omit = omit, markdown_url: Optional[str] | Omit = omit, model: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -492,10 +501,6 @@ def _make_status_error( class AsyncLandingAIADE(AsyncAPIClient): - parse_jobs: parse_jobs.AsyncParseJobsResource - with_raw_response: AsyncLandingAIADEWithRawResponse - with_streaming_response: AsyncLandingAIADEWithStreamedResponse - # client options apikey: str @@ -574,9 +579,19 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.parse_jobs = parse_jobs.AsyncParseJobsResource(self) - self.with_raw_response = AsyncLandingAIADEWithRawResponse(self) - self.with_streaming_response = AsyncLandingAIADEWithStreamedResponse(self) + @cached_property + def parse_jobs(self) -> AsyncParseJobsResource: + from .resources.parse_jobs import AsyncParseJobsResource + + return AsyncParseJobsResource(self) + + @cached_property + def with_raw_response(self) -> AsyncLandingAIADEWithRawResponse: + return AsyncLandingAIADEWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLandingAIADEWithStreamedResponse: + return AsyncLandingAIADEWithStreamedResponse(self) @property @override @@ -655,7 +670,7 @@ async def extract( self, *, schema: str, - markdown: Optional[FileTypes] | Omit = omit, + markdown: Union[FileTypes, str, None] | Omit = omit, markdown_url: Optional[str] | Omit = omit, model: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -812,7 +827,7 @@ async def split( self, *, split_class: Iterable[client_split_params.SplitClass], - markdown: Optional[FileTypes] | Omit = omit, + markdown: Union[FileTypes, str, None] | Omit = omit, markdown_url: Optional[str] | Omit = omit, model: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -908,8 +923,10 @@ def _make_status_error( class LandingAIADEWithRawResponse: + _client: LandingAIADE + def __init__(self, client: LandingAIADE) -> None: - self.parse_jobs = parse_jobs.ParseJobsResourceWithRawResponse(client.parse_jobs) + self._client = client self.extract = to_raw_response_wrapper( client.extract, @@ -921,10 +938,18 @@ def __init__(self, client: LandingAIADE) -> None: client.split, ) + @cached_property + def parse_jobs(self) -> parse_jobs.ParseJobsResourceWithRawResponse: + from .resources.parse_jobs import ParseJobsResourceWithRawResponse + + return ParseJobsResourceWithRawResponse(self._client.parse_jobs) + class AsyncLandingAIADEWithRawResponse: + _client: AsyncLandingAIADE + def __init__(self, client: AsyncLandingAIADE) -> None: - self.parse_jobs = parse_jobs.AsyncParseJobsResourceWithRawResponse(client.parse_jobs) + self._client = client self.extract = async_to_raw_response_wrapper( client.extract, @@ -936,10 +961,18 @@ def __init__(self, client: AsyncLandingAIADE) -> None: client.split, ) + @cached_property + def parse_jobs(self) -> parse_jobs.AsyncParseJobsResourceWithRawResponse: + from .resources.parse_jobs import AsyncParseJobsResourceWithRawResponse + + return AsyncParseJobsResourceWithRawResponse(self._client.parse_jobs) + class LandingAIADEWithStreamedResponse: + _client: LandingAIADE + def __init__(self, client: LandingAIADE) -> None: - self.parse_jobs = parse_jobs.ParseJobsResourceWithStreamingResponse(client.parse_jobs) + self._client = client self.extract = to_streamed_response_wrapper( client.extract, @@ -951,10 +984,18 @@ def __init__(self, client: LandingAIADE) -> None: client.split, ) + @cached_property + def parse_jobs(self) -> parse_jobs.ParseJobsResourceWithStreamingResponse: + from .resources.parse_jobs import ParseJobsResourceWithStreamingResponse + + return ParseJobsResourceWithStreamingResponse(self._client.parse_jobs) + class AsyncLandingAIADEWithStreamedResponse: + _client: AsyncLandingAIADE + def __init__(self, client: AsyncLandingAIADE) -> None: - self.parse_jobs = parse_jobs.AsyncParseJobsResourceWithStreamingResponse(client.parse_jobs) + self._client = client self.extract = async_to_streamed_response_wrapper( client.extract, @@ -966,6 +1007,12 @@ def __init__(self, client: AsyncLandingAIADE) -> None: client.split, ) + @cached_property + def parse_jobs(self) -> parse_jobs.AsyncParseJobsResourceWithStreamingResponse: + from .resources.parse_jobs import AsyncParseJobsResourceWithStreamingResponse + + return AsyncParseJobsResourceWithStreamingResponse(self._client.parse_jobs) + Client = LandingAIADE diff --git a/src/landingai_ade/_version.py b/src/landingai_ade/_version.py index c8f6894..f87443c 100644 --- a/src/landingai_ade/_version.py +++ b/src/landingai_ade/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "landingai_ade" -__version__ = "1.3.0" # x-release-please-version +__version__ = "1.4.0" # x-release-please-version diff --git a/src/landingai_ade/types/client_extract_params.py b/src/landingai_ade/types/client_extract_params.py index a93dde2..02bc817 100644 --- a/src/landingai_ade/types/client_extract_params.py +++ b/src/landingai_ade/types/client_extract_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Union, Optional from typing_extensions import Required, TypedDict from .._types import FileTypes @@ -19,7 +19,7 @@ class ClientExtractParams(TypedDict, total=False): the document. """ - markdown: Optional[FileTypes] + markdown: Union[FileTypes, str, None] """The Markdown file or Markdown content to extract data from.""" markdown_url: Optional[str] diff --git a/src/landingai_ade/types/client_split_params.py b/src/landingai_ade/types/client_split_params.py index 4345672..50c3f5d 100644 --- a/src/landingai_ade/types/client_split_params.py +++ b/src/landingai_ade/types/client_split_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Required, Annotated, TypedDict from .._types import FileTypes @@ -18,7 +18,7 @@ class ClientSplitParams(TypedDict, total=False): Can be provided as JSON string in form data. """ - markdown: Optional[FileTypes] + markdown: Union[FileTypes, str, None] """The Markdown file or Markdown content to split.""" markdown_url: Annotated[Optional[str], PropertyInfo(alias="markdownUrl")] diff --git a/src/landingai_ade/types/parse_job_get_response.py b/src/landingai_ade/types/parse_job_get_response.py index f03fc74..2ec6b0d 100644 --- a/src/landingai_ade/types/parse_job_get_response.py +++ b/src/landingai_ade/types/parse_job_get_response.py @@ -17,6 +17,9 @@ "DataParseResponseChunkGrounding", "DataParseResponseSplit", "DataParseResponseGrounding", + "DataParseResponseGroundingParseResponseGrounding", + "DataParseResponseGroundingParseResponseTableCellGrounding", + "DataParseResponseGroundingParseResponseTableCellGroundingPosition", "DataSpreadsheetParseResponse", "DataSpreadsheetParseResponseChunk", "DataSpreadsheetParseResponseChunkGrounding", @@ -53,7 +56,7 @@ class DataParseResponseSplit(BaseModel): pages: List[int] -class DataParseResponseGrounding(BaseModel): +class DataParseResponseGroundingParseResponseGrounding(BaseModel): box: ParseGroundingBox page: int @@ -78,6 +81,50 @@ class DataParseResponseGrounding(BaseModel): ] +class DataParseResponseGroundingParseResponseTableCellGroundingPosition(BaseModel): + chunk_id: str + + col: int + + colspan: int + + row: int + + rowspan: int + + +class DataParseResponseGroundingParseResponseTableCellGrounding(BaseModel): + box: ParseGroundingBox + + page: int + + type: Literal[ + "chunkLogo", + "chunkCard", + "chunkAttestation", + "chunkScanCode", + "chunkForm", + "chunkTable", + "chunkFigure", + "chunkText", + "chunkMarginalia", + "chunkTitle", + "chunkPageHeader", + "chunkPageFooter", + "chunkPageNumber", + "chunkKeyValue", + "table", + "tableCell", + ] + + position: Optional[DataParseResponseGroundingParseResponseTableCellGroundingPosition] = None + + +DataParseResponseGrounding: TypeAlias = Union[ + DataParseResponseGroundingParseResponseGrounding, DataParseResponseGroundingParseResponseTableCellGrounding +] + + class DataParseResponse(BaseModel): chunks: List[DataParseResponseChunk] diff --git a/src/landingai_ade/types/parse_response.py b/src/landingai_ade/types/parse_response.py index 1e29931..bbeb475 100644 --- a/src/landingai_ade/types/parse_response.py +++ b/src/landingai_ade/types/parse_response.py @@ -1,7 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, List, Optional -from typing_extensions import Literal +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, TypeAlias from pydantic import Field as FieldInfo @@ -9,7 +9,16 @@ from .shared.parse_metadata import ParseMetadata from .shared.parse_grounding_box import ParseGroundingBox -__all__ = ["ParseResponse", "Chunk", "ChunkGrounding", "Split", "Grounding"] +__all__ = [ + "ParseResponse", + "Chunk", + "ChunkGrounding", + "Split", + "Grounding", + "GroundingParseResponseGrounding", + "GroundingParseResponseTableCellGrounding", + "GroundingParseResponseTableCellGroundingPosition", +] class ChunkGrounding(BaseModel): @@ -40,7 +49,7 @@ class Split(BaseModel): pages: List[int] -class Grounding(BaseModel): +class GroundingParseResponseGrounding(BaseModel): box: ParseGroundingBox page: int @@ -65,6 +74,48 @@ class Grounding(BaseModel): ] +class GroundingParseResponseTableCellGroundingPosition(BaseModel): + chunk_id: str + + col: int + + colspan: int + + row: int + + rowspan: int + + +class GroundingParseResponseTableCellGrounding(BaseModel): + box: ParseGroundingBox + + page: int + + type: Literal[ + "chunkLogo", + "chunkCard", + "chunkAttestation", + "chunkScanCode", + "chunkForm", + "chunkTable", + "chunkFigure", + "chunkText", + "chunkMarginalia", + "chunkTitle", + "chunkPageHeader", + "chunkPageFooter", + "chunkPageNumber", + "chunkKeyValue", + "table", + "tableCell", + ] + + position: Optional[GroundingParseResponseTableCellGroundingPosition] = None + + +Grounding: TypeAlias = Union[GroundingParseResponseGrounding, GroundingParseResponseTableCellGrounding] + + class ParseResponse(BaseModel): chunks: List[Chunk]